JavaScript_几种继承方式(2017-07-04)
原型链继承
核心: 将父类的实例作为子类的原型
//父类 function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property }; //子类 function SubType() { this.subproperty = false; } //拿父类实例来充当子类原型对象 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty }; var instance = new SubType(); console.log(instance.getSuperValue()); //true
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 要想为子类新增属性和方法,必须要在
new
语句之后执行,不能放到构造器中 - 无法实现多继承
- 来自原型对象的引用属性是所有实例共享的
- 创建子类实例时,无法向父类构造函数传参
借用构造继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
//父类 function SuperType() { this.colors = ["red", "blue"] } //子类 function SubType() { SuperType.call(this);//通过apply()和call()方法在新创建的对象上执行构造函数 } var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); //["red", "blue", "black"] var instance2 = new SubType(); console.log(instance2.colors); //["red", "blue"]
传递参数
//父类 function SuperType(name) { this.name = name; } //子类 function SubType() { SuperType.call(this, "mnixu");//通过apply()和call()方法在新创建的对象上执行构造函数 this.age = 20; } var instance1 = new SubType(); console.log(instance1.name); //"mnixu" console.log(instance1.age); //20
特点:
- 解决了原型链继承中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
组合继承(最常用的继承模式)
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
//通过借用构造函数实现继承父类构造函数的属性(构造函数通常用this.属性,对实例对象来说私有)。 //使用原型链实现继承父类原型的属性和方法(原型的属性和方法,对实例对象来说共享)。 //父类 function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } SuperType.prototype.sayName = function() { console.log(this.name); } //子类 function SubType(name, age) { SuperType.call(this, name);//通过apply()和call()方法在新创建的对象上执行构造函数 this.age = age; } SubType.prototype = new SuperType();//拿父类实例来充当子类原型对象 SubType.prototype.constructor = SubType;//重新生成constructor属性指向子类,模型原型对象 SubType.prototype.sayAge = function() { console.log(this.age); }; var instance1 = new SubType("mnixu", 20); instance1.colors.push("black"); console.log(instance1.colors); //["red", "blue", "black"] instance1.sayAge(); // 20 instance1.sayName(); // "mnixu" var instance2 = new SubType("zwj", 28); console.log(instance2.colors); //["red", "blue"] instance2.sayAge(); //28 instance2.sayName(); //"zwj"
特点:
- 弥补了借用构造函数继承方式的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
缺点:
- 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
4.原型式继承
借助原型并基于已有的对象创建新对象,同时还不用创建自定义类型
//生成对象的函数 function object(o) { //部分浏览器支持Object.create()方法 function F() {}; F.prototype = o; return new F(); } //父类 var Person = { name: "mnixu", color: ["blue", "black"] } //子对象1 var anotherPerson = object(Person); anotherPerson.name = "Greg"; anotherPerson.color.push("red") //子对象2 var yetAnotherPerson = object(Person); yetAnotherPerson.name = "zwj"; yetAnotherPerson.color.push("white") console.log(anotherPerson.color); //["blue", "black", "red", "white"] console.log(yetAnotherPerson.color); //["blue", "black", "red", "white"]
原型式继承首先在obj()
函数内部创建一个临时性的构造函数 ,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。
5.寄生式继承
这种继承方式是把原型式+工厂模式结合起来,目的是为了封装创建的过程。
//生成对象的函数 function object(o) { //部分浏览器支持Object.create()方法 function F() {}; F.prototype = o; return new F(); } //生成并增强对象的函数 function createAnother(original){ var clone = object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //增强这个对象 console.log("hi") }; return clone; //返回这个对象 } //父类 var Person = { name: "mnixu", color: ["blue", "black"] } //子对象 var anotherPerson = createAnother(Person); anotherPerson.sayHi();
略
6.寄生组合式继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
//生成对象的函数 function object(o) {//部分浏览器支持Object.create()方法 function F() {}; F.prototype = o; return new F(); } //继承原型函数 function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 } //父类 function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } SuperType.prototype.sayName = function() { console.log(this.name); } //子类 function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { console.log(this.age); };
特点:
-
这个例子的高效率体现在只调用了一次SuperType构造函数,避免了在SubType.prototype上面创建不必要的属性,并且,原型链还能保持不变。堪称完美
缺点:
- 实现较为复杂
copy: