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


392

私は最近Object.create()JavaScriptでこのメソッドに遭遇し、それが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()宣言されたfromのプロパティ/関数ではプロトタイプを形成しません。
  • Object.create()関数構文の場合のように、構文を使用してクロージャーを作成することはできません。これは、JavaScriptの字句(vsブロック)タイプのスコープが与えられた場合に論理的です。

上記の説明は正しいですか?そして、私は何かを逃していますか?いつどちらを使用しますか?

編集:上記のコードサンプルのjsfiddleバージョンへのリンク:http ://jsfiddle.net/rZfYL/


回答:


246

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の字句(vsブロック)タイプのスコープが与えられた場合に論理的です。

まあ、例えばプロパティ記述子の引数を使ってクロージャーを作成できます:

var o = Object.create({inherited: 1}, {
  foo: {
    get: (function () { // a closure
      var closured = 'foo';
      return function () {
        return closured+'bar';
      };
    })()
  }
});

o.foo; // "foobar"

私が話しているのObject.createは、Crockfordのシムではなく、ECMAScript 5th Edition メソッドについてです。

このメソッドは最新のブラウザでネイティブに実装され始めています。この互換性の表を確認してください


2
@CMS 2つの質問。1)Object.create(null)のスコープチェーンはグローバルスコープ(ブラウザの「ウィンドウ」など)で終了しますか、それともそれ自体で終了しますか?2)Object.createが導入された理由(たとえば、この機能が欠けていた機能が欠けていましたか?)と、なぜnew Function()の代わりにそれを使用するのかは、まだはっきりしていません。
Matt

9
@ Matt、1)スコープチェーンは実際にはここでは関連する概念ではありません。スコープチェーンは識別子の解決に関連しています。例:foo;現在の字句環境でどのように解決されるか。2)継承を実装する簡単な方法を提供するために、これは非常に強力な構成要素です。IMO本当にシンプルで軽量なのでそれを使用しますが、製品コードでは、ES5が広くサポートされるまでしばらく待つ必要があります。欠落している機能については、「元の」オブジェクトを作成するという事実が欠落しており、Object.create(null);信頼性の高いハッシュテーブルのようなオブジェクトを実装することは非常に役立ちます...
CMS

@CMSありがとう。したがって、単に「Object.create」を使用してオブジェクトを作成すると、そのプロトタイプとなるオブジェクトを選択できます。
Anshul

@CMS OK、Object.create(null)つまり、hasOwnProperty()何も継承しないので、反復するときにがらくたを使用する必要がないということですか?私はそれが好きです-ありがとう。もちろん、誰もが使用するわけではないので、誰もがまだやるつもりなhasOwnPropertyので、Object.create(null)それが本当の利点であるかどうかはわかりません...これまでのところ、Object.create()完全に納得できない他の「利点」を見つけました。
user949300 2014

425

非常に単純に、言っnew XているObject.create(X.prototype)に加えて実行されているとconstructor機能を。(そして、実際のオブジェクトに、の代わりに式の結果であるconstructor機会を与える。)returnthis

それでおしまい。:)

他の答えは混乱を招くものですnew。明らかに他の誰もどちらかの定義を読んでいないからです。;)


23
+1シンプルさと明快さ!(Object.create(null)は良いオプションのようですが、多分それについて言及すべきです)。
user949300 2014

それが進むべき道であるそれを単純にしてください
ビル

それは「待つので、関数にもプロトタイプがあるのですか?それらとオブジェクトプロトタイプの関係は何ですか?」
Qwertie、2016年

3
@Qwertie:JSでは、すべてがオブジェクトです。:)彼らはJavaからそれをコピーし、JavaはSmallTalkからコピーしました。これは「出現」の良い例であり、一般的に生活を楽にします。
Evi1M4chine 2016年

@ Evi1M4chineは実際にはJavaであり、関数はオブジェクトではありません(さらに言えば、どちらもプリミティブではありません)...そしてオブジェクトにはプロトタイプがないため、比較は適切ではないようです。JSが他の一般的なOO言語とは動作が異なるという事実は、混乱の主な原因です(そして、ブラウザーが関数やプロトタイプを含むオブジェクトのネットワークを視覚化する簡単な方法を提供しないことは助けにはなりません)。PSこのリンクは役に立ちました:davidwalsh.name/javascript-objects-deconstruction
Qwertie

204

両方の呼び出しで内部的に行われる手順は次のとおりです
(ヒント:唯一の違いは手順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はコンストラクタを実行しません。


@Rayでは、object.createを使用して、コンストラクタ関数で言及されている関数のプロパティをフォントに設定しますか?

@sortednounプロパティがプライベートであり、プロトタイプで指定されていない限り、はい、それらは継承されず、新しいオブジェクトにそれらがありません(そして、私が追加すると、最終的にプロトタイプ化されたプロパティを取得することが期待できます)親から、親コンストラクタが少なくとも1回実行されたとき)。
カマフェザー2017

ほとんどのコンストラクター関数と同様に、メソッドは返されたオブジェクト内で定義され、new基本的にすべての関数が複製されますが、複製されObject.createません。
SparK

61

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

  1. Carコンストラクタを作成するときvar Car = function(){}、これは内部的には次のように なります。アクセスできないJavaScriptオブジェクトを作成するときのプロトタイプチェーンの図 1つの{prototype}非表示リンクと、アクセス可能で実際のを持つFunction.prototype1つのprototypeリンクがCar.prototypeあります。Function.prototypeとCar.prototypeの両方にへの隠しリンクがあります。constructorCarObject.prototype
  2. new演算子とcreateメソッドを使用して2つの同等のオブジェクトを作成する場合は、次のようにする必要がHonda = new Car();ありMaruti = Object.create(Car.prototype)ます。さまざまなオブジェクト作成方法のプロトタイプチェーンの図 何が起こっている?

    Honda = new Car();—このようなオブジェクトを作成すると、hidden {prototype}プロパティがポイントされCar.prototypeます。したがって、ここで{prototype}は、Hondaオブジェクトのは常にになります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とします。そのためには、オブジェクトの継承が必要です(これらのメソッドにアクセスする場合abのみ、プロトタイプにする必要があります)。我々はのプロトタイプをチェックするab、我々は、彼らがプロトタイプを共有していることがわかりますObject.prototype

Object.prototype.isPrototypeOf(b); //true
a.isPrototypeOf(b); //false (the problem comes into the picture here).

問題—オブジェクトaをのプロトタイプにしたいのですbが、ここbではプロトタイプを使用してオブジェクトを作成しましたObject.prototypeソリューション—Object.create()そのような継承を簡単に実現するためにECMAScript 5が導入されました。次の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()は、継承に非常に役立ちます。


それで、コンストラクタを呼び出さないオブジェクト作成にいくぶん似ていますか?クラスのすべてのメリットをお楽しみいただけます。obj instanceof Classもtrueになります。ただし、newを介してClass関数を呼び出すわけではありません。
Praveen

@Anshul 両方のオブジェクトが異なり、異なるメモリを指しているため、どちらが正しいかa.isPrototypeOf(b);を返しfalseます。newオペレーターでこれを行う正しい方法はここにあります。- jsfiddle.net/167onunp
Sagar Karira、2016年

これを行う代わりに、bのプロトタイププロパティを単にaに設定しないのはなぜですか。
健忘症16

ブログの記事も気に入りました。概念をよりよく理解するのに役立ちました。ありがとうございました。
steady_daddy

1
結論はそれをすべて言います。
kushalvm 2017

44

この:

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()はるかに強力な機能を実行できることに注意してください。


1
素晴らしい説明。追加するかもしれObject.createませんが、このような最も単純な形式で使用すると、プロトタイプ継承を利用しながら、コードからコンストラクター関数を省略できます。
リッキー・ボイス、

23

違いは、いわゆる「疑似クラシック継承とプロトタイプ継承」です。コードで1つのタイプのみを使用し、2つを混合しないことをお勧めします。

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

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

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

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


21
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でansを参照してください)。


15

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


バリアント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: ' new Object(person) '->引数付きのオブジェクトコンストラクター。

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: ' new 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!

ここに画像の説明を入力してください


いい要約。ありがとう。それは今日私を助けました!!
Anandaraja_Srinivasan

11

内部的にObject.createこれを行います:

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

構文は、JavaScriptが古典的継承を使用しているという幻想を取り去るだけです。


25
ECMAScript 5 Object.createメソッドは、それだけではなく、プロパティ記述子でプロパティを定義し、何も継承しないオブジェクトを作成することができます(Object.create(null);)。 ES3での動作。詳細
CMS

@CMSに同意しますが、一般的にはの単純なポリフィルですObject.create
V. Kovpak 2017

10

そのために、この答えとにこのビデオ newキーワードは、次のことを行います。

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

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

  3. 作りthis新しいオブジェクトに変数のポイントを。

  4. 新しいオブジェクトと暗黙的なパフォーマンスを使用してコンストラクター関数を実行しreturn thisます。

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

Object.create1st2ndステップのみを実行します!!!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.