2014-09-28 81 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