2013-04-24 98 views
2

我希望能够学习按照新的JavaScript ECMA 5标准创建对象,并在我的当前项目中使用它们,而不会破坏功能。但我看到未解释的东西,这让我害怕Object.create而不是构造函数继承

考虑下面的代码:

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 */ }); 

请告诉我这样做的正确方法?

+0

* *为什么你想摆脱建设者? – Bergi 2013-04-24 08:44:43

+1

@Bergi,只是为了学习现代JavaScript,强调相同。不要把我误认为叛徒队友。对于详细的解释,我始终用构造函数的方式作为结构体 – 2013-04-24 18:37:56

回答

1

这里的问题是您为每个属性省略的descriptors(除value之外)的默认值。

使用该语法(像definePropertydefineProperties),writablefalse默认情况下,和enumerablefalse

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

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

http://jsfiddle.net/YZmbg/

另外,希望你明白这是如何不能在所有浏览器都支持;请注意0​​。符合writableconfigurable属性的垫片不存在也不能存在。

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

0
bar2 // logs Object {} 

我不能够改变BAR2

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

这是因为第二个参数Object.create酷似Object.defineProperties财产 - 这意味着你正在使用property descriptors。虽然您只设置,但enumerablewritableconfigurable标志默认为false

什么是正确的做法?

要么你明确地设置enumberablewritable标志明确地true,或者您不使用第二个参数:

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

但是,如果这看起来太冗长的话,你会用工厂实用功能 - 基本上是一个构造函数。

1

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

对于bar2你没有提供的描述和enumerable默认值和可写的是false。因此,您无法更改属性或在for...in循环中枚举它们。

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

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

现在我们想AdvancedSlideshowSlideshow继承。所以我们希望父项的所有函数都可用于其子项的实例。如果没有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); 
} 

现在AdvancedSlideshowSlideshow继承一切,但也SlideshowAdvancedSlideshow继承一切。那就是我们不想要

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

所以我们必须使用更复杂的东西。道格拉斯克罗克福德很久以前就制作了一个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。我们可以代码:4

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

代替

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

然后从SlideshowSlideshow犯规AdvancedSlideshow继承AdvancedSlideshow继承。

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

编辑2014年2月20日: 我前几天reallized,即constructor属性为原型链的一部分。因此,如果我们使用Object.create进行继承,我们也会更改子对象的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 

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

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

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

+0

+1的基础,一直对Prototypal Inheritance进行长时间编码。但是,我接受另一个答案,说我错过了**属性描述符**。 PS:作为捷克语,你的语法非常好。 :) – 2013-05-02 18:59:02