-
Notifications
You must be signed in to change notification settings - Fork 8
Open
Labels
Description
ES6 Class的底层实现原理
ES6 中的类
Class,仅仅只是基于现有的原型继承的一种语法糖,我们一起来看一下Class的底层实现原理。
Class的底层实现要素
- 只能使用new操作符调用
Class; Class可定义实例属性方法和静态属性方法;- 子
Class的实例可继承父Class上的实例属性方法、子Class可继承父Class上的静态属性方法。
只能使用new操作符调用Class
实现思路:�使用instanceof操作符检测实例是否为指定类的实例。
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function')
}
}定义实例属性方法和静态属性方法
实现思路
- 在构造函数的原型上定义属性方法,即为实例属性方法;
- 在构造函数本身定义属性方法,即为静态属性方法。
function _createClass(Constructor, protoProps = [], staticProps = []) {
// 在构造函数的原型上定义实例属性方法
_defineProperties(Constructor.prototype, protoProps)
// 在构造函数本身定义静态属性方法
_defineProperties(Constructor, staticProps)
}
// 实现公用的批量给对象添加属性方法的方法
function _defineProperties(target, props) {
props.forEach(prop => {
Object.defineProperty(target, prop.key, prop)
})
}继承实例属性方法和静态属性方法
实现思路:借用原型链继承实现。
function _inherits(subClass, superClass) {
// 子类实例继承父类的实例属性方法
subClass.prototype = Object.create(superClass.prototype)
// 修正constructor属性
subClass.prototype.constructor = subClass
// 子类继承父类的静态属性方法
Object.setPrototypeOf(subClass, superClass)
}模拟编译
了解了Class的底层实现要素,我们就来将Class模拟编译为使用原型继承实现的代码。
源代码
class Person {
constructor(options) {
this.name = options.name
this.age = options.age
}
eat() {
return 'eating'
}
static isPerson(instance) {
return instance instanceof Person
}
}
class Student extends Person {
constructor(options) {
super(options)
this.grade = options.grade
}
study() {
return 'studying'
}
static isStudent(instance) {
return instance instanceof Student
}
}编译后代码
var Person = (function() {
function Person(options) {
// 确保使用new调用
_classCallCheck(this, Person)
this.name = options.name
this.age = options.age
}
_createClass(
Person,
// 实例属性方法
[{
key: 'eat',
value: function eat() {
return 'eating'
}
}],
// 静态属性方法
[{
key: 'isPerson',
value: function isPerson(instance) {
return instance instanceof Person
}
}]
)
return Person
})();
var Student = (function (_Person) {
// 继承父类实例属性方法和静态属性方法
_inherits(Student, _Person)
function Student(options) {
// 确保使用new调用
_classCallCheck(this, Student)
// 执行父类构造函数
_Person.call(this, options)
this.grade = options.grade
}
_createClass(Student,
// 实例属性方法
[{
key: 'study',
value: function study() {
return 'studying'
}
}],
// 静态属性方法
[{
key: 'isStudent',
value: function isStudent(instance) {
return instance instanceof Student
}
}]
)
return Student
})(Person);测试代码
const person = new Person({ name: 'Logan', age: 18 })
const student = new Student({ name: 'Logan', age: 18, grade: 9 })
expect(person.eat()).toBe('eating')
expect(student.eat()).toBe('eating') // 继承实例方法
expect(student.study()).toBe('studying')
expect(Student.isStudent(student)).toBe(true)
expect(Person.isPerson(person)).toBe(true)
expect(Student.isStudent(person)).toBe(false)
expect(Student.isPerson(student)).toBe(true) // 继承静态方法公有字段和私有字段(提案中)
公有(public)和私有(private)字段声明目前在JavaScript标准委员会TC39的 试验性功能 (第3阶段),下面进行模拟实现。
静态公有字段
使用
class ClassWithStaticField {
static staticField1 = 'static field' // 设定初始值
static staticField2 // 不设定初始值
}polyfill
function ClassWithStaticField() {}
// @babel/plugin-proposal-class-properties 中使用Object.defineProperty实现,此处简便起见直接赋值
ClassWithStaticField.staticField1 = 'static field' // 设定初始值
ClassWithStaticField.staticField2 = undefined // 不设定初始值公有实例字段
使用
class ClassWithInstanceField {
instanceField1 = 'instance field' // 设定初始值
instanceField2 // 不设定初始值
}polyfill
function ClassWithInstanceField() {
// @babel/plugin-proposal-class-properties 中使用Object.defineProperty实现,此处简便起见直接赋值
this.instanceField1 = 'instance field' // 设定初始值
this.instanceField2 = undefined // 不设定初始值
}静态私有字段
静态私有字段只能在静态方法内访问,且只能通过类的属性进行访问,不能通过this进行访问。
使用
class ClassWithPrivateStaticField {
static #PRIVATE_STATIC_FIELD
static publicStaticMethod() {
ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42
return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD
}
}polyfill
通过闭包实现静态私有字段
var ClassWithPrivateStaticField = (function() {
var _PRIVATE_STATIC_FIELD
function ClassWithPrivateStaticField() {}
ClassWithPrivateStaticField.publicStaticMethod = function() {
_PRIVATE_STATIC_FIELD = 42
return _PRIVATE_STATIC_FIELD
}
return ClassWithPrivateStaticField
})();私有实例字段
使用
class ClassWithPrivateField {
#privateField;
constructor() {
this.#privateField = 42;
console.log(this.$privateField)
}
}polyfill
通过WeakMap结合实例本身为key实现
var ClassWithPrivateField = (function() {
var _privateField = new WeakMap()
function ClassWithPrivateField() {
_privateField.set(this, undefined)
_privateField.set(this, 42)
console.log(_privateField.get(this))
}
})();