私は最近、JavaScriptのObject.create()
メソッドに遭遇し、それがnew SomeFunction()
を使ってオブジェクトの新しいインスタンスを作成するのとどう違うのか、そしてもう片方を使いたいと思う時を推測しようとしています。
次の例を見てください。
var test = {
val: 1,
func: function() {
return this.val;
}
};
var testA = Object.create(test);
testA.val = 2;
console.log(test.func()); // 1
console.log(testA.func()); // 2
console.log('other test');
var otherTest = function() {
this.val = 1;
this.func = function() {
return this.val;
};
};
var otherTestA = new otherTest();
var otherTestB = new otherTest();
otherTestB.val = 2;
console.log(otherTestA.val); // 1
console.log(otherTestB.val); // 2
console.log(otherTestA.func()); // 1
console.log(otherTestB.func()); // 2
どちらの場合も同じ動作が見られます。これら2つのシナリオの主な違いは、次のように思われます。
Object.create()
で使用されるオブジェクトは実際には新しいオブジェクトのプロトタイプを形成しますが、宣言されたプロパティ/関数からのnew Function()
ではプロトタイプを形成しません。Object.create()
構文でクロージャを作成することはできません。 JavaScriptの字句(ブロック対)型スコープを考えると、これは論理的です。上記の記述は正しいですか?そして私は何かが足りないのですか?いつ、どちらを使用しますか。
編集:上記のコードサンプルのjsfiddleバージョンへのリンク: http://jsfiddle.net/rZfYL/
Object.createで使用されるオブジェクトは、実際には新しいオブジェクトのプロトタイプを形成します。new Function()フォームの場合と同様に、宣言されたプロパティ/関数はプロトタイプを形成しません。
はい、Object.create
は最初の引数として渡されたものから直接継承するオブジェクトを構築します。
コンストラクタ関数では、新しく作成されたオブジェクトはコンストラクタのプロトタイプから継承します。
var o = new SomeConstructor();
上記の例では、o
はSomeConstructor.prototype
から直接継承しています。
ここに違いがあります。Object.create
では、何も継承しないオブジェクトObject.create(null);
を作成できます。一方、SomeConstructor.prototype = null;
を設定すると、新しく作成されたオブジェクトはObject.prototype
から継承します。
関数的な構文のようにObject.createの構文でクロージャを作成することはできません。 JavaScriptの字句(ブロック対)型スコープを考えると、これは論理的です。
まあ、あなたはクロージャを作成することができます、例えばプロパティ記述子引数を使用します。
var o = Object.create({inherited: 1}, {
foo: {
get: (function () { // a closure
var closured = 'foo';
return function () {
return closured+'bar';
};
})()
}
});
o.foo; // "foobar"
私はECMAScript第5版 Object.create
メソッドについて話していることに注意してください。Crockfordのシムではありません。
このメソッドは最新のブラウザでネイティブに実装され始めています、これをチェックしてください 互換性テーブル .
非常に簡単に言うと、new X
はconstructor
関数を追加で実行したObject.create(X.prototype)
です。 (そしてconstructor
にreturn
の代わりに式の結果となるべき実際のオブジェクトをthis
にする機会を与えます。)
それでおしまい。 :)
それ以外の答えは混乱を招くだけです。new
の定義を他の誰も読んでいないようです。 ;)
両方の呼び出しに対して内部的に発生する手順は次のとおりです。
(ヒント:唯一の違いはステップ3です)
new Test()
:
new Object()
オブジェクトを作成するobj.__proto__
をTest.prototype
に設定しますreturn Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value
Object.create( Test.prototype )
new Object()
オブジェクトを作成するobj.__proto__
をTest.prototype
に設定しますreturn obj;
だから基本的にObject.create
はコンストラクタを実行しません。
説明してみましょう(詳細は ブログ ):
Car
コンストラクタvar Car = function(){}
を書くとき、これは物事が内部的である方法です: アクセスできない{prototype}
への1つのFunction.prototype
隠しリンクと、アクセス可能でprototype
という実際のconstructor
を持つCar.prototype
への1つのCar
リンクがあります。 Function.prototypeとCar.prototypeはどちらもObject.prototype
への隠されたリンクを持っています。new
演算子とcreate
メソッドを使用して2つの同等のオブジェクトを作成する場合は、Honda = new Car();
とMaruti = Object.create(Car.prototype)
のようにします。 何が起こっている?
Honda = new Car();
- このようなオブジェクトを作成すると、隠された{prototype}
プロパティはCar.prototype
を指します。したがって、ここでは、Hondaオブジェクトの{prototype}
は常にCar.prototype
になります - オブジェクトの{prototype}
プロパティを変更するオプションはありません。新しく作成したオブジェクトのプロトタイプを変更したい場合はどうすればいいですか?Maruti = Object.create(Car.prototype)
- このようなオブジェクトを作成するときは、そのオブジェクトの{prototype}
プロパティを選択するための追加のオプションがあります。 Car.prototypeを{prototype}
として使用する場合は、それを関数のパラメータとして渡します。オブジェクトに{prototype}
を必要としない場合は、null
を次のように渡すことができます:Maruti = Object.create(null)
。
結論 - メソッドObject.create
を使うことで、あなたは自分のオブジェクト{prototype}
プロパティを自由に選ぶことができます。 new Car();
では、あなたはその自由を持っていません
OO JavaScriptの推奨方法:
2つのオブジェクトa
とb
があるとします。
var a = new Object();
var b = new Object();
a
がb
もアクセスしたいメソッドをいくつか持っているとします。そのためには、オブジェクトの継承が必要です(これらのメソッドにアクセスしたい場合に限り、a
をb
のプロトタイプにする必要があります)。 a
とb
のプロトタイプをチェックすると、それらがプロトタイプObject.prototype
を共有していることがわかります。
Object.prototype.isPrototypeOf(b); //true
a.isPrototypeOf(b); //false (the problem comes into the picture here).
問題 - a
のプロトタイプとしてオブジェクトb
が欲しいのですが、ここではプロトタイプObject.prototype
でオブジェクトb
を作成しました。 解決策 - ECMAScript 5では、そのような継承を簡単に実現するためにObject.create()
が導入されました。このようにオブジェクトb
を作成すると、
var b = Object.create(a);
それから、
a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.)
したがって、オブジェクト指向スクリプトを実行している場合、Object.create()
は継承に非常に役立ちます。
この:
var foo = new Foo();
そして
var foo = Object.create(Foo.prototype);
かなり似ています。 1つの重要な違いはnew Foo
が実際にコンストラクタコードを実行するのに対して、Object.create
は以下のようなコードを実行しないということです。
function Foo() {
alert("This constructor does not run with Object.create");
}
2パラメータバージョンのObject.create()
を使用すると、もっと強力なことができることに注意してください。
違いは、いわゆる「疑似古典的対原型継承」です。提案は、2つを混在させるのではなく、コード内で1つのタイプのみを使用することです。
疑似古典的継承( "new"演算子を使用)では、最初に疑似クラスを定義し、次にそのクラスからオブジェクトを作成することを想像してください。たとえば、擬似クラス「Person」を定義してから、「Person」から「Alice」と「Bob」を作成します。
プロトタイプ継承(Object.createを使用)では、特定の人物「Alice」を直接作成してから、「Alice」をプロトタイプとして使用して別の人物「Bob」を作成します。ここには「クラス」はありません。すべてオブジェクトです。
内部的には、JavaScriptは「プロトタイプ継承」を使用します。 「擬古典的」な方法は単なる砂糖です。
2つの方法の比較については、 このリンク を参照してください。
function Test(){
this.prop1 = 'prop1';
this.prop2 = 'prop2';
this.func1 = function(){
return this.prop1 + this.prop2;
}
};
Test.prototype.protoProp1 = 'protoProp1';
Test.prototype.protoProp2 = 'protoProp2';
var newKeywordTest = new Test();
var objectCreateTest = Object.create(Test.prototype);
/* Object.create */
console.log(objectCreateTest.prop1); // undefined
console.log(objectCreateTest.protoProp1); // protoProp1
console.log(objectCreateTest.__proto__.protoProp1); // protoProp1
/* new */
console.log(newKeywordTest.prop1); // prop1
console.log(newKeywordTest.__proto__.protoProp1); // protoProp1
概要:
1)new
キーワードを使用して注意することが2つあります。
a)関数がコンストラクタとして使用されている
b)function.prototype
オブジェクトが__proto__
プロパティに渡されます。または__proto__
がサポートされていない場合は、新しいオブジェクトがプロパティを検索する2番目の場所になります。
2)Object.create(obj.prototype)
を使用してオブジェクト(obj.prototype
)を作成し、それを目的のオブジェクトに渡します。新しいオブジェクトの__proto__
もobj.prototypeを指しているという違いがあります(xj9でお答えください)。
内部的にはObject.create
がこれを行います。
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
この構文は、JavaScriptがClassical Inheritanceを使用しているという幻想をただ取り除きます。
オブジェクト作成バリアント
変形例1: 'new Object()' - >オブジェクトコンストラクタなし引数.
var p1 = new Object(); // 'new Object()' create and return empty object -> {}
var p2 = new Object(); // 'new Object()' create and return empty object -> {}
console.log(p1); // empty object -> {}
console.log(p2); // empty object -> {}
// p1 and p2 are pointers to different objects
console.log(p1 === p2); // false
console.log(p1.prototype); // undefined
// empty object which is in fact Object.prototype
console.log(p1.__proto__); // {}
// empty object to which p1.__proto__ points
console.log(Object.prototype); // {}
console.log(p1.__proto__ === Object.prototype); // true
// null, which is in fact Object.prototype.__proto__
console.log(p1.__proto__.__proto__); // null
console.log(Object.prototype.__proto__); // null
変形例2: '新しいオブジェクト(人)' - >オブジェクトコンストラクタ引数付き。
const person = {
name: 'no name',
lastName: 'no lastName',
age: -1
}
// 'new Object(person)' return 'person', which is pointer to the object ->
// -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p1 = new Object(person);
// 'new Object(person)' return 'person', which is pointer to the object ->
// -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p2 = new Object(person);
// person, p1 and p2 are pointers to the same object
console.log(p1 === p2); // true
console.log(p1 === person); // true
console.log(p2 === person); // true
p1.name = 'John'; // change 'name' by 'p1'
p2.lastName = 'Doe'; // change 'lastName' by 'p2'
person.age = 25; // change 'age' by 'person'
// when print 'p1', 'p2' and 'person', it's the same result,
// because the object they points is the same
console.log(p1); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(p2); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(person); // { name: 'John', lastName: 'Doe', age: 25 }
バージョン3.1: 'Object.create(person)'。単純なオブジェクト 'person'と共にObject.createを使用してください。 'Object.create(person)'は、新しい空のオブジェクトを作成(および返し)して、同じ新しい空のオブジェクトにプロパティ '__proto__'を追加します。このプロパティ '__proto__'はオブジェクト 'person'を指します。
const person = {
name: 'no name',
lastName: 'no lastName',
age: -1,
getInfo: function getName() {
return `${this.name} ${this.lastName}, ${this.age}!`;
}
}
var p1 = Object.create(person);
var p2 = Object.create(person);
// 'p1.__proto__' and 'p2.__proto__' points to
// the same object -> 'person'
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(p1.__proto__);
console.log(p2.__proto__);
console.log(p1.__proto__ === p2.__proto__); // true
console.log(person.__proto__); // {}(which is the Object.prototype)
// 'person', 'p1' and 'p2' are different
console.log(p1 === person); // false
console.log(p1 === p2); // false
console.log(p2 === person); // false
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);
console.log(p1); // empty object - {}
console.log(p2); // empty object - {}
// add properties to object 'p1'
// (properties with the same names like in object 'person')
p1.name = 'John';
p1.lastName = 'Doe';
p1.age = 25;
// add properties to object 'p2'
// (properties with the same names like in object 'person')
p2.name = 'Tom';
p2.lastName = 'Harrison';
p2.age = 38;
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);
// { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);
// { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);
// use by '__proto__'(link from 'p1' to 'person'),
// person's function 'getInfo'
console.log(p1.getInfo()); // John Doe, 25!
// use by '__proto__'(link from 'p2' to 'person'),
// person's function 'getInfo'
console.log(p2.getInfo()); // Tom Harrison, 38!
バージョン3.2: 'Object.create(Object.prototype)'。 Object.createを組み込みオブジェクト - > 'Object.prototype'とともに使用します。 'Object.create(Object.prototype)'は新しい空のオブジェクトを作成して(そしてそれを返して)、同じ新しい空のオブジェクトにプロパティ '__proto__'を追加します。このプロパティ '__proto__'はオブジェクト 'Object.prototype'を指します。
// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p1' property '__proto__', which is link to 'Object.prototype'
var p1 = Object.create(Object.prototype);
// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p2' property '__proto__', which is link to 'Object.prototype'
var p2 = Object.create(Object.prototype);
console.log(p1); // {}
console.log(p2); // {}
console.log(p1 === p2); // false
console.log(p1.prototype); // undefined
console.log(p2.prototype); // undefined
console.log(p1.__proto__ === Object.prototype); // true
console.log(p2.__proto__ === Object.prototype); // true
変形例4: '新しいSomeFunction()'
// 'this' in constructor-function 'Person'
// represents a new instace,
// that will be created by 'new Person(...)'
// and returned implicitly
function Person(name, lastName, age) {
this.name = name;
this.lastName = lastName;
this.age = age;
//-----------------------------------------------------------------
// !--- only for demonstration ---
// if add function 'getInfo' into
// constructor-function 'Person',
// then all instances will have a copy of the function 'getInfo'!
//
// this.getInfo: function getInfo() {
// return this.name + " " + this.lastName + ", " + this.age + "!";
// }
//-----------------------------------------------------------------
}
// 'Person.prototype' is an empty object
// (before add function 'getInfo')
console.log(Person.prototype); // Person {}
// With 'getInfo' added to 'Person.prototype',
// instances by their properties '__proto__',
// will have access to the function 'getInfo'.
// With this approach, instances not need
// a copy of the function 'getInfo' for every instance.
Person.prototype.getInfo = function getInfo() {
return this.name + " " + this.lastName + ", " + this.age + "!";
}
// after function 'getInfo' is added to 'Person.prototype'
console.log(Person.prototype); // Person { getInfo: [Function: getInfo] }
// create instance 'p1'
var p1 = new Person('John', 'Doe', 25);
// create instance 'p2'
var p2 = new Person('Tom', 'Harrison', 38);
// Person { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);
// Person { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);
// 'p1.__proto__' points to 'Person.prototype'
console.log(p1.__proto__); // Person { getInfo: [Function: getInfo] }
// 'p2.__proto__' points to 'Person.prototype'
console.log(p2.__proto__); // Person { getInfo: [Function: getInfo] }
console.log(p1.__proto__ === p2.__proto__); // true
// 'p1' and 'p2' points to different objects(instaces of 'Person')
console.log(p1 === p2); // false
// 'p1' by its property '__proto__' reaches 'Person.prototype.getInfo'
// and use 'getInfo' with 'p1'-instance's data
console.log(p1.getInfo()); // John Doe, 25!
// 'p2' by its property '__proto__' reaches 'Person.prototype.getInfo'
// and use 'getInfo' with 'p2'-instance's data
console.log(p2.getInfo()); // Tom Harrison, 38!