# 原型链
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 (opens new window)