Object.create instead of Constructors for inheritance

…衆ロ難τιáo~ 提交于 2020-01-14 02:47:10

问题


I want to be able to learn creating Objects per the new JavaScript ECMA 5 standards, and use them in my current projects, without breaking functionality. But I see un-explainable stuff that makes me afraid

Consider the following code:

var foo = {
        oldProp: 'ECMA Script 3',
        newProp: 'ECMA Script 5'
    };

// The new way of inheritance
var bar = Object.create( foo );

// and of assigning properties, if I am right
var bar2 = Object.create( Object.prototype, {
        oldProp: { value: 'ECMA Script 3'},
        newProp: { value: 'ECMA Script 5'}
    });

// and then
bar; 
// console output
Object {oldProp: "ECMA Script 3", newProp: "ECMA Script 5"}

// whereas !!
bar2;
Object {} // Why this?

Also,

bar.hasOwnProperty('oldProp');  // false, whereas
bar2.hasOwnProperty('oldProp'); // true !! It should be the other way round, right? 

Additionally, I am not able to change the property of bar2

bar2.oldProp = "Marry had a little lamb";
bar2.oldProp; // "ECMA Script 3"

And more --

for (k in bar2) console.log(bar2[k]) // undefined  

Am I creating the Objects in a correct way? What I basically want to achieve is getting rid of constructors.

Old code:

var Slideshow = function(type) {
    this.type = type;
    this.deleteSlideshow = function() {
        // some code
    }
}
Slideshow.prototype.nextSlide = function() {
    // lotsa code
}
var vanityFairSlideshow = new Slideshow('wide-screen');

I want to change to Object.create, may be kind of this:

var Slideshow = Object.create({ /* prototype */ }, { /* properties and methods */ });
var vanityFairSlideshow = Object.create( Slideshow , { /* properties and methods */ });

Whats the proper way of doing it?


回答1:


Your problem here is the defaults for the descriptors (aside from value) that you omitted for each property.

Using this syntax (like for defineProperty and defineProperties), writable is false by default, and enumerable is false.

Setting both of these to true will result in the behaviour you expect.

var bar2 = Object.create( Object.prototype, {
    oldProp: { value: 'ECMA Script 3', writable: true, enumerable: true}
});

http://jsfiddle.net/YZmbg/

Also, hopefully you appreciate how this isn't supported in all browsers; note the compatibility table for Object.create. Shims which adhere to the writable and configurable properties do not, and cannot, exist.

Finally, +1 for such a well written question; it's a breath of fresh air.




回答2:


I'll try to answer on this question:

Am I creating the Objects in a correct way? What I basically want to achieve is getting rid of constructors.

First small example. Imagine that we have this simple structure:

var Slideshow = function(type) {
  this.type = type;
}

Slideshow.prototype.nextSlide = function() {
    console.log('next slide');
}

Slideshow.prototype.deleteSlideshow = function() {
    console.log('delete slideshow');
}

var AdvancedSlideshow = function(bgColor) {
    this.bgColor = bgColor;
}

AdvancedSlideshow.prototype.showMeBgColor = function() {
    console.log('bgColor iS: ' + bgColor);
}

Now we want AdvancedSlideshow to inherit from Slideshow. So we want all functions from parent available for instances of its child. Without Object.create we did it like that:

// THIS CODE ISNT CORRECT
// code for slideshow is same

var AdvancedSlideshow = function(type, bgColor) {
    // this line can supply whole Slideshow constructor
    Slideshow.call( this, type ); 

    this.bgColor = bgColor;
}

AdvancedSlideshow.prototype = Slideshow.prototype; // there problems start!!!!

AdvancedSlideshow.prototype.showMeBgColor = function() { // we did augumentation of Slideshow
    console.log('bgColor is: ' + bgColor);
}

Now AdvancedSlideshow inherits everything from Slideshow, but also Slideshow inherits everything from AdvancedSlideshow. That is what we don't want.

var simpleSlideshow = new Slideshow('simple');
simpleSlideshow.showMeBgColor(); // ReferenceError: bgColor is not defined
                                 // but function exists and that's wrong!

So we have to use something more complicated. There is a polyfill made by Douglas Crockford a long time ago.

if (!Object.create) {
    Object.create = function (o) {
        if (arguments.length > 1) {
            throw new Error('Object.create implementation only accepts the first parameter.');
        }
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Well first it wasn't polyfill, it was small pattern and it looked like otherwise. But pattern was evolving and then it was so good that it became part of javascript. So now we have just a polyfill for browsers that doesn't support ecmascript-5. And we can code:

// THIS IS CORRECT
AdvancedSlideshow.prototype = Object.create(Slideshow.prototype);

instead of

function F() {};
F.prototype = Slideshow.prototype;
AdvancedSlideshow.prototype = new F();

Then AdvancedSlideshow inherits from Slideshow but Slideshow doesn't inherit from AdvancedSlideshow.

So purpose of Object.create isn't to get rid of constructors or "new" keywords. Its purpose is to make correct prototype chain!

EDIT 20.2.2014: Few days ago I realised, that constructor property is part of the prototype chain. So if we use Object.create for inheritance, we also change constructor property (not the constructor itself, just a property) of our child object. Therefore constructor property of new instance of child object points its parent. We can fix that with simple Child.prototype.constructor = Child.

...
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

var first = new Child();
first.constructor === Child; // true
first.constructor === Parent; // false
first instanceOf Child; // true
first instanceOf Parent; // true 

Now, there is a second parameter for Object.create which allows you to make lets say "hidden secrets" of objects. But I don't recommend you to do this, because you don't need it. It is something advanced that also works like some patterns.

If you want to avoid constructor, "new" keyword or anything important, then you are probably looking for some factory pattern. But remember, for example "new" has some weaknesses but pattern for removing built-in features are usually worse! Anyway, maybe you can find some inspiration here:

http://www.2ality.com/2010/12/javascripts-prototypal-inheritance.html




回答3:


baris created without any property and fooas its prototype, so bar.hasOwnProperty('oldProp');consequently returns false.

For bar2you don't provide descriptors and the default value for enumerable and writable is false. So you can't change the properties or enumerate them in a for...in loop.

As for Am I creating the Objects in a correct way?: What you are doing is reasonable. You create Slideshowas a prototype for all slideshows and create a slideshow with this prototype and additional properties.




回答4:


bar2 // logs Object {}

I am not able to change the property of bar2

for (k in bar2) console.log(bar2[k]) // undefined

That is because the second argument to Object.create resembles Object.defineProperties - and that means you are using property descriptors. While you are only setting the value, the enumerable, writable and configurable flags default to false.

Whats the proper way of doing it?

Either you explicitly set the enumberable and writable flags explicitly to true, or you don't use the second parameter:

 var bar2 = Object.create( Object.prototype ); // or Slideshow.prototype or whatever
 bar2.oldProp = 'ECMA Script 3';
 bar2.newProp = 'ECMA Script 5';

However, if this looks too lengthy to you, you would use a factory utility function - basically a constructor.



来源:https://stackoverflow.com/questions/16187072/object-create-instead-of-constructors-for-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!