2

我希望能够学习根据新的 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 */ });

这样做的正确方法是什么?

4

4 回答 4

2

我将尝试回答这个问题:

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

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

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继承了一切SlideshowSlideshow继承了一切AdvancedSlideshow。那是我们不想要的。

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

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

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

好吧,首先它不是 polyfill,它是小图案,看起来不像。但是模式在不断发展,然后它变得非常好,以至于它成为了 javascript 的一部分。所以现在我们只有一个用于不支持 ecmascript-5 的浏览器的 polyfill。我们可以编码:

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

代替

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

然后AdvancedSlideshow继承自SlideshowSlideshow不继承自AdvancedSlideshow

所以目的Object.create不是摆脱构造函数或“新”关键字。其目的是制作正确的原型链!

编辑 20.2.2014: 几天前我意识到,该constructor属性是原型链的一部分。因此,如果我们使用Object.create继承,我们也会更改constructor子对象的属性(不是构造函数本身,只是一个属性)。因此,子对象的新实例的构造函数属性指向其父对象。我们可以用 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 

现在,Object.create 有第二个参数,它允许您说对象的“隐藏的秘密”。但我不建议你这样做,因为你不需要它。它是一种先进的东西,也可以像某些模式一样工作。

如果你想避免构造函数、“new”关键字或任何重要的东西,那么你可能正在寻找一些工厂模式。但请记住,例如“新”有一些弱点,但删除内置功能的模式通常更糟!无论如何,也许你可以在这里找到一些灵感:

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

于 2013-04-30T13:44:05.330 回答
1

您的问题是您为每个属性省略的描述符(除了)的默认值。value

使用这种语法(如 fordefinePropertydefineProperties),默认情况下是 ,writable并且是。falseenumerablefalse

将这两个设置为true将导致您期望的行为。

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

http://jsfiddle.net/YZmbg/

另外,希望您能理解并非所有浏览器都支持此功能;请注意 的兼容性表Object.create。符合writableconfigurable属性的垫片不存在,也不可能存在。

最后,为这样一个写得很好的问题+1;这是一股清新的空气。

于 2013-04-24T08:35:27.090 回答
1

bar创建时没有任何属性并foo作为其原型,因此bar.hasOwnProperty('oldProp');返回 false。

因为bar2您不提供描述符,enumerable并且可写的默认值为false. 因此,您不能更改属性或for...in循环枚举它们。

至于我是否以正确的方式创建对象?: 你的做法是合理的。您创建Slideshow为所有幻灯片的原型,并使用此原型和其他属性创建幻灯片。

于 2013-04-24T08:44:56.107 回答
0
bar2 // logs Object {}

我无法更改 bar2 的属性

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

这是因为第二个参数Object.create类似于Object.defineProperties- 这意味着您正在使用属性描述符。虽然您只设置,但enumerablewritable标志configurable默认为false

这样做的正确方法是什么?

要么明确地将enumberableandwritable标志设置为true,要么不使用第二个参数:

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

但是,如果这对您来说太长了,您将使用工厂实用程序函数 - 基本上是构造函数。

于 2013-04-24T08:39:22.297 回答