在父原型上设置属性

时间:2013-09-27 10:42:54

标签: javascript prototype prototypal-inheritance

我一直在尝试原型继承,如下面的代码片段所示。

function extend(c, p) {
    function f() { this.constructor = c; }
    f.prototype = p.prototype;
    c.prototype = new f();
}

function Parent() {}
function Child() {}

extend(Child, Parent);

var p = new Parent();
var c = new Child();

// Child.prototype.say = function () { alert("child"); };
Parent.prototype.say = function () { alert("parent"); };

p.say();
c.say();

运行此脚本时,将显示两个显示parent的警报。

但是,如果我取消注释注释行,则第一个警告会显示parent,而第二个警告会显示child
乍一看,这是非常意外的。似乎Parent.say覆盖 Child.say,因为它会在稍后设置。

根据我的理解,由于Child.prototype是对象的实例,f的实例,因此在此原型上设置的所有属性都直接设置在f的特定实例上。 / p>

调用c.say时,会发生以下情况:

  1. 如果直接在say上设置了c,请将其调用。它永远不会直接在实例上设置,因此,请跳至2.

  2. say中查找Children.prototypef的实例。再次,直接在f的实例上查找属性集。如果该行取消注释,则会在此处找到say,搜索将停止。

  3. say中查找f.prototype,即Parent.prototype。如果该行仍然已评论,则会找到say

  4. 问:我是否正确理解JavaScript如何查找该属性?如果是这种情况,则可以解释为什么在取消注释该行时,父属性不会覆盖子属性

1 个答案:

答案 0 :(得分:1)

  

我是否正确理解JavaScript如何查找该属性?

基本上,是的。但重要的是要注意,对象的底层原型由new操作设置为指向构造函数的prototype属性引用的对象,此时如果指向构造函数{{1在一个完全不同的对象的属性,它不会对现有的子项产生任何影响。孩子们指的是对象,而不是财产。

所以一般来说,属性查找的工作原理如下:

  • 该属性是否存在于对象本身上?如果是这样,请使用其值。
  • 不,该对象的底层原型上是否存在该属性?如果是这样,请使用其值。
  • 它的原型的原型是否存在?如果是,请使用值。
  • 依此类推,直到我们找到属性,或用完原型。

让我们在那里建造一些ASCII-Art,只是为了好玩:

+-----------+
| Parent    |<---------------------------+
+-----------+                            |
| prototype |---------->+-------------+  |
+-----------+      +--->|  (object)   |  |
                   | +->+-------------+  |
                   | |  | constructor |--+       +------------------+
                   | |  | say         |--------->|    (function)    |
                   | |  +-------------+          +------------------+
                   | |                           | alert("parent"); |
                   | |                           +------------------+
                   | |
                   | +--------------------------------------------------+
                   |                                                    |
                   +-----------------------+                            |
+-----------+                              |                            |
| Child     |<---------------------------+ |                            |
+-----------+           +-------------+  | |                            |
| prototype |------+--->|  (object)   |  | |                            |
+-----------+      |    +-------------+  | |                            |
                   |    | constructor |--+ |                            |
                   |    | __proto__   |----+     +------------------+   |
                   |    | say         |--------->|    (function)    |   |
                   |    +-------------+          +------------------+   |
                   |                             | alert("child");  |   |
+-----------+      |                             +------------------+   |
|     c     |      |                                                    |
+-----------+      |                                                    |
| __proto__ |------+                                                    |
+-----------+                                                           |
                                                                        |
+-----------+                                                           |
|     p     |                                                           |
+-----------+                                                           |
| __proto__ |-----------------------------------------------------------+
+-----------+

...其中prototype是隐藏属性,表示对象与其原型的链接。 (有些引擎实际上暴露了这个,并且有人建议将其添加到标准中。)

正如您所看到的,__proto__Child.prototype实例的c都指向同一个对象(同样适用于__proto__Parent.prototype的{ {1}})。

我在上面第一段中作出区分的原因是:

p

__proto__function Foo() { } Foo.prototype.a = 1; var f1 = new Foo(); Foo.prototype = {b: 2}; // Putting an entirely new object on `prototype`, not just modifying it var f2 = new Foo(); console.log(f1.a); // "1" console.log(f1.b); // "undefined" console.log(f2.a); // "undefined" console.log(f2.b); // "2" 最终会有完全不同的原型,到上述结束时,f1的{​​{1}}不再引用同一个对象{{ 1}}指的是。

出于这个原因,除非在特定情况下(例如您的f2函数),我强烈建议将新对象分配给构造函数的f1属性,因为它可能会让人感到困惑。 :-)你的__proto__函数再次成为例外。