前记
在维基百科中继承的含义是使子类别具有父类别的属性和方法,或者说子类构造的对象直接拥有父类对象的属性。
但是在JavaScript中并没有”子类”和”父类”的概念,也没有”类”(class)和”实例”(instance)的区分,JavaScript的继承是靠prototype(原型链)来实现的。
prototype继承实现
我曾写过一篇名为原型和原型链的文章,那时候说明了原型和原型链是什么,那继承只要在它的原型链中添加公有属性就可以了
假如我们写一个构造函数Human
function Human(name){
this.name = name//构造函数可以生成新对象,this就是这个新对象
}
然后在Human的原型链上添加函数run
Human.prototype.run = function (){
console.log(this.name + '在跑')
return undefined
}
这样当我们用Human构造一个对象时候
let a = new Human('lgm')
//Human {name: "lgm"}
// name: "lgm"/
// __proto__:
// run: ƒ ()
// constructor: ƒ Human(name)
// __proto__: Object
现在我们再写一个构造函数Man
function Man(name){
this.name = name//this新生成的对象
this.gender: '男'//this新生成的对象
}
然后在Man的原型链上添加函数fight
Man.prototype.fight = function (){
console.log(this.name + '会打架')
}
这样用Man构造一个对象就是
let b = new Man('lgm')
//Man {name: "lgm"}
// name: "lgm"/
// gender: '男'
// __proto__:
// fight: ƒ ()
// constructor: ƒ Man(name)
// __proto__: Object
当我们想要Man继承Human的属性的时候,就可以知道,只要
Man.prototype.__proto__ = Human.prototype
修改Man
function Man(name){
Human.call(this, name)
this.gender: '男'//this新生成的对象
}
就可以完成继承了,让我们来试一下
再次构造一个Man对象
let lgm = new Man('lgm')
//Man {name: "lgm", gender: "男"}
// gender: "男"
// name: "lgm"/
// __proto__: Human
// fight: ƒ ()
// constructor: ƒ Man(name)
// __proto__:
// run: ƒ ()
// constructor: ƒ Human(name)
// __proto__: Object
由此我们可以看到,Man继承了Human中的run()函数,但是有一个问题
直接操作__proto__是不属于ECMA规范的,在IE中直接操作并不可行,我们需要换一种写法
var f = function(){}
f.prototype = Human.prototype
Man.prototype = new f()
这样是使用了new的特性,让我们来试一下,然后再次构造一个Man对象
let lgm = new Man('lgm')
//Man {name: "lgm", gender: "男"}
// gender: "男"
// name: "lgm"/
// __proto__: Human
// fight: ƒ ()
// __proto__:
// run: ƒ ()
// constructor: ƒ Human(name)
// __proto__: Object
可以看到,效果是一样的
class语法糖
在ES6语法中,新增了一个叫做class的语法,专门用来实现继承,上面代码通过class改写如下
class Human {
constructor(name) {
this.name = name
}
run() {
console.log("我叫" + this.name + ",我在跑")
return undefined
}
}
class Man extends Human {//Man.prototype.__proto__ = Human.prototype
constructor(name) {
super(name)//Human.call(this, name)
this.gender = '男'
}
fight() {
console.log(this.name + '会打架')
}
}
两种方法的优劣
prototype继承写法麻烦,但是简单易懂,而class继承的写法虽然简单,但难以理解
并且,原型链继承添加继承属性的话会会非常简单,只需
Human.prototype.headNumber = '1'
而class继承如果需要添加继承属性会非常麻烦