Object.create而不是用于继承的构造方法

问题描述 投票:2回答:4

我希望能够学习按照新的JavaScript ECMA 5标准创建对象,并在我当前的项目中使用它们,而又不会破坏功能。但是我看到无法解释的东西让我感到害怕

请考虑以下代码:

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?

也,

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

此外,我无法更改bar2的属性

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

还有更多-

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

我是否以正确的方式创建对象?我基本上想要实现的是摆脱构造函数。

旧代码:

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

我想更改为Object.create,可能是这样的:

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

什么是正确的方法?

javascript ecmascript-5
4个回答
1
投票

您的问题是您为每个属性省略的descriptors(除value之外的默认设置。

使用此语法(类似于definePropertydefineProperties),默认情况下writablewritablefalseenumerable

将它们都设置为enumerable将导致您期望的行为。

false

true

此外,希望您对所有浏览器都不支持此功能表示感谢;注意var bar2 = Object.create( Object.prototype, { oldProp: { value: 'ECMA Script 3', writable: true, enumerable: true} }); 。依附于http://jsfiddle.net/YZmbg/compatibility table for Object.create属性的垫片不存在,也不可能存在。

最后,对于一个写得很好的问题,+ 1;这是新鲜空气的呼吸。


2
投票

我将尝试回答这个问题:

我是否以正确的方式创建对象?我基本上想要实现的是摆脱构造函数。

第一个小例子。想象一下,我们有一个简单的结构:

Object.create

现在,我们希望writable继承自configurable。因此,我们希望父级的所有功能都可用于其子级实例。没有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); } ,我们就是这样:

AdvancedSlideshow

现在Slideshow继承了Object.create的所有内容,但// 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); } 继承了AdvancedSlideshow]的所有内容>。那就是我们不想

Slideshow

所以我们必须使用更复杂的东西。很久以前,Douglas Crockford制作了一个polyfill。

Slideshow

首先,它不是polyfill,它是小图案,看起来像其他情况。但是模式在不断发展,然后变得如此出色,以至于它成为了javascript的一部分。因此,现在我们为不支持ecmascript-5的浏览器提供了一个polyfill。我们可以编写代码:

AdvancedSlideshow

而不是

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

然后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(); }; } 继承自// THIS IS CORRECT AdvancedSlideshow.prototype = Object.create(Slideshow.prototype); ,但function F() {}; F.prototype = Slideshow.prototype; AdvancedSlideshow.prototype = new F(); 不继承自AdvancedSlideshow

因此Slideshow的目的不是摆脱构造函数或“ new”关键字。其目的是制作正确的原型链!

编辑20.2.2014:

几天前,我意识到Slideshow属性是原型链的一部分。因此,如果我们使用AdvancedSlideshow进行继承,我们还将更改子对象的Object.create属性(不是构造函数本身,只是一个属性)。因此,子对象的新实例的构造方法属性指向其父对象。我们可以用简单的constructor来解决。
Object.create

现在,存在Object.create的第二个参数,该参数使您可以说出对象的“隐藏机密”。但是我不推荐

您这样做,因为您不需要它。它是高级的东西,也可以像某些模式一样工作。

如果要避免使用构造函数,“ new”关键字或其他重要内容,那么您可能正在寻找某种工厂模式。但是请记住,例如“ new”有一些弱点,但是删除内置功能的模式通常更糟!无论如何,也许您可​​以在这里找到一些灵感:

constructor

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 作为其原型,因此http://www.2ality.com/2010/12/javascripts-prototypal-inheritance.html因此返回false。

对于bar,您不提供描述符,foo且可写的默认值为bar.hasOwnProperty('oldProp');。因此,您无法更改属性或在bar2循环中枚举它们。

关于我是否以正确的方式创建对象?

:您正在做的事情是合理的。您将enumerable创建为所有幻灯片的原型,并使用此原型和其他属性创建幻灯片。
false

我无法更改bar2的属性

for...in

这是因为Slideshow的第二个参数类似于bar2 // logs Object {} -这表示您正在使用for (k in bar2) console.log(bar2[k]) // undefined 。当您仅设置value时,Object.createObject.definePropertiesObject.defineProperties标志默认为property descriptors

什么是正确的方法?

要么您将enumerablewritable标志显式设置为configurable,要么不使用第二个参数:

false

但是,如果您觉得这太冗长,则可以使用工厂实用程序功能-本质上是构造函数。


1
投票

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 作为其原型,因此http://www.2ality.com/2010/12/javascripts-prototypal-inheritance.html因此返回false。


0
投票
false
© www.soinside.com 2019 - 2024. All rights reserved.