2013-04-24 97 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