js继承之原型链继承

481 阅读2分钟

这是我参与更文挑战的第7天,活动详情查看:更文挑战

原型链的继承

function Parent () {
    this.parentDesc = '这是父级的描述'
}
Parent.prototype.getDesc = function () {
    return this.parentDesc
}
Parent.prototype.parentName = '父级'
Parent.prototype.obj = {
    name: '父级的对象'
}

function Child () {}

Child.prototype = new Parent()
Child.prototype.constructor = Child

let child1 = new Child()
// new的隐藏的操作 child.__proto__ === Child.prototype 
console.log(child1.getDesc()) // '这是父级的描述'
let child2 = new Child()
console.log(child2.getDesc()) // '这是父级的描述'

// 修该Parent的parentName属性值
child1.parentName = '父级被改名了吗'
child1.obj.name = '把name值改了'

console.log(child2.parentName) // '父级'
console.log(child2.obj.name) // '把name值改了'

上述代码中,将Child的原型对象Child.prototype指向了Animal的实例对象,而Parent的实例对象的__proto__属性指向它的原型对象Parent.prototype, 所以Child.prototype = new Parent()等价于Child.prototype = Parent.prototype,Child的实例对象child1和child2可以访问其原型链上的属性和方法,此时原型对象指向了Parent.prototype,故此也就可以输出child1.getDesc()

PS:在通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做就会重写原型链。

这就是我们所说的原型链继承,其本质是将一个对象的原型对象指向另一个对象的实例对象。 那么其优缺点也就不言而喻。

  • 优点:多个实例可以共享原型链上定义的属性和方法。
  • 缺点:每个实例对引用类型的属性的修改也会被其他实例共享,这不是我们想看到的。
  • 缺点:子类在实例化的时候不能给父类构造函数传参。 再来看下面这个栗子:
// 从一个函数里创建一个对象o,它自身拥有属性a和b的:
function f() {
  this.a = 1;
  this.b = 2;
}
let o = new f(); // {a: 1, b: 2}

// 在f函数的原型上定义属性
f.prototype.b = 3;
f.prototype.c = 4;
console.log(o.b) // 2
console.log(o.c) // 4

/** 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链,
因为 {b:3,c:4} 也是个对象 {b:3,c:4}.prototype.__proto__ 指向 Object.prototype

**/
// o.[[Prototype]] 有属性 b 和 c
//  (其实就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最后o.[[Prototype]].[[Prototype]].[[Prototype]]是null
// 这就是原型链的末尾,即 null,
// 根据定义,null 就是没有 [[Prototype]]。

// 综上,整个原型链如下:

// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null

console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为 1

console.log(o.b); // 2
// b是o的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为"属性遮蔽 (property shadowing)"

console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看它的原型上有没有
// c是o.[[Prototype]]的属性吗?是的,该属性的值为 4

console.log(o.d); // undefined
// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined

看这张对应的图

WX20210607-222643.png

PS:对原型理解不是那么清晰的可移步这里