ES5如何实现继承
一.原型链继承
原型链继承的原理很简单,直接让子类的原型对象指向父类实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承
function Prant() { this.name = 'hello' } Prant.prototype.getName = function() { return this.name; } function child() {} // 子类 //让子类的原型对象指向父类实例, 这样一来在Child实例中找不到的属性和方法就会到原型对象(父类实例)上寻找 child.prototype = new Prant(); // 根据原型链的规则,顺便绑定一下constructor, 这一步不影响继承, 只是在用到constructor时会需要 child.prototype.constructor = child; const alias = new child(); alias.name //hello alias.getName(); //hello
缺点:
- 由于所有Child实例原型都指向同一个Parent实例, 因此对某个Child实例的父类引用类型变量修改会影响所有的Child实例
- 在创建子类实例时无法向父类构造传参, 即没有实现
super()
的功能
二.构造函数继承
构造函数继承,即在子类的构造函数中执行父类的构造函数,并为其绑定子类的this
,让父类的构造函数把成员属性和方法都挂到子类的this
上去,这样既能避免实例之间共享一个原型实例,又能向父类构造方法传参
function Parent(name) { this.name = [name] } Parent.prototype.getName = function() { return this.name } function Child() { // 子类 Parent.call(this, 'hello') } const child1 = new Child(); const child2 = new Child(); child1.name[0] = '构造函数继承'; console.log(child1.name) // 构造函数继承 console.log(child2.name) //hello child2.getName(); // 找不到,报错
缺点:
- 继承不到父类原型上的属性和方法
三.组合式继承
将以上两者组合起来使用
function Parent(name) { this.name = [name] } Parent.prototype.getName = function() { return this.name } function Child() { // 构造函数继承 Parent.call(this, 'zhangsan') } //原型链继承 Child.prototype = new Parent() Child.prototype.constructor = Child //测试 const child1 = new Child() const child2 = new Child() child1.name[0] = 'hello' console.log(child1.name) // ['hello'] console.log(child2.name) // ['zhangsan'] child2.getName() // ['zhangsan']
缺点:
- 每次创建子类实例都执行了两次构造函数(
Parent.call()
和new Parent()
),虽然这并不影响对父类的继承,但子类创建实例时,原型中会存在两份相同的属性和方法,这并不优雅
四.寄生式组合继承
解决构造函数被执行两次的问题, 我们将指向父类实例
改为指向父类原型
, 减去一次构造函数的执行
function Parent(name) { this.name = [name] } Parent.prototype.getName = function() { return this.name } function Child() { // 构造函数继承 Parent.call(this, 'zhangsan') } //原型链继承 // Child.prototype = new Parent() Child.prototype = Object.create(Parent.prototype) //将`指向父类实例`改为`指向父类原型` Child.prototype.constructor = Child //测试 const child = new Child() const parent = new Parent() child.getName() // ['zhangsan'] parent.getName() // 报错, 找不到getName()
目前最成熟的继承方式,js高级体现。
回顾:
说到js继承,最开始想到的应该是是
原型链继承
,通过把子类实例的原型指向父类实例来继承父类的属性和方法,但原型链继承的缺陷在于对子类实例继承的引用类型的修改会影响到所有的实例对象
以及无法向父类的构造方法传参
。构造函数继承
, 通过在子类构造函数中调用父类构造函数并传入子类this来获取父类的属性和方法,但构造函数继承也存在缺陷,构造函数继承不能继承到父类原型链上的属性和方法
。后面有了组合式继承,但也有了新的问题,每次都会执行两次父类的构造方法,最终有了寄生式组合式继承。