常见的实现继承的方式有以下几种:
1.原型链式继承
2.构造函数式继承
3.组合式继承
4.原型式继承
5.寄生式继承
6.寄生组合式继承
下面来详细讲些以下每种继承的实现方式,及优缺点
先声明一个叫Parent的构造函数 (引:JavaScript中的构造函数是一种特殊的函数,它用于创建新的对象实例)
function Parent() { this.name = 'parent1'; this.play = [1, 2, 3]; this.sayHei = function(){ console.log("Hei~我是:" + this.name) } } Parent.prototype.getFun = function(){ console.log("你找到了这个方法!输出为:"+this.play) }
一:原型链式继承
实现方案:
function ChildOne() { this.type = 'childOne';
}
// 用.prototype 实现原型链式继承
ChildOne.prototype = new Parent();
var c1 = new ChildOne()
console.log(c1.name) // 'parent1'
console.log(c1.play) // [1, 2, 3]
console.log(c1.sayHei) // 'Hei~我是:parent1'
console.log(c1.getFun) // '你找到了这个方法!输出为:1,2,3'
缺点:
1.原型中包含的引用值会在所有实例间共享 这是因为 在使用原型实现继承时,原型实际上变成了另一个类型的实例
var c2 = new ChildOne() console.log(c2.play) // [1,2,3] c2.play.push(4) console.log(c2.play) // [1,2,3,4] console.log(c1.play) // [1,2,3,4]
console.log(ChildOne.prototype.play) // [1,2,3,4]
// 由上可见,ChildOne原型上的play属性,都被改了
2.子类型在实例化时不能给父类型的构造函数传参
二:构造函数式继承
实现方案
function ChildTwo (){ // 借助call 来调用Parent函数 Parent.call(this) this.type = 'child' } var t1 = new ChildTwo() var t2 = new ChildTwo() console.log(t1.name) // 'parent1' console.log(t1.play) // [1, 2, 3] console.log(t1.sayHei) // 'Hei~我是:parent1' t2.play.push(4) console.log(t2.play) // [1,2,3,4] console.log(t1.play) // [1,2,3]
缺点:
只能继承父类的实例属性和方法,不能访问父类原型上定义的方法
console.log(t1.getFun) // Uncaught TypeError: t1.getFun is not a function console.log(t2.getFun) // Uncaught TypeError: t2.getFun is not a function
三:组合式继承 (把原型链式继承和构造函数式继承的优点结合。基本的思路是:使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。)
实现方案:
function ChildThree() {
// 继承属性 Parent.call(this) }
// 继承方法 ChildThree.prototype = new Parent()
var s1 = new ChildTwo()
var s2 = new ChildTwo()
console.log(s1.name) // 'parent1'
console.log(s1.play) // [1, 2, 3]
console.log(s1.sayHei) // 'Hei~我是:parent1'
console.log(s1.getFun) // '你找到了这个方法!输出为:1,2,3'
s2.play.push(4)
console.log(s2.play) // [1,2,3,4]
console.log(s1.play) // [1,2,3]
完美的解决了上面的问题,唯一的缺点就是 Parent执行了两次【Parent.call(this) 和 new Parent()】,造成了多构造一次的性能开销