JavaScript中的原型链

问题描述 投票:8回答:3

我正在读一本叫做JavaScript模式的书,但有一部分我觉得这家伙很困惑。

那个人实际上在书中引向了klass设计模式,在那里他逐步地开发了它。他首先提出问题:

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

他说:

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

但是,我实际上试图修改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

空函数以某种方式断开链接没有意义。实际上,子级指向F,而F依次指向父级的原型。因此,它们仍然都指向相同的内存位置。上面已经说明了这一点,在这里输出的值与第一个示例相同。我不知道作者试图证明什么,为什么他提出的主张对我不利,我无法复制。

感谢您的回复。

javascript inheritance prototype reference chaining
3个回答
8
投票

关于您的第一点:

[这个家伙想说的是,如果您修改创建实例的原型after,则用于子对象和父对象的方法都会改变。

例如:

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() );

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对象。只需更改他们的顺序,它就可以正常工作。


1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.