2014-09-28 82 views
1

我跟着凱爾辛普森的YouDontKnowJS關於frontendmasters.com,並且被他的一個例子弄糊塗了。原型鏈 - 在這種情況下是使用原型鏈嗎?

下面是示例代碼:

function Foo(who){ 
    this.me = who; 
} 

Foo.prototype.identify = function() { 
    return "I am " + this.me; 
}; 

function Bar(who) { 
    Foo.call(this, who); 
} 


Bar.prototype = Object.create(Foo.prototype); 

Bar.prototype.speak = function() { 
    alert("Hello, " + this.identify() + "."); 
} 

var b1 = new Bar("b1"); 
var b2 = new Bar("b2"); 

b1.speak(); 
b2.speak(); 

當調用new Bar("b1");他走你通過什麼與原型鏈發生時b1.speak()被調用

  • Bar.prototype警報函數被調用,this.identify()也被稱爲。
  • 當調用this.identify()時,它在this對象(b1)上找不到,所以它查找它的原型。
  • 看着Bar.prototype它找不到識別方法,所以它再次查找它的原型鏈。
  • 現在發現在Foo.prototype

這裏的識別方法是什麼,我不清楚。當我們呼叫Bar.prototype = Object.create(Foo.prototype)時,不應該Bar.prototype現在引用一個新對象,它是Foo.prototype的一個副本,它會有identify()方法嗎?爲什麼它不得不在原型鏈上多走一步到Foo.prototype找到identify()方法?

Object.create()從文檔的官方定義:

的的Object.create()方法創建一個具有指定原型對象的新對象和屬性

回答

4

當我們調用Bar.prototype = Object.create(Foo.prototype),不應Bar.prototype現在引用新的對象,是Foo.prototype副本...

Object.create複製對象。它創建了一個新的對象,其原型underyling是我們傳遞所以用這個首發:

 
+---------------+ 
| Foo.prototype | 
+---------------+  +----------------------+ 
| [[Prototype]] |---->| Object.prototype | 
+---------------+  +----------------------+ 
| identify: ... |  | [[Prototype]]: null | 
+---------------+  +----------------------+ 
         | ...     | 
         +----------------------+ 

(我已經離開了函數對象爲identify爲簡單起見。)

...當我們這樣做Bar.prototype = Object.create(Foo.prototype),它創建了一個:

 
+---------------+ 
| Bar.prototype | 
+---------------+  +---------------+ 
| [[Prototype]] |---->| Foo.prototype | 
+---------------+  +---------------+  +----------------------+ 
         | [[Prototype]] |---->| Object.prototype | 
         +---------------+  +----------------------+ 
         | identify: ... |  | [[Prototype]]: null | 
         +---------------+  +----------------------+ 
              | ...     | 
              +----------------------+ 

後來,執行Bar.prototype.speak = function...行之後,Bar.prototype也有speak財產。

 
+---------------+ 
| Bar.prototype | 
+---------------+  +---------------+ 
| [[Prototype]] |---->| Foo.prototype | 
+---------------+  +---------------+  +----------------------+ 
| speak: ... |  | [[Prototype]] |---->| Object.prototype | 
+---------------+  +---------------+  +----------------------+ 
         | identify: ... |  | [[Prototype]]: null | 
         +---------------+  +----------------------+ 
              | ...     | 
              +----------------------+ 

var b1 = new Bar("b1");後,我們有:

 
+---------------+ 
|  b1  | 
+---------------+  +---------------+             
| [[Prototype]] |---->| Bar.prototype |             
+---------------+  +---------------+  +---------------+        
| me: "b1"  |  | [[Prototype]] |---->| Foo.prototype |        
+---------------+  +---------------+  +---------------+  +----------------------+ 
         | speak: ... |  | [[Prototype]] |---->| Object.prototype | 
         +---------------+  +---------------+  +----------------------+ 
              | identify: ... |  | [[Prototype]]: null | 
              +---------------+  +----------------------+ 
                    | ...     | 
                    +----------------------+ 

[在上面,[[Prototype]]指對象的內置鏈接到它的原型;名稱爲[[Prototype]]的對象上沒有真正的屬性。 事實上,在ES5中,沒有辦法從對象本身直接訪問原型的鏈接,儘管ES5增加了Object.getPrototypeOf,它可以讓你通過傳入對象引用來檢索它,例如, var p = Object.getPrototypeOf(someObject)。 ES6將添加更多方式與對象的原型進行交互,包括Mozilla的JavaScript多年以來的__proto__屬性。]

+0

這很有道理,謝謝!只是爲了澄清我最後一個問題。在該Bar.prototype上,它有__proto__,它是Prototype鏈接的公共版本,那麼它也會有說法? – HelloWorld 2014-09-28 17:04:28

+0

@HelloWorld:是的,稍後,我們添加它。不是在Object.create之後。我應該證明這一點。 – 2014-09-28 17:06:49

+1

很好的解釋,非常感謝@ T.J Crowder! – HelloWorld 2014-09-28 17:08:02

1

「當我們調用Bar.prototype = Object.create(Foo.prototype),不應該Bar.prototype現在引用一個新的對象,它是Foo.prototype的副本。「

Object.create不會創建對象的副本,但會爲該對象創建委派。所以你必須上升一級到Foo.prototype,因爲這就是Bar.prototype所委託的。

+0

嗯。我對此有點困惑。如果它爲該對象創建了一個委派,並且添加了上面代碼中的'speak'屬性。爲什麼不是Foo.prototype也包含發言屬性?爲什麼文檔說它創建了一個新對象?我不問,我只是沒有看到這是如何工作的? – HelloWorld 2014-09-28 16:59:12