为什么以及如何__proto__记住构造函数的未定义原型成员的旧属性?

时间:2013-02-17 20:42:46

标签: javascript prototype

快速注意:请不要在答案中解释javascript继承的基础知识。

这是一个简单的构造函数,其中有一些属性附加到它的原型成员。

function Foo() { 
    this.relationship = "Love"; 
};

Foo.prototype.name = "Natalie";
Foo.prototype.age = 22;
Foo.prototype.country = "France";

现在我们用Foo创建新对象并测试一些基础知识。一切都很酷。

var girl = new Foo();

girl.hasOwnProperty("relationship"); //=> true
girl.hasOwnProperty("name");         //=> false

girl.relationship;  //=> "Love"
girl.name;          //=> "Natalie", this comes from Foo.prototype

girl.__proto__ === Foo.prototype;            //=> true
girl.__proto__.name === Foo.prototype.name;  //=> true
girl.name === Foo.prototype.name;            //=> true

如果我们更新Foo.prototype.name属性的值,girl.name将指向新值。

Foo.prototype.name = "Lucia";
girl.name;  //=> "Lucia", this comes from Foo.prototype


当我们改变Foo.prototype并使其为null,未定义,空对象等时,会发生神秘的事情。

Foo.prototype = null;

如果我们的 girl 对象有一个隐藏的__ proto__(ECMA [[Prototype]])链接到Foo.prototype,那么在制作Foo.prototype null 之后女孩应该没有机会获得名称属性,但确实如此!

girl.name;     //=> "Lucia"
girl.age;      //=> 22
girl.country;  //=> "France"

现在如果我们用Foo创建另一个对象。它没有 name age country ,因为当然,Foo.prototype为null。

var new_girl = new Foo();
new_girl.name;     //=> undefined
new_girl.age;      //=> undefined
new_girl.country;  //=> undefined


所以我的问题是在我们将Foo.prototype指定为 null 之后,前一个对象( girl )和他隐藏的__ proto__链接如何记住这些属性吗

1 个答案:

答案 0 :(得分:2)

它根本不是神秘的,它与继承无关 它与对象指针/引用有关。

var a = { name : "Bob", age : 32 };
var b = a;

b.name; // "Bob";
a.name = "Jim";
b.name; // "Jim";
a = null;
b.name; // "Jim";

发生了什么事?

ab被赋予了指向同一对象的指针 当您更改对象的属性时,通过引用其中一个(如果您在ab上更改它们无关紧要),则另一个引用也会看到变化。

你没有制作具有相同属性和值的新对象,你只是给它们同一个对象的地址,并且每次你问它们时它们都会查找属性。

然后重新分配a

您没有更改对象,而是将a地址提供给其他地方 b仍有地址。

所以现在想一想:

function Foo () { }
Foo.prototype = { name : "Bob", age : 32 };

var a = new Foo();
var b = new Foo();

a.__proto__ === Foo.prototype;
b.__proto__ === Foo.prototype;

Foo.prototype.age = 35;

a.age; // 35
b.age; // 35

// now we're replacing the `.prototype` reference with a brand new object
Foo.prototype = { name : "Sally", age : 16 };

a.__proto__ !== Foo.prototype;
b.__proto__ !== Foo.prototype;

a.name; // "Bob"
b.age;  // 35

所有构造函数在后台都在说:

this.constructor = arguments.callee;
this.__proto__   = this.constructor.prototype;

因此,当您更改this.constructor.prototypethis.__proto__上引用的对象的属性时,将查找新值。
但是从一个引用中删除引用不会删除另一个引用的对象。

如果这是你想要的结果,那么你需要擦除原型的每个属性(无论你从哪里做 - 来自函数或来自任何实例)然后使{{无效1}}通过将其设置为空对象(null会在较小的浏览器上导致错误),因此将来的对象无法访问。