首先抛出三个问题
- 什么是原型对象
- 构造函数, 原型对象, 实例的三角关系图
- 原型链如何形成
原型对象
在 JavaScript 中,除去一部分内建函数,绝大多数的函数都会包含有一个叫做 prototype 的属性,指向原型对象,
基于构造函数创建出来的实例, 都可以共享访问原型对象的属性。
例如我们的 hasOwnProperty, toString ⽅法等其实是 Obejct 原型对象的方法,它可以被任何对象当做⾃⼰的⽅法来使⽤。
hasOwnProperty 用于判断, 某个属性, 是不是自己的 (还是原型链上的)
来看一段代码:
let person = {
name: 'Tom',
age: 18,
job: 'student',
}
console.log(person.hasOwnProperty('name')) // true
console.log(person.hasOwnProperty('hasOwnProperty')) // false
console.log(Object.prototype.hasOwnProperty('hasOwnProperty')) // true
可以看到,hasOwnProperty 并不是 person 对象的属性,但是 person 却能调用它。
那么 person 对象是如何找到 Object 原型中的 hasOwnProperty 的呢?这就要靠原型链的能力了。
需求: 简单绘制原型三角关系图!
原型链
在 JavaScript 中,每个对象中都有一个 __proto__ 属性,这个属性指向了当前对象的构造函数的原型。
对象可以通过自身的 __proto__属性与它的构造函数的原型对象连接起来,
而因为它的原型对象也有 __proto__,因此这样就串联形成一个链式结构,也就是我们称为的原型链。
原型链的作用:
原型链如此的重要的原因就在于它决定了 JavaScript 中继承的实现方式。当我们访问一个属性时,查找机制如下:
- 访问对象实例属性,有则返回,没有就通过
__proto__去它的原型对象查找。 - 原型对象找到即返回,找不到,继续通过原型对象的 proto 查找。
- 一层一层一直找到
Object.prototype,如果找到目标属性即返回,找不到就返回undefined,不会再往下找,因为在往下找__proto__就是null了。
通过原型链实现继承
寄生组合继承:是现在业内公认的比较可靠的JS 继承模式,ES6 的 class 继承在 babel 转义后,底层也是使用的寄生组合继承的方式实现的。
function inherit(Child, Parent) {
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false, // 不可枚举该属性
writable: true, // 可改写该属性
configurable: true, // 可用 delete 删除该属性
},
});
}
instanceof 的原理
instanceof用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
function _instanceof(l, R) {
// 判断是不是对象或者函数(不包括null)
const isObjectOrFunction = (target) => {
return (typeof target === "object" && target !== null) || typeof target === "function";
};
if (!isObjectOrFunction(l)) {
//原始值,直接返回false
return false;
} else {
// 创建一个指针,指向l的隐式原型
let p = l;
// 构造函数的原型
const prototype = R.prototype;
while (p) {
let proto = p.__proto__; //或者使用 Object.getPrototypeOf
// 如果隐式原型===显示原型,则返回true
if (proto === prototype) {
return true;
}
// 沿着原型链执行,最终到null结束
p = proto;
}
// 执行了到原型链终点,没有找到则返回false
return false;
}
}
- 需要注意的是左边如果为原始值直接返回 false