原型链总结及继承

https://www.muyiy.cn/blog/5/5.2.html#%E5%8E%9F%E5%9E%8B%E9%93%BE%E7%BB%A7%E6%89%BF

原型链继承

原型链继承的本质是重写原型对象,代之以一个新类型的实例。如下代码,新原型 Cat 不仅有 new Animal() 实例上的全部属性和方法,并且由于指向了 Animal 原型,所以还继承了Animal 原型上的属性和方法。

function Animal() {
    this.value = 'animal';
}

Animal.prototype.run = function() {
    return this.value + ' is runing';
}

function Cat() {}

// 这里是关键,创建 Animal 的实例,并将该实例赋值给 Cat.prototype
// 相当于 Cat.prototype.__proto__ = Animal.prototype
Cat.prototype = new Animal(); 

var instance = new Cat();
instance.value = 'cat'; // 创建 instance 的自身属性 value
console.log(instance.run()); // cat is runing

问题 1

原型链继承方案中,原型实际上会变成另一个类型的实例,如下代码,Cat.prototype 变成了 Animal 的一个实例,所以 Animal 的实例属性 names 就变成了 Cat.prototype 的属性。

而原型属性上的引用类型值会被所有实例共享,所以多个实例对引用类型的操作会被篡改。如下代码,改变了 instance1.names 后影响了 instance2。

function Animal(){
this.names = [“cat”, “dog”];
}
function Cat(){}

Cat.prototype = new Animal();

var instance1 = new Cat();
instance1.names.push(“tiger”);
console.log(instance1.names); // [“cat”, “dog”, “tiger”]

var instance2 = new Cat();
console.log(instance2.names); // [“cat”, “dog”, “tiger”]

问题 2

子类型原型上的 constructor 属性被重写了,执行 Cat.prototype = new Animal() 后原型被覆盖,Cat.prototype 上丢失了 constructor 属性, Cat.prototype 指向了 Animal.prototype,而 Animal.prototype.constructor 指向了 Animal,所以 Cat.prototype.constructor 指向了 Animal。

Cat.prototype = new Animal(); 
Cat.prototype.constructor === Animal
// true

解决办法就是重写 Cat.prototype.constructor 属性,指向自己的构造函数 Cat。

function Animal() {
    this.value = 'animal';
}

Animal.prototype.run = function() {
    return this.value + ' is runing';
}

function Cat() {}
Cat.prototype = new Animal(); 

// 新增,重写 Cat.prototype 的 constructor 属性,指向自己的构造函数 Cat
Cat.prototype.constructor = Cat; 

在这里插入图片描述

问题 3

给子类型原型添加属性和方法必须在替换原型之后,原因在第二点已经解释过了,因为子类型的原型会被覆盖。

function Animal() {
    this.value = 'animal';
}

Animal.prototype.run = function() {
    return this.value + ' is runing';
}

function Cat() {}
Cat.prototype = new Animal(); 
Cat.prototype.constructor = Cat; 

// 新增
Cat.prototype.getValue = function() {
  return this.value;
}

var instance = new Cat();
instance.value = 'cat'; 
console.log(instance.getValue()); // cat

问题 4 属性遮蔽

改造上面的代码,在 Cat.prototype 上添加 run 方法,但是 Animal.prototype 上也有一个 run 方法,不过它不会被访问到,这种情况称为属性遮蔽 (property shadowing)。

function Animal() {
    this.value = 'animal';
}

Animal.prototype.run = function() {
    return this.value + ' is runing';
}

function Cat() {}
Cat.prototype = new Animal(); 
Cat.prototype.constructor = Cat; 

// 新增
Cat.prototype.run = function() {
  return 'cat cat cat';
}

var instance = new Cat();
instance.value = 'cat'; 
console.log(instance.run()); // cat cat cat

那如何访问被遮蔽的属性呢?通过 proto 调用原型链上的属性即可。

// 接上

console.log(instance.__proto__.__proto__.run()); // undefined is runing

小结

1.每个对象拥有一个原型对象,通过 proto 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这种关系被称为**原型链 **
2.当访问一个对象的属性 / 方法时,它不仅仅在该对象上查找,还会查找该对象的原型,以及该对象的原型的原型,一层一层向上查找,直到找到一个名字匹配的属性 / 方法或到达原型链的末尾(null)。
3.原型链的构建依赖于 proto,一层一层最终链接到 null。
4.instanceof 原理就是一层一层查找 proto,如果和 constructor.prototype 相等则返回 true,如果一直没有查找成功则返回 false。
5.原型链继承的本质是重写原型对象,代之以一个新类型的实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值