Using “Object.create” instead of “new”

后端 未结 15 2156
隐瞒了意图╮
隐瞒了意图╮ 2020-11-22 06:08

Javascript 1.9.3 / ECMAScript 5 introduces Object.create, which Douglas Crockford amongst others has been advocating for a long time. How do I replace new

相关标签:
15条回答
  • 2020-11-22 06:56

    I think the main point in question - is to understand difference between new and Object.create approaches. Accordingly to this answer and to this video new keyword does next things:

    1. Creates new object.

    2. Links new object to constructor function (prototype).

    3. Makes this variable point to the new object.

    4. Executes constructor function using the new object and implicit perform return this;

    5. Assigns constructor function name to new object's property constructor.

    Object.create performs only 1st and 2nd steps!!!

    In code example provided in question it isn't big deal, but in next example it is:

    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);
    

    As side effect result will be:

    [ undefined, 'James' ]
    

    because of Guest.prototype = new SiteMember();
    But we don't need to execute parent constructor method, we need only make method getName to be available in Guest. Hence we have to use Object.create.
    If replace Guest.prototype = new SiteMember();
    to Guest.prototype = Object.create(SiteMember.prototype); result be:

    [ 'James' ]
    
    0 讨论(0)
  • 2020-11-22 06:56

    The advantage is that Object.create is typically slower than new on most browsers

    In this jsperf example, in a Chromium, browser new is 30 times as fast as Object.create(obj) although both are pretty fast. This is all pretty strange because new does more things (like invoking a constructor) where Object.create should be just creating a new Object with the passed in object as a prototype (secret link in Crockford-speak)

    Perhaps the browsers have not caught up in making Object.create more efficient (perhaps they are basing it on new under the covers ... even in native code)

    0 讨论(0)
  • 2020-11-22 06:57

    Sometimes you cannot create an object with NEW but are still able to invoke the CREATE method.

    For example: if you want to define a Custom Element it must derive from HTMLElement.

    proto = new HTMLElement  //fail :(
    proto = Object.create( HTMLElement.prototype )  //OK :)
    document.registerElement( "custom-element", { prototype: proto } )
    
    0 讨论(0)
  • 2020-11-22 06:58

    Another possible usage of Object.create is to clone immutable objects in a cheap and effective way.

    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}
    

    Notes: The above snippet creates a clone of an source object (aka not a reference, as in cObj = aObj). It benefits over the copy-properties method (see 1), in that it does not copy object member properties. Rather it creates another -destination- object with it's prototype set on the source object. Moreover when properties are modified on the dest object, they are created "on the fly", masking the prototype's (src's) properties.This constitutes a fast an effective way of cloning immutable objects.

    The caveat here is that this applies to source objects that should not be modified after creation (immutable). If the source object is modified after creation, all the clone's unmasked properties will be modified, too.

    Fiddle here(http://jsfiddle.net/y5b5q/1/) (needs Object.create capable browser).

    0 讨论(0)
  • 2020-11-22 06:58

    new Operator

    • This is used to create object from a constructor function
    • The new keywords also executes the constructor function
    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

    • You can also use Object.create to create a new object
    • But, it does not execute the constructor function
    • Object.create is used to create an object from another object
    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
    
    
    0 讨论(0)
  • 2020-11-22 07:02

    new and Object.create serve different purposes. new is intended to create a new instance of an object type. Object.create is intended to simply create a new object and set its prototype. Why is this useful? To implement inheritance without accessing the __proto__ property. An object instance's prototype referred to as [[Prototype]] is an internal property of the virtual machine and is not intended to be directly accessed. The only reason it is actually possible to directly access [[Prototype]] as the __proto__ property is because it has always been a de-facto standard of every major virtual machine's implementation of ECMAScript, and at this point removing it would break a lot of existing code.

    In response to the answer above by 7ochem, objects should absolutely never have their prototype set to the result of a new statement, not only because there's no point calling the same prototype constructor multiple times but also because two instances of the same class can end up with different behavior if one's prototype is modified after being created. Both examples are simply bad code as a result of misunderstanding and breaking the intended behavior of the prototype inheritance chain.

    Instead of accessing __proto__, an instance's prototype should be written to when an it is created with Object.create or afterward with Object.setPrototypeOf, and read with Object.getPrototypeOf or Object.isPrototypeOf.

    Also, as the Mozilla documentation of Object.setPrototypeOf points out, it is a bad idea to modify the prototype of an object after it is created for performance reasons, in addition to the fact that modifying an object's prototype after it is created can cause undefined behavior if a given piece of code that accesses it can be executed before OR after the prototype is modified, unless that code is very careful to check the current prototype or not access any property that differs between the two.

    Given

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

    the following VM pseudo-code is equivalent to the statement const x0 = new X(1);:

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

    Note although the constructor can return any value, the new statement always ignores its return value and returns a reference to the newly created object.

    And the following pseudo-code is equivalent to the statement const x1 = Object.create(X.prototype);:

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

    As you can see, the only difference between the two is that Object.create does not execute the constructor, which can actually return any value but simply returns the new object reference this if not otherwise specified.

    Now, if we wanted to create a subclass Y with the following definition:

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

    Then we can make it inherit from X like this by writing to __proto__:

    Y.prototype.__proto__ = X.prototype;

    While the same thing could be accomplished without ever writing to __proto__ with:

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

    In the latter case, it is necessary to set the constructor property of the prototype so that the correct constructor is called by the new Y statement, otherwise new Y will call the function X. If the programmer does want new Y to call X, it would be more properly done in Y's constructor with X.call(this, u)

    0 讨论(0)
提交回复
热议问题