web-dev-qa-db-ja.com

Object.create()とnew SomeFunction()の違いを理解する

私は最近、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/

363
Matt

Object.createで使用されるオブジェクトは、実際には新しいオブジェクトのプロトタイプを形成します。new Function()フォームの場合と同様に、宣言されたプロパティ/関数はプロトタイプを形成しません。

はい、Object.createは最初の引数として渡されたものから直接継承するオブジェクトを構築します。

コンストラクタ関数では、新しく作成されたオブジェクトはコンストラクタのプロトタイプから継承します。

var o = new SomeConstructor();

上記の例では、oSomeConstructor.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のシムではありません。

このメソッドは最新のブラウザでネイティブに実装され始めています、これをチェックしてください 互換性テーブル .

232
CMS

非常に簡単に言うと、new Xconstructor関数を追加で実行したObject.create(X.prototype)です。 (そしてconstructorreturnの代わりに式の結果となるべき実際のオブジェクトをthisにする機会を与えます。)

それでおしまい。 :)

それ以外の答えは混乱を招くだけです。newの定義を他の誰も読んでいないようです。 ;)

381
Evi1M4chine

両方の呼び出しに対して内部的に発生する手順は次のとおりです。
(ヒント:唯一の違いはステップ3です)


new Test()

  1. new Object()オブジェクトを作成する
  2. obj.__proto__Test.prototypeに設定します
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create( Test.prototype )

  1. new Object()オブジェクトを作成する
  2. obj.__proto__Test.prototypeに設定します
  3. return obj;

だから基本的にObject.createはコンストラクタを実行しません。

195
Ray Hulha

説明してみましょう(詳細は ブログ ):

  1. Carコンストラクタvar Car = function(){}を書くとき、これは物事が内部的である方法です: A diagram of prototypal chains when creating javascript objects アクセスできない{prototype}への1つのFunction.prototype隠しリンクと、アクセス可能でprototypeという実際のconstructorを持つCar.prototypeへの1つのCarリンクがあります。 Function.prototypeとCar.prototypeはどちらもObject.prototypeへの隠されたリンクを持っています。
  2. new演算子とcreateメソッドを使用して2つの同等のオブジェクトを作成する場合は、Honda = new Car();Maruti = Object.create(Car.prototype)のようにします。A diagram of prototypal chains for differing object creation methods 何が起こっている?

    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つのオブジェクトabがあるとします。

var a = new Object();
var b = new Object();

abもアクセスしたいメソッドをいくつか持っているとします。そのためには、オブジェクトの継承が必要です(これらのメソッドにアクセスしたい場合に限り、abのプロトタイプにする必要があります)。 abのプロトタイプをチェックすると、それらがプロトタイプ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()は継承に非常に役立ちます。

59
Anshul

この:

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()を使用すると、もっと強力なことができることに注意してください。

40
Leopd

違いは、いわゆる「疑似古典的対原型継承」です。提案は、2つを混在させるのではなく、コード内で1つのタイプのみを使用することです。

疑似古典的継承( "new"演算子を使用)では、最初に疑似クラスを定義し、次にそのクラスからオブジェクトを作成することを想像してください。たとえば、擬似クラス「Person」を定義してから、「Person」から「Alice」と「Bob」を作成します。

プロトタイプ継承(Object.createを使用)では、特定の人物「Alice」を直接作成してから、「Alice」をプロトタイプとして使用して別の人物「Bob」を作成します。ここには「クラス」はありません。すべてオブジェクトです。

内部的には、JavaScriptは「プロトタイプ継承」を使用します。 「擬古典的」な方法は単なる砂糖です。

2つの方法の比較については、 このリンク を参照してください。

22
user1931858
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でお答えください)。

21
user3124360

内部的にはObject.createがこれを行います。

Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};

この構文は、JavaScriptがClassical Inheritanceを使用しているという幻想をただ取り除きます。

10
xj9

したがって この答え そして このビデオnewというキーワードは次のことをします。

  1. 新しいオブジェクトを作成します。

  2. 新しいオブジェクトをコンストラクタ関数(prototype)にリンクします。

  3. this変数を新しいオブジェクトを指すようにします。

  4. 新しいオブジェクトを使用してコンストラクタ関数を実行し、暗黙的にreturn thisを実行します。

  5. 新しいオブジェクトのプロパティconstructorにコンストラクタ関数名を割り当てます。

Object.create1st2ndステップだけを実行します。

9
Vladimir Kovpak

オブジェクト作成バリアント


変形例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

enter image description here


変形例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 }

enter image description here


バージョン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!

enter image description here


バージョン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

enter image description here


変形例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!

enter image description here

8
Ted