原型链继承
缺点:
- 引用类型的属性被所有实例共享。
- 在创建 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); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
var child2 = new Child('daisy', '20');
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.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.name
this.肤色 = options.肤色
}
Human.prototype.eat = function() {}
Human.prototype.drink = function() {}
Human.prototype.poo = function() {}
function Soldier(options) {
Human.call(this, options)
this.ID = options.ID
this.生命值 = 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.prototype
Soldier.prototype = new fakeHuman()
// 等价于
Soldier.prototype = Object.create(Human.prototype)
Soldier.prototype.兵种 = '美国大兵'
Soldier.prototype.攻击力 = 5
Soldier.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.name
this.肤色 = options.肤色
}
// 不支持非函数
eat() {}
drink() {}
poon() {}
}
class Soldier extends Human {
constructor(options) {
super(options)
this.ID = options.ID
this.生命值 = 42
this.兵种 = '美国大兵'
this.攻击力 = 5
}
// 不支持非函数
行走() {}
奔跑() {}
死亡() {}
攻击() {}
防御() {}
}
var s = new Soldier({ name: 'xxiang', 肤色: 'yellow', ID: 1 })
但是 class 写法也是有缺点的
- 共有属性不支持非函数。
- typeof className 竟然是个函数,但是这个函数只能用 new 来调用不能执行(只能当作特殊函数)。
参考:
[1] JS 的 new 到底是干什么的?
[2] JavaScript深入之继承的多种方式和优缺点