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
}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)
}
}