Javascript 原型继承和指针

Javascript 原型继承和指针,javascript,Javascript,我正在用javascript玩原型,但我无法理解一些继承概念。请考虑下面的代码片段和我在评论中的问题。 function Cat() { } Cat.prototype.age = 10; var cat1 = new Cat(); // cat has age = 10 Cat.prototype = { age: 15 }; // the Cat's prototype is pointing to a new object with age = 15 var cat2 = new

我正在用javascript玩原型,但我无法理解一些继承概念。请考虑下面的代码片段和我在评论中的问题。

function Cat() {
}

Cat.prototype.age = 10;

var cat1 = new Cat(); // cat has age = 10

Cat.prototype = { age: 15 }; // the Cat's prototype is pointing to a new object with age = 15

var cat2 = new Cat(); // cat2 is using the new prototype object, thus having age = 15

display(cat1.age); // will display 10, because cat1 is using the old prototype object
display(cat2.age); // will display 15 because cat2 is using the new prototype object
上面的例子清楚地说明了原型引用是如何工作的。但是,在继承时,相同的原则似乎不适用,如下所示

function Animal() {
}

Animal.prototype.speak = function () {
    console.log("Speak 1");
}

function Cat() {
}

Cat.prototype = Object.create(Animal.prototype); // creates a new instance of the Animal prototype, Cat.prototype === Animal.prototype is FALSE
var cat1 = new Cat(); // this cat instance uses the prototype created at the above step (right?) and not the actual Animal.prototype

Animal.prototype.speak = function() { // this code changes the 'actual' Animal.prototype instance, NOT cat1's prototype (right?)
    console.log("Speak 2");
}

cat1.speak(); // this displays Speak 2. Why ??

在第一个示例中,将替换整个prototype对象。在第二种情况下,原型链保持完整,因为您不替换任何对象;仅更新属性值(“speak”属性)

原型链由实际原型对象组成,而不是原型对象的副本。因此,更新“speak”属性会影响原型链中包含该对象的所有对象

因此:在创建cat1之后,这将是正确的:

Object.getPrototypeOf(cat1) === Cat.prototype
同样地

Object.getPrototypeOf(Cat.prototype) === Animal.prototype

也将是真实的。更新“speak”属性后,这两个表达式仍然为true。由于原型链完全相同,因此,
cat1.speak()
将调用新函数而不是旧函数。

在第二个示例中-您从未更改过
Cat
的原型(或
动物的原型),只为
动物
的原型添加了一个方法,该方法已链接到
的原型

所以与你的第一个例子更合适的类比是

功能动物(){
}
Animal.prototype.speak=函数(){
console.log(“Speak 1”);
}
函数Cat(){
}
Cat.prototype=Object.create(Animal.prototype);
var cat1=新的Cat();
Animal.prototype={speak:function(){console.log(3)}

cat1.speak()//说1,因为年长动物的原型眼仍将被链接
您走在正确的轨道上

但是,
cat1
speak
属性是通过检查属性链来解决的。它不使用由
对象创建的副本。create()
对象。create
不创建副本。参见Mozilla:

create()方法使用现有的 对象提供新创建的对象的proto。(请参见浏览器。) 控制台,用于查看证据。)

资料来源:


请注意,
Object.create(somePrototype)。\uuuu proto\uuuu===somePrototype
并且当
a
的属性
b
被解析时,它会检查
a.\uu proto\uuuuu.b
,如果必要的话。

这是一个关于内存引用而不是原型链接等的问题

执行此代码段后,请参阅注释和打印的日志

功能动物(){
}
Animal.prototype.speak=函数(){
console.log(“Speak 1”);
}
函数Cat(){
}
//您已经创建了一个新对象,其中属性“speak”指向同一个函数
//用动物语言
Cat.prototype=Object.create(Animal.prototype);
//相同内存引用
log(“指向内存中的相同函数:”,Cat.prototype.speak==Animal.prototype.speak);
var cat1=新的Cat();
//在这里,您正在更改Cat.protype.speak,因为
//两个原型都指向同一个函数“speak”
Animal.prototype.speak=函数(){
console.log(“Speak 2”);
}
console.log('分配新函数后')
log(“指向内存中的相同函数:”,Cat.prototype.speak==Animal.prototype.speak);
cat1.speak();//这将显示Speak 2。为什么??
原因 正如其他人所指出的,使用
Object.assign
Object.create
仍将链接到父原型。原因是对象和函数是通过引用传递的,而不是通过值传递的

因此,虽然
Object.assign
将创建原型对象的新实例,但新原型的方法在内存中仍然引用父对象的方法

这一点在以下方面进行了简要说明:

然而,对象引用也是值,它们是特殊的:如果 函数更改引用对象的属性,该更改是 在函数外部可见,如以下示例所示


解决方案 如果需要,可以使用
函数
构造函数手动创建每个父原型方法的实际副本,从而创建不引用父原型方法的方法

Function.prototype.extends = function(parent){
  for(let p in parent.prototype){
    let f = parent.prototype[p].toString().replace(/\r/g, '');
    let params = (f.match(/function.*\((.*)\)/)||['',''])[1].split(',').map(p=>p.trim());
    var body = f.match(/{(?:\n|.)*}/)[0].slice(1,-1);
    this.prototype[p] = Function(...params, body);
  }
}
然后你只需打电话给

Cat.extends(Animal);
执行此操作后,您可以自由更改
动物
原型,而不会影响
原型,反之亦然


例子
Function.prototype.extends=函数(父级){
for(让p进入parent.prototype){
设f=parent.prototype[p].toString().replace(/\r/g');
设params=(f.match(/function.*\(.*)/)| |['',][1].split(',').map(p=>p.trim());
var body=f.match(/{(?:\n |.*}/)[0]。切片(1,-1);
this.prototype[p]=函数(…参数,body);
}
}
函数Animal(){}
Animal.prototype.speak=函数(){
console.log(“Speak 1”);
}
函数Cat(){}
//继承
猫(动物);
var cat1=新的Cat();
Animal.prototype.speak=函数(){
console.log(“Speak 2”);
}
cat1.speak();

新动物说话
您从未更改过猫的原型,只是使用
*,在动物的原型中添加了一个方法。原型=…
打破了原型链。您有4个不同的人解释了原因,但没有实际的解决方案,所以我给出了一个解决方案。@DimitarTsonev是,但是,您在第二个示例中没有像在第一个示例中一样更改对动物原型的引用。@downvoter-解释您的downvote,请留下评论。