390

Javascript 1.9.3 / ECMAScript 5はObject.create、とりわけDouglasCrockfordが長い間提唱してきたを紹介します。new以下のコードを次のように置き換えるにはどうすればよいObject.createですか?

var UserA = function(nameParam) {
    this.id = MY_GLOBAL.nextId();
    this.name = nameParam;
}
UserA.prototype.sayHello = function() {
    console.log('Hello '+ this.name);
}
var bob = new UserA('bob');
bob.sayHello();

(存在すると仮定MY_GLOBAL.nextIdします)。

私が思いつくことができる最高のものは次のとおりです。

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var bob = Object.create(userB);
bob.init('Bob');
bob.sayHello();

何のメリットもないようですので、うまくいかないと思います。私はおそらく新古典主義的すぎます。Object.createユーザー「bob」を作成するにはどのように使用すればよいですか?

4

15 に答える 15

267

継承のレベルが1つしかない場合、この例では、の実際の利点を確認できない場合がありますObject.create

このメソッドを使用すると、オブジェクトが他のオブジェクトから直接継承できる差分継承を簡単に実装できます。

あなたのuserB例では、メソッドがパブリックである必要はなく、存在する必要もないと思います。init既存のオブジェクトインスタンスでこのメソッドを再度呼び出すidと、nameプロパティが変更されます。

Object.create2番目の引数を使用してオブジェクトのプロパティを初期化できます。例:

var userB = {
  sayHello: function() {
    console.log('Hello '+ this.name);
  }
};

var bob = Object.create(userB, {
  'id' : {
    value: MY_GLOBAL.nextId(),
    enumerable:true // writable:false, configurable(deletable):false by default
  },
  'name': {
    value: 'Bob',
    enumerable: true
  }
});

ご覧のとおり、プロパティは、メソッドで使用されるのとObject.create同様の構文を使用してオブジェクトリテラルを使用して、の2番目の引数で初期化できます。Object.definePropertiesObject.defineProperty

プロパティ属性(、、、または)を設定できますenumerablewritableこれconfigurableは非常に便利です。

于 2010-04-25T20:18:42.683 に答える
55

Object.create(...)overを使用することに実際には利点はありませんnew object

この方法を提唱する人々は、一般的に、 「スケーラビリティ」、または「JavaScriptにとってより自然な」などのかなりあいまいな利点を述べています。

ただし、を使用するよりも利点Object.createがあることを示す具体的な例はまだ見ていません。それどころか、それには既知の問題があります。Sam Elsammanは、ネストされたオブジェクトがあり、使用されたときに何が起こるかを説明しています。newObject.create(...)

var Animal = {
    traits: {},
}
var lion = Object.create(Animal);
lion.traits.legs = 4;
var bird = Object.create(Animal);
bird.traits.legs = 2;
alert(lion.traits.legs) // shows 2!!!

これは、データを使用して新しいオブジェクトを作成するObject.create(...)方法を提唱しているために発生します。ここで、データムはとのプロトタイプの一部になり、共有されると問題が発生します。newを使用する場合、プロトタイプの継承は明示的です。Animallionbird

function Animal() {
    this.traits = {};
}

function Lion() { }
Lion.prototype = new Animal();
function Bird() { }
Bird.prototype = new Animal();

var lion = new Lion();
lion.traits.legs = 4;
var bird = new Bird();
bird.traits.legs = 2;
alert(lion.traits.legs) // now shows 4

に渡されるオプションのプロパティ属性に関しては、Object.create(...)を使用して追加できますObject.defineProperties(...)

于 2012-10-03T09:03:26.990 に答える
42

Object.createは、IE8、Opera v11.5、Konq 4.3など、いくつかのブラウザではまだ標準ではありません。これらのブラウザには、ダグラスクロックフォードのバージョンのObject.createを使用できますが、これには、CMSの回答で使用される2番目の「初期化オブジェクト」パラメータは含まれていません。

クロスブラウザコードの場合、その間にオブジェクトを初期化する1つの方法は、CrockfordのObject.createをカスタマイズすることです。これが1つの方法です:-

Object.build = function(o) {
   var initArgs = Array.prototype.slice.call(arguments,1)
   function F() {
      if((typeof o.init === 'function') && initArgs.length) {
         o.init.apply(this,initArgs)
      }
   }
   F.prototype = o
   return new F()
}

これにより、Crockfordのプロトタイプの継承が維持され、オブジェクト内のinitメソッドもチェックされ、new man('John'、'Smith')などのパラメーターを使用して実行されます。コードは次のようになります:-

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}  // For example

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var bob = Object.build(userB, 'Bob');  // Different from your code
bob.sayHello();

したがって、bobはsayHelloメソッドを継承し、独自のプロパティid=1およびname='Bob'を持ちます。もちろん、これらのプロパティは書き込み可能であり、列挙可能です。これは、特に書き込み可能、​​列挙可能、構成可能な属性を気にしない場合は、ECMAObject.createよりもはるかに簡単に初期化する方法です。

initメソッドを使用しない初期化には、次のCrockfordmodを使用できます。

Object.gen = function(o) {
   var makeArgs = arguments 
   function F() {
      var prop, i=1, arg, val
      for(prop in o) {
         if(!o.hasOwnProperty(prop)) continue
         val = o[prop]
         arg = makeArgs[i++]
         if(typeof arg === 'undefined') break
         this[prop] = arg
      }
   }
   F.prototype = o
   return new F()
}

これにより、userBパラメーターの後にObject.genパラメーターが左から右に使用され、userB独自のプロパティが定義された順序で入力されます。for(prop in o)ループを使用するため、ECMA標準では、プロパティの列挙の順序をプロパティの定義の順序と同じに保証することはできません。ただし、(4)主要なブラウザーでテストされたいくつかのコード例は、hasOwnPropertyフィルターが使用されている場合、および使用されていない場合でも、同じであることを示しています。

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}};  // For example

var userB = {
   name: null,
   id: null,
   sayHello: function() {
      console.log('Hello '+ this.name);
   }
}

var bob = Object.gen(userB, 'Bob', MY_GLOBAL.nextId());

userBはinitメソッドを必要としないので、Object.buildよりもやや簡単です。また、userBは特にコンストラクターではありませんが、通常のシングルトンオブジェクトのように見えます。したがって、このメソッドを使用すると、通常のプレーンオブジェクトから構築および初期化できます。

于 2011-07-04T12:10:39.023 に答える
28

TL; DR:

new Computer()Computer(){}コンストラクター関数を一度だけ呼び出しますが、呼び出さObject.create(Computer.prototype)ないでしょう。

すべての利点はこの点に基づいています。

パフォーマンスに関する補足:likeを呼び出すコンストラクターnew Computer()は、エンジンによって大幅に最適化されているため、よりも高速になる可能性がありますObject.create

于 2014-09-12T09:15:28.053 に答える
14

init次のように、メソッドをreturnthisにしてから、呼び出しを連鎖させることができます。

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
        return this;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};

var bob = Object.create(userB).init('Bob');
于 2012-08-03T16:37:59.860 に答える
10

Object.createのもう1つの可能な使用法は、安価で効果的な方法で不変オブジェクトのクローンを作成することです。

var anObj = {
    a: "test",
    b: "jest"
};

var bObj = Object.create(anObj);

bObj.b = "gone"; // replace an existing (by masking prototype)
bObj.c = "brand"; // add a new to demonstrate it is actually a new obj

// now bObj is {a: test, b: gone, c: brand}

:上記のスニペットは、ソースオブジェクトのクローンを作成します(cObj = aObjのように、参照ではありません)。オブジェクトメンバーのプロパティをコピーしないという点で、copy-propertiesメソッド(1を参照)よりも優れています。むしろ、ソースオブジェクトにプロトタイプが設定された別の-destination-オブジェクトを作成します。さらに、プロパティがdestオブジェクトで変更されると、「オンザフライ」で作成され、プロトタイプ(src)のプロパティがマスクされます。これは、不変オブジェクトのクローンを作成するための高速で効果的な方法です。

ここでの注意点は、これは作成後に変更してはならない(不変)ソースオブジェクトに適用されるということです。作成後にソースオブジェクトが変更されると、クローンのマスクされていないすべてのプロパティも変更されます。

ここでフィドル(http://jsfiddle.net/y5b5q/1/)(Object.create対応のブラウザが必要です)。

于 2012-09-06T16:26:45.063 に答える
7

new問題の主なポイントは、アプローチとアプローチの違いを理解することだと思いますObject.create。この答えこのビデオ newキーワードに応じて、次のことを行います。

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

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

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

  4. 新しいオブジェクトと暗黙のperformを使用してコンストラクター関数を実行しますreturn this

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

Object.create1stとステップのみを実行し2ndます!!!

問題のコード例では大したことではありませんが、次の例では次のようになります。

var onlineUsers = [];
function SiteMember(name) {
    this.name = name;
    onlineUsers.push(name);
}
SiteMember.prototype.getName = function() {
    return this.name;
}
function Guest(name) {
    SiteMember.call(this, name);
}
Guest.prototype = new SiteMember();

var g = new Guest('James');
console.log(onlineUsers);

副作用として次のようになります:

[ undefined, 'James' ]

ただし、Guest.prototype = new SiteMember();
親コンストラクターメソッドを実行する必要はありません。GuestでメソッドgetNameを使用できるようにするだけで済みます。したがって、を使用する必要がありますObject.create。 結果に
置き換える場合:Guest.prototype = new SiteMember();
Guest.prototype = Object.create(SiteMember.prototype);

[ 'James' ]
于 2017-07-27T19:39:38.867 に答える
6

NEWでオブジェクトを作成できない場合でも、CREATEメソッドを呼び出すことはできます。

例:カスタム要素を定義する場合は、HTMLElementから派生する必要があります。

proto = new HTMLElement  //fail :(
proto = Object.create( HTMLElement.prototype )  //OK :)
document.registerElement( "custom-element", { prototype: proto } )
于 2015-08-04T09:34:42.713 に答える
4

利点は、Object.create通常new、ほとんどのブラウザよりも遅いことです。

このjsperfの例では、Chromiumでは、ブラウザーnewはどちらもかなり高速ですが、30倍高速です。Object.create(obj)これはすべてかなり奇妙なことです。newは(コンストラクターの呼び出しなど)より多くのことを実行し、Object.createは、渡されたオブジェクトをプロトタイプとして新しいオブジェクトを作成するだけである必要があります(Crockfordの秘密のリンク-話す)

おそらく、ブラウザはより効率的にすることに追いついていないでしょうObject.create(おそらく彼らはそれを裏でnew...ネイティブコードでさえもベースにしています)

于 2014-06-12T22:27:46.087 に答える
4

概要:

  • Object.create()2つの引数を取り、新しいオブジェクトを返すJavascript関数です。
  • 最初の引数は、新しく作成されたオブジェクトのプロトタイプとなるオブジェクトです。
  • 2番目の引数は、新しく作成されたオブジェクトのプロパティとなるオブジェクトです。

例:

const proto = {
  talk : () => console.log('hi')
}

const props = {
  age: {
    writable: true,
    configurable: true,
    value: 26
  }
}


let Person = Object.create(proto, props)

console.log(Person.age);
Person.talk();

実用的なアプリケーション:

  1. この方法でオブジェクトを作成する主な利点は、プロトタイプを明示的に定義できることです。オブジェクトリテラル、またはnewこれを制御できないキーワードを使用する場合(ただし、もちろんそれらを上書きできます)。
  2. プロトタイプが必要な場合newキーワードはコンストラクター関数を呼び出します。コンストラクター関数を呼び出したり、宣言したりする必要Object.create()ありません。
  3. 基本的に、非常に動的な方法でオブジェクトを作成する場合に役立つツールです。受け取った引数に応じて異なるプロトタイプのオブジェクトを作成するオブジェクトファクトリ関数を作成できます。
于 2018-08-19T08:41:20.473 に答える
3

カスタムObject.create()関数を作成する必要があります。Crockfordsの懸念に対処し、init関数を呼び出すもの。

これは機能します:

var userBPrototype = {
    init: function(nameParam) {
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};


function UserB(name) {
    function F() {};
    F.prototype = userBPrototype;
    var f = new F;
    f.init(name);
    return f;
}

var bob = UserB('bob');
bob.sayHello();

ここで、UserBはObject.createに似ていますが、ニーズに合わせて調整されています。

必要に応じて、次の電話をかけることもできます。

var bob = new UserB('bob');
于 2010-04-25T22:37:26.103 に答える
3

Douglas CrockfordはObject.create()の熱心な支持者であり、基本的にこの構成が実際にjavascriptにある理由ですが、彼はもはやこの意見を持っていません。

彼はObject.createの使用をやめました。これは、問題が多すぎるため、このキーワードの使用を完全にやめたためです。たとえば、注意しないと、グローバルオブジェクトを簡単に指す可能性があり、非常に悪い結果を招く可能性があります。そして彼は、このObject.createを使用しないと、もはや意味がないと主張します。

彼がNordic.jsで話している2014年のこのビデオをチェックできます。

https://www.youtube.com/watch?v=PSGEjv3Tqo0

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

于 2018-02-01T19:30:35.740 に答える
3

newObject.createさまざまな目的を果たします。newオブジェクトタイプの新しいインスタンスを作成することを目的としています。Object.create単に新しいオブジェクトを作成し、そのプロトタイプを設定することを目的としています。なぜこれが便利なのですか?プロパティにアクセスせずに継承を実装し__proto__ます。と呼ばれるオブジェクトインスタンスのプロトタイプ[[Prototype]]は、仮想マシンの内部プロパティであり、直接アクセスすることを目的としたものではありません。[[Prototype]]プロパティとして実際に直接アクセスできる唯一の理由__proto__は、これが常にすべての主要な仮想マシンのECMAScriptの実装の事実上の標準であり、この時点で削除すると多くの既存のコードが破損するためです。

7ochemによる上記の回答に応じて、オブジェクトのプロトタイプをnewステートメントの結果に設定することは絶対にしないでください。同じプロトタイプコンストラクターを複数回呼び出す意味がないだけでなく、同じクラスの2つのインスタンスが次のようになる可能性があるためです。作成後にプロトタイプを変更すると、動作が異なります。どちらの例も、プロトタイプの継承チェーンの意図された動作を誤解して壊した結果として、単に悪いコードです。

にアクセスする代わりに__proto__、インスタンスのプロトタイプは、で作成されたとき、Object.createまたは後で作成されたときに書き込まれ、またはObject.setPrototypeOfで読み取られる必要があります。Object.getPrototypeOfObject.isPrototypeOf

また、Object.setPrototypeOfのMozillaドキュメントが指摘しているように、オブジェクトの作成後にプロトタイプを変更すると未定義になる可能性があるという事実に加えて、パフォーマンス上の理由から、作成後にオブジェクトのプロトタイプを変更することはお勧めできません。アクセスする特定のコードがプロトタイプの変更前または変更後に実行できる場合の動作。ただし、そのコードが現在のプロトタイプを非常に注意深くチェックするか、2つの間で異なるプロパティにアクセスしない場合を除きます。



const X = function (v) { this.v = v }; X.prototype.whatAmI = 'X'; X.prototype.getWhatIAm = () => this.whatAmI; X.prototype.getV = () => this.v;

次のVM擬似コードは、ステートメントと同等です 。コンストラクターは任意の値を返すことができますが、ステートメントconst x0 = new X(1);は常にその戻り値を無視し、新しく作成されたオブジェクトへの参照を返します。 そして、次の擬似コードはステートメントと同等です。ご覧のとおり 、2つの違いは、コンストラクターを実行しないことです。コンストラクターは実際には任意の値を返すことができますが、特に指定されていない場合は新しいオブジェクト参照を返すだけです。

const x0 = {}; x0.[[Prototype]] = X.prototype; X.prototype.constructor.call(x0, 1);

new

const x1 = Object.create(X.prototype);

const x0 = {}; x0.[[Prototype]] = X.prototype;

Object.createthis

ここで、次の定義でサブクラスYを作成したい場合は、次の ように

const Y = function(u) { this.u = u; } Y.prototype.whatAmI = 'Y'; Y.prototype.getU = () => this.u;

記述してXから継承させること ができます__proto__。プロトタイプのコンストラクタープロパティを設定して、ステートメントによって正しいコンストラクターが呼び出されるようにします。それ以外の場合は、関数を呼び出します。プログラマーが呼び出したい場合は、Yのコンストラクターでより適切に実行されます。

Y.prototype.__proto__ = X.prototype;

__proto__

Y.prototype = Object.create(X.prototype); Y.prototype.constructor = Y;

new Ynew YXnew YXX.call(this, u)

于 2018-12-08T21:04:24.183 に答える
3

newオペレーター

  • これは、コンストラクター関数からオブジェクトを作成するために使用されます
  • newキーワードはコンストラクター関数も実行します
function Car() {
  console.log(this) // this points to myCar
  this.name = "Honda";
}

var myCar = new Car()
console.log(myCar) // Car {name: "Honda", constructor: Object}
console.log(myCar.name) // Honda
console.log(myCar instanceof Car) // true
console.log(myCar.constructor) // function Car() {}
console.log(myCar.constructor === Car) // true
console.log(typeof myCar) // object

Object.create

  • Object.create新しいオブジェクトを作成するために使用することもできます
  • ただし、コンストラクター関数は実行しません
  • Object.create別のオブジェクトからオブジェクトを作成するために使用されます
const Car = {
  name: "Honda"
}

var myCar = Object.create(Car)
console.log(myCar) // Object {}
console.log(myCar.name) // Honda
console.log(myCar instanceof Car) // ERROR
console.log(myCar.constructor) // Anonymous function object
console.log(myCar.constructor === Car) // false
console.log(typeof myCar) // object

于 2020-03-15T08:21:20.027 に答える
2

私は閉鎖アプローチを好みます。

私はまだ使用していますnew。使用しませんObject.create。使用しませんthis

new私はそれの宣言的な性質が好きなので、今でも使用しています。

単純な継承のためにこれを考慮してください。

window.Quad = (function() {

    function Quad() {

        const wheels = 4;
        const drivingWheels = 2;

        let motorSize = 0;

        function setMotorSize(_) {
            motorSize = _;
        }

        function getMotorSize() {
            return motorSize;
        }

        function getWheelCount() {
            return wheels;
        }

        function getDrivingWheelCount() {
            return drivingWheels;
        }
        return Object.freeze({
            getWheelCount,
            getDrivingWheelCount,
            getMotorSize,
            setMotorSize
        });
    }

    return Object.freeze(Quad);
})();

window.Car4wd = (function() {

    function Car4wd() {
        const quad = new Quad();

        const spareWheels = 1;
        const extraDrivingWheels = 2;

        function getSpareWheelCount() {
            return spareWheels;
        }

        function getDrivingWheelCount() {
            return quad.getDrivingWheelCount() + extraDrivingWheels;
        }

        return Object.freeze(Object.assign({}, quad, {
            getSpareWheelCount,
            getDrivingWheelCount
        }));
    }

    return Object.freeze(Car4wd);
})();

let myQuad = new Quad();
let myCar = new Car4wd();
console.log(myQuad.getWheelCount()); // 4
console.log(myQuad.getDrivingWheelCount()); // 2
console.log(myCar.getWheelCount()); // 4
console.log(myCar.getDrivingWheelCount()); // 4 - The overridden method is called
console.log(myCar.getSpareWheelCount()); // 1

フィードバックをお勧めします。

于 2018-02-08T08:54:50.913 に答える