JS中所有的对象都默认有一个
[[Prototype]]属性,里面存储的是对其它对象的引用。
而JS中所有的内置对象(Function、Array等)都是基于Object.prototype这个对象来实现的。所以链的尽头就是Object.prototype。
这种环环相扣的结构很像一条锁链,所以被称为原型链。
原型链基本上属于我们学习JS必须要了解的,不懂的话,会发现JS有很多不能理解的"神奇现象"。
为什么要这么链接呢?
当我们在一个对象上,没找到需要的属性或方法时,就会沿着[[Prototype]]指向的对象继续找,找到就返回,没找到返回undefined。这就是链的作用。
如何自己创建链接呢?
如果我们想把自己定义的两个对象通过原型链的方式链接起来,该怎么做呢?
通过Object.create()来实现。
var obj1 = {
num1: 1
}
var obj2 = Object.create(obj1);
上述代码中Object.create()的执行过程:
- 先创建一个对象
- 这个对象的
[[prototype]]关联到obj1 - 返回这个对象
这个返回的对象就赋值给了obj2,所以obj2的[[prototype]]关联到obj1。
原型链图示:
给原型链上存在的属性赋值会发生什么?
不要弄混哦,这里是赋值。取值的话就沿着链直接取,返回第一个找到的,与作用域RHS查询一样。
链上的属性不是只读
如果在prototype链上层有同名属性且不是只读,那就会在当前对象上添加一个同名属性
var obj1 = {
num1: 1
};
var obj2 = Object.create(obj1);
obj2.num1 = 2; // 会直接在obj2上创建一个num1,不会覆盖obj1
console.log(obj2.num1); // 2
console.log(obj1.num1); // 1
// 这些是为什么后面的章节中,使用对象可以实现继承的原因
链上的属性是只读
非严格模式下赋值语句被忽略。严格模式下抛出错误。
var obj1 = {
num1: 1
};
// 设置只读
Object.defineProperty(obj1, 'num1', {
writable: false
});
var obj2 = Object.create(obj1);
obj2.num1 = 2; // 赋值失败
console.log(obj2.num1); // 1 沿着链往上找,找到了num1返回
console.log(obj1.num1); // 1
__proto__与prototype的区别是什么?
__proto__与prototype本质上没有区别都是用来存储对其它对象的引用。
__proto__是所有对象都具有的属性(也就是前面说的[[prototype]])。
prototype是只有函数才具有的属性。
下面通过代码和图示来展示一下函数中的链式关系,和上面的对象链式关系做个对比:
在这之前,再回顾一下,在new 一个函数时发生了什么?
- 创建一个
新对象 新对象的[[ prototype ]]连接到函数的prototype- 然后确认函数的返回值
- 如果函数return不是一个对象, 把
this指向创建的新对象,然后执行构造函数(使用this.赋值的属性会放进新对象) - 如果函数
return一个对象,new调用的函数调用会返回这个对象,并丢弃之前创建的新对象
- 如果函数return不是一个对象, 把
- 结束
函数链接关系:
function Smile() {
this.name = 'Jack';
}
var laugh = new Smile();
console.log(laugh.name); // Jack
原型链图示:
可以看出和对象链相比,函数因为多了一个prototype属性,链要复杂一点。(复杂还有一个原因,函数还多了一个constructor属性,但就是把constructor属性去除,还是要比对象链复杂一点)
如果觉得内容对你有帮助,请点个赞和关注,你们的鼓励是我持续更新下去的动力,比心。
如需转载请注明出处,感恩。
更多内容可先关注GitHub