原型和闭包是JS的两个难点,最近碰到了原型继承的概念,正好在这里总结一下。闭包
既然要实现继承,就必定要有一个父类。函数
// 定义一个父类 function father(name) { //属性 this.name = name; } // 原型方法 father.prototype.getName = function () { return this.name; }
基本思想就是利用原型让一个引用类型继承另外一个引用类型的属性和方法。性能
回顾一下原型、实例和构造函数的关系。this
每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象内部的指针。
// 子类 function son(age) { // 属性 this.age = age; }; son.prototype = new father('jason'); son.prototype.getAge = function () { return this.age; } let firstchild = new son('19'); console.log(firstchild.getAge()) // 19
这里须要注意几点的是:spa
原型链的最顶端是Object,全部引用类型默认都是继承于Object的,因此默认也是有toString等方法的。prototype
console.log(firstchild instanceof Object) //true console.log(firstchild instanceof son) //true console.log(firstchild instanceof father) //true
第二个方法是,isPrototypeOf方法。
console.log(Object.prototype.isPrototypeOf(firstchild)) //true console.log(son.prototype.isPrototypeOf(firstchild)) //true console.log(father.prototype.isPrototypeOf(firstchild)) //true
子类型可能要重写父类型方法,或定义父类没有的方法。无论是啥,这个方法必定要写在替换原型语句的后面。
还有原型链继承的时候,不能使用对象字面量建立原型方法。设计
例如:指针
son.prototype = new father('jason'); son.prototype = { getAge: function() { return this.age } }
这样会致使建立一个新的Object实例,而非原来的father。code
第一,引用类型的原型属性会被全部实例共享。对象
function father(name) { this.name = name; this.colors = ['blue', 'red', 'white']; } let firstchild = new son('19'); let secondchild = new son('20'); firstchild.colors.push("black"); console.log(firstchild.colors) // ["blue", "red", "white", "black"] console.log(secondchild.colors) // ["blue", "red", "white", "black"]
第二,不能像父类型构造函数传参数,书里准确说法是,没有办法在不影响全部实例的状况下,给父类构造函数传递参数。
优势:
缺点:
在子类型的构造函数中调用父类的构造函数,使用父类的构造函数来加强子类实例,等因而复制父类的实例属性给子类(不用原型)
function son(age) { father.call(this); this.age = age; }; son.prototype = new father('jason'); son.prototype.getAge = function () { return this.age; } let firstchild = new son('19'); let secondchild = new son('20'); firstchild.colors.push("black"); console.log(firstchild.colors); // ["blue", "red", "white", "black"] console.log(secondchild.colors); // ["blue", "red", "white"]
优势:
缺点:
也就是将原型链继承和构造函数继承融合,原型链实现对原型属性和方法的继承,构造函数实现对实例属性的继承。
这样既保证了原型上函数的复用,也保证了每一个实例有本身的属性。
function son(name, age) { father.call(this, name); this.age = age; }; son.prototype = new father(); son.prototype.getAge = function () { return this.age; } let firstchild = new son('jason', '19'); let secondchild = new son('jason junior', '18'); firstchild.colors.push("black"); console.log(firstchild.colors); // ["blue", "red", "white", "black"] console.log(secondchild.colors); //["blue", "red", "white"] console.log(firstchild.getName()); // jason console.log(secondchild.getName()); // jason junior console.log(firstchild.getAge()); //19 console.log(secondchild.getAge()); //18
特色:
缺点:
为父类实例添加新特性,做为子类实例返回
let p = { name: 'jason', colors: ['white', 'black', 'red'] } function object (o) { function F() {}; F.prototype = o; return new F(); } let firstchild = object(p) let secondchild = object(p) firstchild.name = 'jason1' firstchild.colors.push('blue') secondchild.name = 'jason2' secondchild.colors.push('green') console.log(p.colors) // ["white", "black", "red", "blue", "green"]
ECMAScript 5新增Object.create()方法规范原型式继承。两个参数,一个参数是新对象原型的对象,一个参数是对象定义额外属性的对象,第二个可忽略,就等于上述object函数了
创造一个用于封装继承过程的函数,该函数内部以某种方式加强对象。
function create(o) { let clone = object(o); o.sayHi = function () { console.log('Hi') } return o; }
组合继承虽然好用,可是也有缺陷,就是会调用两次构造函数,一次在建立时候,一次在内部,那个call方法。
所谓寄生组合继承,即经过借用构造函数方式,继承属性,经过原型链形式继承方法。
沿用寄生方式:
function inheritPrototype (sub, sup) { let prototype = object(sup.prototype); prototype.constructor = sub; sub.prototype = prototype; }
function father(name) { this.name = name; this.colors = ['blue', 'red', 'white']; } father.prototype.getName = function () { return this.name; } function son(name, age) { father.call(this, name); this.age = age; }; function object (o) { function F() {}; F.prototype = o; return new F(); } function inheritPrototype (sub, super) { let prototype = object(super.prototype); prototype.constructor = sub; sub.prototype = prototype; } inheritPrototype(son, father); son.prototype.getAge = function () { return this.age; }
优势:
缺点:
参考 <<JavaScript高级程序设计>>总结