简介
JS的继承对于Java的继承其实不太是一回事,JS的继承的原理就是用原型链。
假设
假设我们有两个类: Human 和 Man。Human有name
属性和一个run
方法;Man有name
和gender
属性,还有一个fight
方法。
function Human(name) {
this.name = name
}
Human.prototype.run = function() {}
function Man(name) {
this.name = name
this.gender = '男'
}
Man.prototype.fight = function() {}
现在开始我们的继承实现。
ES5
属性继承
首先是属性的继承,其实对于Man
来说,this.name = name
是废话一话,因为如果继承了Human
,Human
里面就有这句话,只不过两者的this
不一样而已。但是我们可以通过call()
来指定this
就可以实现代码的复用。
function Human(name) {
this.name = name
}
Human.prototype.run = function() {}
function Man(name) {
// 调用Human构造函数
// 实现 this.name = name
Human.call(this, name)
this.gender = '男'
}
Man.prototype.fight = function() {}
方法继承
除了属性的继承,我们还想要方法的继承,一般来说方法的继承是指要继承Human
里prototype
的方法,而不是自有方法。简单来说就是new Man()
出来的对象可以用Human.prototype
里的方法。
根据原理链的查找,我们可以得出下面的的代码
function Human(name) {
this.name = name
}
Human.prototype.run = function() {}
function Man(name) {
Human.call(this, name)
this.gender = '男'
}
// 连接了Human.prototype,相当于继承了里面的方法
Man.prototype.__proto__ = Human.prototype
Man.prototype.fight = function() {}
这样new Man()
出来的对象就可以用run()
方法了。
IE
但是像这种直接操作__proto__
在IE里是不被允许的,我们得另找方法。参考如下代码。
var arr = new Array()
arr.__proto__ === Array.prototype // true
根据上面的等式,我们可以推断出
Man.prototype = new Human()
这样使用代入法,不就是我们想要的吗?
Man.prototype.__proto__ === Human.prototype // true
但是这里有个问题,直接new Human()
会执行里面Human
函数的代码,这样不好,所以我们可以用一个临时变量来做。
var f = function(){}
f.prototype = Human.prototype
Man.prototype = new f()
- 我先把
prototype
放到临时函数的prototype
里 - 再去用这个临时函数构造一个对象,这样不就可以不执行
Human
的代码了么,搞定!
终极爆炸ES5继承代码
function Human(name) {
this.name = name
}
Human.prototype.run = function() {}
function Man(name) {
// 属性继承
Human.call(this, name)
this.gender = '男'
}
var f = function(){}
// 继承Human 方法
f.prototype = Human.prototype
Man.prototype = new f()
Man.prototype.fight = function() {}
ES6
有了ES6就爽很多了,ES6提供了一个语法糖
class Human {
constructor(name) {
this.name = name
}
run() { }
}
// extends 相当于方法的继承
// 替换了上面的3行代码
class Man extends Human {
constructor(name) {
// super 相当于属性的继承
// 替换了 Human.call(this, name)
super(name)
this.gender = '男'
}
fight() { }
}
但是ES6也不是万能的,如果Human.prototype
中想写入一个非函数变量如a: 1
ES6就不行了。ES5可以直接操作Human.prototype.a = 1
就可以了,但是ES6语法只能写函数。
不过我们有一个变通的方法。
function Human(name) {
this.name = name
}
Human.prototype.run = function() {}
function Man(name) {
Human.call(this, name)
this.gender = '男'
}
var f = function(){}
f.prototype = Human.prototype
Man.prototype = new f()
Man.prototype.fight = function() {}
class Human {
constructor(name) {
this.name = name
}
// 这里的函数会被继承,所以可以获取1值
getA() {
return 1
}
run() { }
}
// extends 相当于方法的继承
// 替换了上面的3行代码
class Man extends Human {
constructor(name) {
// super 相当于属性的继承
// 替换了 Human.call(this, name)
super(name)
this.gender = '男'
}
fight() { }
}
总结
- 其实JS的继承就是
- 属性继承:绑定this,调用父类构造器
- 方法继承:
子.__proto__ = 父.prototype
,使用原理链完成串连
- ES5方法麻烦,但是能够让我们看清JS继承的本质
- ES5方法简单,但是由于不能直接操作
prototype
,所以没有ES5方法灵活
网友评论