概述
在面试或者浏览技术网站时,常常会遇到这样一个问题:ES6 和 ES5 的继承机制有哪些不同 ? 本文章带你深刻理解 ES6 和 ES5 继承的原理。
原型链
聊 js 的继承,必定是绕不开原型链,咱们先来温习一下 js 的原型链:
('').__proto__ === String.prototype // true
({}).__proto__ === Object.prototype // true
Object.prototype.__prototype === null // true
function Person () {}
Person.__proto__ === Function.prototype // true
(new Person).__proto__ === Person.prototype // true
(class {}).__proto__ === Function.prototype // true
Function instanceof Function // true
Object instanceof Function // true
从这里咱们能够看出,字符串、对象、函数等内置一些办法是因为原型链 __proto__
指向了相应的构造函数的原型 prototype
,从而继承了原型上的办法。在 Javascript 中也是通过原型链来实现继承,先说 es6 的 class
ES 6 class
class Person {constructor (name) {this.name = name}
walk () {}
// 动态属性
static staticFun = function () {console.log('staticFun')
}
}
class Student extends Person {constructor (name) {super(name)
this.school = 'NO.3 middle school'
}
learn () {}
}
const ming = new Student()
ming.learn()
ming.walk()
Student.staticFun()
例子中,ming 有 learn 办法是因为,ming 的 __proto__
指向 Student 类的原型
ming.__proto__ === Student.prototype // true
ming 有 walk 办法是因为 Student 的原型能通过 __proto__
连贯到父类的原型,能找到父类的 walk 办法
Student.prototype.__proto__ === Person.prototype
ming.__proto__.__proto__ === Person.prototype
然而,为什么 Student 为什么继承了 staticFun 办法?Student 通过本人原型的 __proto__
去寻找必定找不到,因为父类的原型上基本就没有这个办法,这个是一个动态的办法。
其实不仅 Student.prototype 有 __proto__
Student 有本人的 __proto__
,而他的原型链指向父类的构造函数:
Student.__proto__ === Person // true
这就解释了,Student 类上没有动态函数 staticFun,进而通过原型链向上寻找,发现 Person 类有静态方法 staticFun。
ES5 的继承
JavaScript 高级程序设计(第三版)提到寄生组合式继承:
function SuperType (name) {this.name = name}
SuperType.staticFun = function () { console.log('staticFun') }
SuperType.prototype.sayName = function () { return this.name}
function SubType (name, age) {SuperType.call(this, name)
this.age = age
}
SubType.prototype = Object.create(SuperType.prototype)
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function () { return this.age}
const demo = new SubType('ming', 22)
demo.sayAge() // ming
demo.sayName() // 22
SubType.staticFun() // throw TypeError
demo 的 sayAge、sayName 办法失常调用能够预期到:
demo.__proto__ === SubType.prototype // true
SubType.prototype.__proto__ === SuperType.prototype // true
那么,为什么 SubType 没有 staticFun 办法呢?必定是 SubType.__proto__
没有指向 SuperType,那指向哪里了呢?
SubType.__proto__ === Function.prototype // true
SuperType.__proto__ === Function.prototype // true
还记得文章开始,原型链局部的温习吗?
function Person () {}
Person.__proto__ === Function.prototype // true
这里就能够发现,传统的寄生组合式继承形式与 ES6 class 的一个区别,无奈主动继承父级的静态方法。
要想实现静态方法的继承也很简略,只须要批改 SubType 的原型链,指向到 SuperType 即可:
SubType.__proto__ = Super // 或者 Object.setPrototypeOf(SubType, SuperType)
SubType.staticFun() // staticFun
总结
两者继承机制不同
ES5 中,子类对于父类构造函数的继承时,子类的 this 曾经存在,通过 SuperType.call(this, ...args)
的形式来批改子类的 this
而 ES6 的子类必须要调用 super(...args)
来生成 this
两者构造函数的原型链指向不同
ES5 的子类和父类的构造函数函数的原型链都指向 Function.prototype
而 ES6 的子类的构造函数的原型链指向父类的构造函数
附录
贴一张 babel 编译 ES6 class 为 ES5 的图片: