JavaScript 为了实现继承,使用了如下两个属性:
prototype
: 原型对象。只有函数才有这个属性,需被实例继承的属性和方法都定义在prototype
对象上。原型对象上有一个指回构造函数自身的指针constructor
。__proto__
: 每个对象都有这个私有属性(函数也是对象),其中实例对象的__proto__
属性指向它的构造函数的原型对象(有些对象不是通过构造函数new
出来的,比如通过Object.create()
和extends
创建的对象)。__proto__
是用以访问[[Prototype]]
的非标准getter/setter
属性,可以使用Object.getPrototypeOf/setPrototypeOf()
获取修改对象的原型。
const obj = Object.create(null)
obj.__proto__ = Array.prototype
console.log(obj.__proto__.slice)
console.log(obj.slice) // undefined
原型上的 __proto__
的 setter
才有改变 [[Prototype]]
的功能,Object.create(null)
的对象设置的 __proto__
只是一个普通的属性,没有改变 [[Prototype]]
原型链
通过
__proto__
将实例对象与他的原型对象串联起来,形成的一条链。
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。这也是为什么判断变量类型用 Object.prototype.toString.call()
更准确,因为在原型链下层继承过程中 toString
被重写了。
Object.prototype.toString.call(NaN).slice(8, -1) // 'Number'
new
操作符的实现
Object.create
: 以一个对象为原型创建一个新对象。
function New(fn, ...args) {
// 1.创建一个空对象,并将该对象的 __proto__ 指向构造函数的 prototype
const obj = Object.create(fn.prototype)
// 2.将构造函数中的 this 指向 obj 后,执行构造函数,获取返回值
const res = fn.apply(obj, args)
// 3.判断返回值类型
return res instanceof Object ? res : obj
}
寄生组合式继承
这是最成熟的方法,也是现在库实现的方法。
- 属性继承:在子类中执行父类构造函数。
- 原型继承:将父类的原型赋值给子类(需注意子类原型上
constructor
的指向)。
function inheritPrototype(subType, superType) {
// 创建对象,创建父类原型的一个副本
const prototype = Object.create(superType.prototype)
// 增强对象,弥补因重写原型而失去默认的 constructor 属性
prototype.constructor = subType
// 指定对象,将新创建的对象赋值给子类的原型
subType.prototype = prototype
}
// 父类初始化实例属性和原型属性
function SuperType(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
// 将父类原型赋值给子类
inheritPrototype(SubType, SuperType)
// 新增子类原型属性
SubType.prototype.sayAge = function () {
console.log(this.age)
}
const instance1 = new SubType('xyc', 23)
const instance2 = new SubType('lxy', 23)
instance1.colors.push('2') // ["red", "blue", "green", "2"]
instance1.colors.push('3') // ["red", "blue", "green", "3"]
ES6 类 extends 继承
核心代码如下,其实现和上述的寄生组合式继承方式一样。
function _inherits(subType, superType) {
subType.prototype = Object.create(superType && superType.prototype, {
constructor: {
value: subType,
enumerable: false,
writable: true,
configurable: true
}
})
if (superType) {
Object.setPrototypeOf ? Object.setPrototypeOf(subType, superType) : (subType.__proto__ = superType)
}
}