原型链继承
缺点:
- 引用类型的属性被所有实例共享。
 - 在创建 Child 的实例时,不能向 Parent 传参。
 
function Parent () {this.names = ['kevin', 'daisy'];}Parent.prototype.getName = function () {console.log(this.names);}function Child () {}Child.prototype = new Parent();var child1 = new Child();var child2 = new Child();console.log(child1.getName()) // ['kevin', 'daisy']child1.names.push('yoyo')console.log(child1.getName()) // ['kevin', 'daisy', 'yoyo']console.log(child2.getName()) // ['kevin', 'daisy', 'yoyo']
借用构造函数(经典继承)
优点:
- 避免了引用类型的属性被所有实例共享。
 - 可以在 Child 中向 Parent 传参。
 
function Parent () {this.names = ['kevin', 'daisy'];}function Child () {Parent.call(this);}var child1 = new Child();child1.names.push('yayu');console.log(child1.names); // ["kevin", "daisy", "yayu"]var child2 = new Child();console.log(child2.names); // ["kevin", "daisy"]
组合继承
原型链继承和经典继承的结合。
function Parent (name) {this.name = name;this.colors = ['red', 'blue', 'green'];}Parent.prototype.getName = function () {console.log(this.name)}function Child (name, age) {Parent.call(this, name);this.age = age;}Child.prototype = new Parent();Child.prototype.constructor = Child;var child1 = new Child('kevin', '18');child1.colors.push('black');console.log(child1.name); // kevinconsole.log(child1.age); // 18console.log(child1.colors); // ["red", "blue", "green", "black"]var child2 = new Child('daisy', '20');console.log(child2.name); // daisyconsole.log(child2.age); // 20console.log(child2.colors); // ["red", "blue", "green"]
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来增强对象,最后返回对象。
function createObj (o) {var clone = Object.create(o);clone.sayName = function () {console.log('hi');}return clone;}
寄生组合式继承(重点)
用一个全新的例子解释
function Human(options) {this.name = options.namethis.肤色 = options.肤色}Human.prototype.eat = function() {}Human.prototype.drink = function() {}Human.prototype.poo = function() {}function Soldier(options) {Human.call(this, options)this.ID = options.IDthis.生命值 = 42}// 目的:让 Solider.prototype 可以访问 Human.prototype// 写法一:使用 __proto__(不行)// 原因:在生产环境里面不能使用 __proto__Soldier.prototype.__proto__ = Human.prototype// 写法二:借助 new 的特性(不行)// 原因:造成实例和原型对象都有相同的属性Soldier.prototype = new Human({ name: 'xxx', 肤色: 'yellow' })// 写法三:借助中转函数(行)// 原因:即利用了 new 的特性,也避免的重复属性。function fakeHuman(){}fakeHuman.prototype = Human.prototypeSoldier.prototype = new fakeHuman()// 等价于Soldier.prototype = Object.create(Human.prototype)Soldier.prototype.兵种 = '美国大兵'Soldier.prototype.攻击力 = 5Soldier.prototype.行走 = function() {}Soldier.prototype.奔跑 = function() {}Soldier.prototype.死亡 = function() {}Soldier.prototype.攻击 = function() {}Soldier.prototype.防御 = function() {}var s = new Soldier({ name: 'xxiang', 肤色: 'yellow', ID: 1 })
es6 class 的写法
class Human {constructor(options) {this.name = options.namethis.肤色 = options.肤色}// 不支持非函数eat() {}drink() {}poon() {}}class Soldier extends Human {constructor(options) {super(options)this.ID = options.IDthis.生命值 = 42this.兵种 = '美国大兵'this.攻击力 = 5}// 不支持非函数行走() {}奔跑() {}死亡() {}攻击() {}防御() {}}var s = new Soldier({ name: 'xxiang', 肤色: 'yellow', ID: 1 })
但是 class 写法也是有缺点的
- 共有属性不支持非函数。
 - typeof className 竟然是个函数,但是这个函数只能用 new 来调用不能执行(只能当作特殊函数)。
 

参考:
[1] JS 的 new 到底是干什么的?
[2] JavaScript深入之继承的多种方式和优缺点
