JavaScript中的原型链接

时间:2010-11-08 21:27:29

标签: javascript inheritance prototype reference chaining

我正在读一本名为JavaScript模式的书,但有一部分我认为这个人很困惑。

这个家伙实际上在书中引出了klass设计模式,在那里他逐一开发了它。他首先提出了这个问题:

function inherit(C, P) {
C.prototype = P.prototype;
}

他说:

“这为您提供了简短快速的原型链查找,因为所有对象实际上共享相同的原型。但这也是一个回顾,因为如果一个孩子或孙子 在继承链的某个地方修改了原型,它影响了所有的父母 和祖父母。“

然而,我实际上试图修改Child中的原型say()并且它对Parent没有影响,实际上Child仍然指向Parent并且完全忽略了它自己的同名原型,这是有意义的,因为它指向一个不同的记忆位置。那家伙怎么能这样说呢?以下证明了我的观点:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

Child.prototype.say = function () {
return 10;
};

inherit(Child, Parent);

function inherit(C, P) {
C.prototype = P.prototype;
 } 

 var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

任何孩子或孙子都不可能修改原型!

这导致了我的第二点。他说,在继承链(我无法重现)中意外修改父原型的可能性问题的解决方案是打破父母和孩子原型之间的直接联系,同时从原型链中受益。他提供以下解决方案:

function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}

问题是输出与其他模式完全相同的值:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

 Child.prototype.say = function () {
return 10;
};

inherit(Child, Parent);

function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}

var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

空函数以某种方式破坏链接是没有意义的。实际上,Child指向F和F依次指向Parent的原型。所以他们仍然指向相同的记忆位置。这在上面说明,它输出与第一个例子相同的精确值。我不知道这位作者试图证明什么,以及为什么他提出的声明不会让我痴迷,而且我无法重现。

感谢您的回复。

3 个答案:

答案 0 :(得分:8)

首先要点:

这家伙想说的是,如果你在创建实例之后修改原型,那么child和parent的方法都会改变。

例如:

function inherit(C, P) {
  C.prototype = P.prototype;
} 

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

inherit(Child, Parent);

Parent.prototype.say = function () {
  return 20;
};

var parent = new Parent();
var child = new Child();


// will alert 20, while the method was set on the parent.
alert( child.say() );

更改子项的构造函数(与父项共享)时会发生同样的事情。

// same thing happens here, 
Child.prototype.speak = function() {
  return 40;
};

// will alert 40, while the method was set on the child
alert( parent.speak() );

关于你的第二点:

function inherit(C, P) {
  var F = function () {};
  F.prototype = P.prototype;
  C.prototype = new F();
}

新的继承函数实际上将父项的构造函数与子项分开,因为它不再指向同一个对象,但现在它指向一个与父项无关的新创建函数的原型。因此,您实际上创建了父构造函数的本地副本,然后创建了一个新的副本实例,它将所有构造函数方法返回一个属性。现在通过更改子项的构造函数,它不会影响父项。

function inherit(C, P) {
  var F = function () {};
  F.prototype = P.prototype;
  C.prototype = new F();
}

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

inherit(Child, Parent);

// same thing happens here, 
Child.prototype.speak = function() {
  return 40;
};

var parent = new Parent();

// will throw an error, because speak is undefined
alert( parent.speak() );

答案 1 :(得分:2)

您可以通过将另一个原型指向它来更改原型对象,这是正常的JavaScript行为。 JavaScript的原始值是不可变的,但对象和数组不是。我将用简单的例子来解释它:

var person = {name: 'greg', age: 20};

>>>person.name; //prints 'greg'

>>>person.age; //prints 20

var a = person;

>>>a.name; //prints 'greg'

a.name = 'peter';

>>>a.name; //prints 'peter'

>>>person.name; //prints 'peter'

//I've changed person.name through a.name. That's why objects in JavaScript are called mutable

数组具有相同的行为:

var arr = ['first', 'second', 'third'],
    newArr = arr;

newArr.pop();

>>>newArr; //prints ['first', 'second']

>>>arr; //prints ['first', 'second']

//Frist array was also changed

让我们看看字符串数字和布尔值(原始数据类型):

var str = 'hello world',

    newStr = str;

>>>str; //prints 'hello world'

>>>newStr; //prints 'hello world'

>>>newStr.toUpperCase(); //prints 'HELLO WORLD'

>>>str; //prints 'hello world'

>>newStr; //prints 'hello world'

//Numbers and booleans have similiar behavior

我有同样的问题,但我修复了它。看,我已经评论了你的代码,其中的一些提示可以帮助你:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}



/**
*
* The area you should examine i've selected below.
*
*/

//Start BUG

//new say method will not affect the Parent.prototype beacuse it wasn't assigned yet
Child.prototype.say = function () {
return 10;
};

//rewrite Child.prototype and all it's methods with Parent.prototype
inherit(Child, Parent);

//End BUG



function inherit(C, P) {
C.prototype = P.prototype;
 } 

var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

这里的问题是,不是复制和更改Parent.prototype而是创建新的Child.prototype.say方法,而是在它之后通过Parent.prototype赋值重写整个Child.prototype对象。只是改变他们的顺序,它应该工作正常。

答案 2 :(得分:1)

如果在继承原型后更改原型,您会看到它也改变了原型的原型:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

inherit(Child, Parent);

Child.prototype.say = function () {
return 10;
};

function inherit(C, P) {
C.prototype = P.prototype;
 } 

 var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say()); //10
alert(parent.say()); //10
alert(child2.say()); //10

如果您使用inherit函数的修改版本,则ChildParent原型保持独立。