8

我正在阅读一本名为 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 的原型。所以他们都仍然指向同一个内存位置。上面演示了这一点,它输出与第一个示例相同的精确值。我不知道这位作者试图证明什么,以及为什么他提出的主张对我来说不是凝胶并且我无法复制。

感谢您的回复。

4

3 回答 3

8

对于您的第一点:

这家伙想说的是,如果您在创建实例后修改原型,则子级和父级的方法都会改变。

例如:

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() );
于 2010-11-08T21:38:53.263 回答
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 对象。只需更改他们的订单,它应该可以正常工作。

于 2011-06-05T09:20:38.960 回答
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原型保持分离。

于 2010-11-08T21:35:24.243 回答