ES5 的寄生组合式继承
function parent (age) {this.age = age}
parent.prototype.say = function () {console.log(this.age)
}
function sub (age, value) {parent.call(this, age)
this.value = value
}
sub.prototype = Object.create(parent.prototype, {
constructor: {
value: sub,
enumerable: false,
writable: true,
configurable: true
}
})
ES6 的 Class
对于 Class 的语法举荐看这里:es6.ruanyifeng.com/#docs/class
ES6 的 class 能够看作只是一个语法糖,它的绝大部分性能,ES5 都能够做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。然而它们还是有区别的。
区别:
- 类必须应用 new 调用,否则会报错。ES 的构造函数是能够当成一般函数应用的
- 类的外部所有定义的办法,都是不可枚举的。(包含外部定义的静态方法)
- 类的静态方法也能够被子类继承
-
能够继承原生构造函数
- ES5 是先新建子类的实例对象 this,再将父类的属性增加到子类上,因为父类的外部属性无奈获取,导致无奈继承原生的构造函数。
- ES6 容许继承原生构造函数定义子类,因为 ES6 是先新建父类的实例对象 this,而后再用子类的构造函数润饰 this,使得父类的所有行为都能够继承
应用 ES5 模仿实现 ES6 的 class
依据下面的区别,咱们一步步的看。
1. new 操作符查看函数
解决问题:
- 类必须应用 new 调用,否则会报错。ES 的构造函数是能够当成一般函数应用的
function _checkType (obj, constructor) {if (!(obj instanceof constructor)) {throw new TypeError('Cannot call a class as a function')
}
}
2. 外部办法不可枚举
解决问题:
- 类的外部所有定义的办法,都是不可枚举的。(包含外部定义的静态方法)
// 批改构造函数描述符
function defineProperties (target, descriptors) {for (let descriptor of descriptors) {
descriptor.enumerable = descriptor.enumerable || false
descriptor.configurable = true
if ('value' in descriptor) {descriptor.writable = true}
Object.defineProperty(target, descriptor.key, descriptor)
}
}
// 结构 class
// constructor 示意类对应的 constructor 对象
// protoDesc 示意 class 外部定义的办法
// staticDesc 示意 class 外部定义的静态方法
function _createClass (constructor, protoDesc, staticDesc) {protoDesc && defineProperties(constructor.prototype, protoDesc)
staticDesc && defineProperties(constructor, staticDesc)
return constructor
}
3. 真正的创立 class
const Foo = function () {function Foo(name) {_checkType(this, Foo) // 先查看是不是 new 调用的
this.name = name
}
_createClass (Foo, [ // 示意在 class 外部定义的办法
{
key: 'say',
value: function () {console.log(this.name)
}
}
], [ // 示意在 class 外部定义的静态方法
{
key: 'say',
value: function () {console.log('static say')
console.log(this.name)
}
}
])
return Foo
}()
到这里 class 实现实现,验证一下。
- 先间接调用一下 Foo(),后果为:
- 应用 new 操作符,生成一个对象
const foo = new Foo('aaa')
- 打印一下在原型链上定义的办法
可见 say 办法是不可枚举的。
- 打印一下静态方法
可见静态方法 say 是不可枚举的。
4. 实现原型链继承和静态方法继承,并思考到能够继承 null 的状况
解决问题:
- 类的静态方法也能够被子类继承
function _inherits(subClass, superClass) {if (typeof superClass !== 'function' && superClass !== null) {throw new TypeError('Super expression must either be null or a function, not' + typeof superClass)
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
})
if (superClass) {Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass
}
}
5. 应用父类的实例对象 this
解决的问题:
-
能够继承原生构造函数
- ES5 是先新建子类的实例对象 this,再将父类的属性增加到子类上,因为父类的外部属性无奈获取,导致无奈继承原生的构造函数。
- ES6 容许继承原生构造函数定义子类,因为 ES6 是先新建父类的实例对象 this,而后再用子类的构造函数润饰 this,使得父类的所有行为都能够继承
// 返回父类的 this;若为 null,则返回本身
function _possibleConstructorReturn(self, call) {if (!self) {throw new ReferenceError("this hasn't been initialised - super() hasn't been called")
}
return call && (typeof call === 'object' || typeof call === 'function') ? call : self
}
6. 创立子类 class
const Child = function (_Parent) {_inherits(Child, _Parent) // 继承父类原型上的属性及静态方法的继承
function Child(name, age) {_checkType(this, Child)
// 先应用父类实例对象 this,再返回
const _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name))
_this.age = age
return _this
}
return Child
}(Foo)
子类 class 实现实现。验证一下。
- 打印一下 Child.say()
Child 并没有在本身定义静态方法,然而它的父类有定义。继承胜利。
- 结构一个继承原生构造函数的子类
const Child = function (_Parent) {_inherits(Child, _Parent)
function Child(name, age) {_checkType(this, Child)
const _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name))
_this.age = age
return _this
}
return Child
}(Array)
const c = new Child('bbb', 12)
继承胜利。
相干文章
- ES2020 中 Javascript 10 个你应该晓得的新性能
- 重温 ES6 外围概念和根本用法
- JavaScript 对象:咱们真的须要模仿类吗?
原文:https://juejin.im/post/684490…
最初,欢送关注我的公众号:前端开发博客,每日分享陈腐技术文章。