ECMAScript只支持实现继承,主要依靠原型链来实现。与实现继承对应的是接口继承,因为script中函数没有签名,因此没法实现接口继承。浏览器
基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法。
构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个纸箱原型对象的内部指针。
基本用法:函数
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
关系图:
但实际上,SuperType也有本身的原型,就是Object,这个原型是默认的。
全部函数的默认原型都是Object的实例,所以默认原型都会包含一个内部指针,指向Object.prototype。
因此完整的关系图应该是
使用原型可以作到继承,但实际中并不单独使用原型链来实现继承,缘由以下:
一、对于不须要‘父亲’的私有属性的继承:咱们知道原型来建立对象,使得全部的实例都拥有这些共享的属性和方法,咱们在使用原型链来继承最主要的是SubType的原型变为SuperType的实例对象,那么原本是Super实例私有的属性property,且处于SubType的原型中成为SubType实例的共享属性。
二、对于须要‘父亲‘私有属性的继承:同一,咱们知道会继承父亲的私有属性,但咱们没法经过传入参数到’父亲‘的构造函数来实现属性特有值的目的。
鉴于以上咱们开始使用第二种继承方式。this
基本思想:在子类型构造函数的内部调用超类型构造函数spa
function SuperType() { this.colors = ['red', 'yellow', 'black']; } SuperType.prototype.getColor = function () { return this.colors; } function SubType() { // 继承了SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push('pink'); // ['red', 'yellow', 'black','pink'] console.log(instance1.colors); var instance2 = new SubType(); console.log(instance2.colors); // ['red', 'yellow', 'black'] console.log(instance2 instanceof SuperType); // false console.log(instance2.getColor()); // instance2.getColor is not a function
此方法是在子类型中调用了超(父)类型的构造函数,使构造函数中的属性初始化了。
继承的是超类型中构造函数中的属性,如上继承了colors属性,但没有继承SuperType原型中的getcolor方法。
使用此方法,咱们还能够传递参数对属性进行初始化prototype
function SuperType(age) { this.age=age; } function SubType() { // 继承了SuperType SuperType.call(this,18); } var instance1 = new SubType(); console.log(instance1.age); // 18
若是须要确保SuperType构造函数不会重写子类型的属性,能够在调用超类型构造函数后,再添加应该在子类型定义的属性。
缺点:
一、超类型的原型不可见
二、全部属性方法都必须写在构造函数中,全部类型都只能使用构造函数模式建立指针
将原型链和借用构造函数的技术组合到一块。
思想:使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承。code
function SuperType(age) { this.age = age; } SuperType.prototype.getAge = function () { return this.age; } function SubType(age) { // 继承了SuperType SuperType.call(this, age); } SubType.prototype = new SuperType(20); var instance1 = new SubType(18); console.log(instance1.age); // 18 console.log(instance1.getAge()); // 18 console.log(instance1.__proto__.age); // 20 var instance2 = new SubType(17); instance2.__proto__.age=55; console.log(instance1.__proto__.age); // 55 console.log(instance2.__proto__.age); // 55
咱们能够看到,实际上instance1和instance2的原型上仍然存在属于SuperType的实例属性的属性。只是instance1和instance2有了各自的age属性,不会再往原型上找。
instanceof和isPrototypeOf()也可以用于识别基于组合继承建立的对象。
组合继承避免了原型链和借用构造函数的缺陷并融合了二者的有点,成为js中最经常使用的继承模式。对象
思想:借助原型能够基于已有的对象建立新的对象,同时还没必要所以建立自定义类型。blog
function object(o) { function F() { }; F.prototype = o; return new F(); } var person = { name: 'linda', friends: ['lily', 'shirley'] }; var antherPerson = object(person); antherPerson.friends.push('tom'); console.log(antherPerson.name); // linda console.log(antherPerson.friends); // ['lily', 'shirley', 'tom]
这个方法和原型方法原理同样,只不过把子类型的原型设置成超类型的实例对象包含在方法object中。
ECMAScript5中新增了object.create()方法来规范原型式继承(做用与上述object函数做用相似),第一个参数是想要继承的超类型的实例对象,第二个参数是子类型所具备的属性。继承
var person={ name:'lily', age:12 } var anotherPerson=Object.create(person,{name:{value:'linda'}}); console.log(anotherPerson.name); // 'linda'
第二个参数的写法必须如上的格式。
支持Object.create()方法的浏览器有ie9+,Firefox4.+、Safari5+、Opera12+和Chrome
思想:与寄生构造函数和工厂模式相似,即建立一个仅用于封装继承过程的函数,该函数在内部以某种方式来加强对象,最后再像真的是它作了全部工做同样返回对象。
代码:
function object(o){ function F(){} F.prototype=o; return new F(); } var person = { name: 'lily', age: 12 } function createAnotherPerson(original){ var clone=object(original); clone.sayHi=function(){ console.log('hi'); } return clone; } var anotherPerson =createAnotherPerson(person); anotherPerson.sayHi(); // 'hi' console.log(anotherPerson.name); // 'linda'
组合继承是js最经常使用的继承模式,能够结合不一样的模式的优势,但组合继承,每次都会调用两次超类型构造函数:一次是在建立子类型原型的时候,另外一次是在子类型构造函数内部。
上述形成的结果是子类型实例中有两组超类型的构造函数中定义的属性,一组在子类型的实例中,一组在子类型实例的原型中。寄生组合式继承能够解决上述缺点。
function Super(name) { this.name = name; } Super.prototype.sayName = function () { console.log(this.name); } function Sub(age) { Super.call(this, 'linda'); this.age = age; } Sub.prototype = new Super(); Sub.constructor = Sub; var type=new Sub(); type.sayName(); // 'linda' console.log(type.name); // 'linda' console.log(type.__proto__.name); // undefined
思想:借用构造函数来继承属性,借用原型链来继承方法。即继承超类型的原型,而后再将结果指定给子类型的原型。
封装一下即:
function Super(name) { this.name = name; } Super.prototype.sayName = function () { console.log(this.name); } function Sub(age) { Super.call(this, 'linda'); this.age = age; } function object(o) { function F() { } F.prototype = o; return new F(); } function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); // 建立对象 prototype.constructor = subType; // 加强对象 subType.prototype = prototype; // 指定对象 } inheritPrototype(Sub, Super); var type = new Sub(); type.sayName(); // 'linda' console.log(type.name); // 'linda' console.log(type.__proto__.name); // undefined
这个模式的优势体如今
一、只调用了一次Super构造函数,高效率
二、避免了在Sub.prototype上面建立没必要要的多余的属性
三、原型链保持不变
开发人员广泛认为寄生组合式继承是引用类型最理想的继承范式