1.原型链继承
function Parent2 () { this.name = '祝敏', this.age = 19, this.play = [1,2,3] } // 一样在父类添加say方法 Parent2.prototype = { say () { console.log('say bye bye') } } function Child2 () { this.address = '硚口区' } // 让子类的原型直接等于父类实例 Child2.prototype = new Parent2() // 生成两个子类的实例s2、s3 var s2 = new Child2() var s3 = new Child2() // s2实例继承了父类中的name属性 console.log(s2.name) //祝敏 // s2实例也同样继承了父类原型上的say方法 console.log(s2.say()) //say bye bye
// 给s2实例继承的play属性的数组中push一个新数字 s2.play.push(4) console.log(s2.play) //[1, 2, 3, 4] console.log(s3.play) //[1, 2, 3, 4]
总结:借助原型链实现继承虽然解决了父类原型的方法能让子类实例对象继承的问题,但是如果我们通过子类的实例对象修改父类上的属性和方法,那么所有子类的所有实例对象上的属性和方法都会被改变。
2.构造继承方式(call()或者apply())
// 定义父类 function Parent1 () { this.name = '陈楚', this.age = 18 } // 定义子类 function Child1 () { //通过call()方法改变Child1的this指向使子类的函数体内执行父级的构造函数从而实现继承效果 Parent1.call(this) this.address = '洪山区' } // 构建子类的实例s1 var s1 = new Child1() console.log(s1.name) //陈楚
确实让s1实例继承了父类Child1的name属性,达到了继承的效果,但是这种继承有一个很严重的缺点
// 父类添加say方法 Parent1.prototype.say = function () { console.log('say bye bye') } // 子类中直接打印这个say方法 console.log(s1.say()) //报错
总结:构造函数继承法只能实现部分继承,如果我们在父类Parent1的原型链上添加属性或者方法的时候子类的实例无法继承到。
3.组合继承
function Parent5 () { this.name = '许风', this.age = 20, this.play = [4,5,6] } function Child5 () { Parent5.call(this) this.address = '江夏区' } // 比较关键的一步 Child5.prototype = Object.create(Parent5.prototype) console.log(Child5.prototype) //Parent5 {}
Object.create()会使用指定的原型对象及其属性去创建一个新的对象,通过Object.create()创建了一个中间对象,这个中间对象有一个特性,它的原型对象是父类的原型对象。
原理:
Object.create()创建的这个对象,原型对象就是参数。所以child5的原型对象是Child5.prototype,这个对象又等于Object.create()创建的新对象,新兑现的原型对象时括号中的参数,也就是Parent5.prototype。连起来了。这样就形成了继承,而且实现了父类和子类原型对象的隔离。这就是原理。
这个时候虽然进行了隔离,但是Child5.prototype依然没有自己的constructor,它要找的话,依然是通过原型链往上找,找的还是Parent5.prototype上去。
console.log(Child5.prototype.constructor) //构造函数Parent5
此时在Child5的原型对象写一个自己的constructor
function Parent5 () { this.name = '许风', this.age = 20, this.play = [4,5,6] } function Child5 () { Parent5.call(this) this.address = '江夏区' } Child5.prototype = Object.create(Parent5.prototype) Child5.prototype.constructor = Child5 var s9 = new Child5() var s10 = new Parent5() console.log(s9.constructor) //指向构造函数Child5 console.log(s10.constructor) //指向构造函数Parent5
如果我们不通过Object.create()方法,在Child4.prototype = Parent4.prototype 上写一个constructor,这是不行的,因为两个的原型对象引用的是同一个,一改的话,都改了。父类子类的都改了。又没办法区分父类的。