Appearance
继承
我们需要掌握 ES5 的继承方式与 ES6 的方式
ES5 的继承
1、 原型链的方式
javascript
function Parent(name) {
this.name = name
this.type = "parent"
}
Parent.prototype.getName = function () {
return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]
function Child(name, age) {
this.name = name
}
Child.prototype = new Parent()
var teacher1 = new Child("张三")
var teacher2 = new Child("李四")
console.log(teacher1)
console.log(teacher1.getName()) //可以调用Person的实例方法与属性,且实例方法中的this指向子对象
console.log(teacher1.constructor === Child) // false
console.log(teacher1.constructor === Parent) // true
问题:
调用父级构造方法的时候不能赋值
Child 的原型其实指向 Parent 的一个同一个实例对象,所以所有的 Child 的实例对象共享同一个实例
Child 的实例对象的构造函数指向 Parent
javascript
teacher1.arr[2] = 100
console.log(teacher2.arr) // [1,2,100,4]
2. 构造函数继承
为了解决上述所有的子实例对象共享同一个父实例对象的问题,其使用了 Parent.call(this, ...args)
ts
function Parent(name) {
this.name = name
this.type = "parent"
}
Parent.prototype.getName = function () {
return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]
function Child(name, age) {
Parent.call(this, ...arguments)
this.name = name
}
var teacher1 = new Child("张三")
var teacher2 = new Child("李四")
console.log(teacher1)
console.log(teacher1.constructor === Child) // true
console.log(teacher1.constructor === Parent) // false
// console.log(teacher1.getName()) // 错误
其解决了上述的 3 个问题,但是引发了一个新的问题,
缺点:
- 子实例对象无法继承父对象原型上的属性和方法
3. 组合式继承
通过上述两种方式的集合,形成了一个新的组合式继承
ts
function Parent(name) {
this.name = name
this.type = "parent"
}
Parent.prototype.getName = function () {
return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]
function Child(name, age) {
Parent.call(this, ...arguments)
this.name = name
}
Child.prototype = new Parent()
// 修改子对象的构造函数的指向
Child.prototype.constructor = Parent
var teacher1 = new Child("张三")
var teacher2 = new Child("李四")
console.log(teacher1)
console.log(teacher1.constructor === Child) // true
console.log(teacher1.constructor === Parent) // false
console.log(teacher1.getName())
问题:
父对象被执行了两次
Child.prototype = new Parent()
修改了子函数的原型,导致自身的原型对象可能被覆盖
4. 寄生式继承
其借用工厂模式和原型链,从而实现了继承的概念
ts
function Parent(name) {
this.name = name
this.type = "parent"
}
Parent.prototype.getName = function () {
return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]
function clone(o) {
function F() {}
F.prototype = o
return new F()
}
function child(name, age) {
var newChild = clone(Parent)
newChild.name = name
return newChild
}
var teacher1 = child()
缺点
- 不再具备构造函数的概念
- 不能实现函数的复用
5. 寄生组合式继承
ts
function Parent(name) {
this.name = name
this.type = "parent"
}
Parent.prototype.getName = function () {
return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]
function Child(name, age) {
Parent.call(this, ...arguments)
this.name = name
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
},
})
var teacher1 = new Child("张三")
var teacher2 = new Child("李四")
console.log(teacher1)
console.log(teacher1.constructor === Child) // false
console.log(teacher1 instanceof Parent) // true
console.log(teacher1.getName()) //可以调用Person的实例方法与属性,且实例方法中的this指向子对象
ES6 的继承
可以使用 extends
进行继承,其核心也是借鉴寄生组合式继承的理念
js
class Animal {
constructor(name, age) {
this.name = name
this.age = age
}
}
class Dog extends Animal {
constructor(name, age) {
super(name, age)
}
}
然后我们看 ES6 转换成 ES5 的结果
js
"use strict"
function _typeof(obj) {
"@babel/helpers - typeof"
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj
}
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype
? "symbol"
: typeof obj
}
}
return _typeof(obj)
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function")
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true },
})
if (superClass) _setPrototypeOf(subClass, superClass)
}
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p
return o
}
return _setPrototypeOf(o, p)
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct()
return function () {
var Super = _getPrototypeOf(Derived),
result
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor
result = Reflect.construct(Super, arguments, NewTarget)
} else {
result = Super.apply(this, arguments)
}
return _possibleConstructorReturn(this, result)
}
}
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call
}
return _assertThisInitialized(self)
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called")
}
return self
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false
if (Reflect.construct.sham) return false
if (typeof Proxy === "function") return true
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}))
return true
} catch (e) {
return false
}
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o)
}
return _getPrototypeOf(o)
}
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left)
} else {
return left instanceof right
}
}
function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a function")
}
}
var Animal = function Animal(name, age) {
_classCallCheck(this, Animal)
this.name = name
this.age = age
}
var Dog = /*#__PURE__*/ (function (_Animal) {
_inherits(Dog, _Animal)
var _super = _createSuper(Dog)
function Dog(name, age) {
_classCallCheck(this, Dog)
return _super.call(this, name, age)
}
return Dog
})(Animal)
- 怎么将 Aninal 的原型对象继承给 Dog,看上面的代码其是通过
_inherits(Dog, _Animal);
js
/**
* subClass 子构造函数
* superClass 父构造函数
*/
function _inherits(subClass, superClass) {
// 错误判断
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function")
}
// 将子构造函数的 prototype 指向父构造函数的 prototype, 并且重写 constructor
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true },
})
if (superClass) _setPrototypeOf(subClass, superClass)
}
function _setPrototypeOf(o, p) {
// setPrototypeOf 的profill语法
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p
return o
}
// 修改 subClass 的原型对象指向 superClass
return _setPrototypeOf(o, p)
}
- 处理 super 语法
对于 Super 其作用主要是以下方面
执行父 Class 对象的构造函数
继承父 Class 构造函数原型链上的属性
js
// var _super = _createSuper(Dog);
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false
if (Reflect.construct.sham) return false
if (typeof Proxy === "function") return true
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}))
return true
} catch (e) {
return false
}
}
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call
}
return _assertThisInitialized(self)
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called")
}
return self
}
function _createSuper(Derived) {
// 判断是否支持 ES6的 Reflect.construct 方法
var hasNativeReflectConstruct = _isNativeReflectConstruct()
return function () {
var Super = _getPrototypeOf(Derived),
result
if (hasNativeReflectConstruct) {
// 获取其原型链上的 constructor
var NewTarget = _getPrototypeOf(this).constructor
// 对
result = Reflect.construct(Super, arguments, NewTarget)
} else {
result = Super.apply(this, arguments)
}
return _possibleConstructorReturn(this, result)
}
}