17.1 基础
在js中,每一个对象都包含一个原型属性,用于关联另一个对象,关联后就能够使用那个对象的属性和方法;对象之间通过原型关联到一起,就好比用一条锁链将一个个对象连接在一起,在与各个对象挂钩后,最终形成了一条原型链。(注意:js中的对象分为函数对象和普通对象,这两类对象均具备__ proto __属性,但是只有函数对象才有prototype属性。)
17.2 原型链流程
下图是网上流传的学习原型链的一幅神图,我们先贴出来一点一点分析。
17.2.1 普通对象
js对象中的一种类型是普通对象,上述图中的一条主线也是根据普通对象来的,下面通过一段代码来演示一下。
const obj = { a: 10, b: 20 }; console.log(obj); console.log(obj.__proto__); console.log(obj.__proto__.__proto__); console.log(obj.__proto__.constructor); console.log(obj.__proto__.constructor.__proto__); console.log(obj.__proto__.constructor.__proto__.__proto__); console.log(obj.__proto__.constructor.__proto__.constructor); console.log(obj.__proto__.constructor.__proto__.constructor.__proto__);
上述的打印结果如下所示,其打印结果与上述普通对象的链路完全一致。
17.2.2 函数对象
js对象中的一种类型是函数对象,上述图中的一条主线也是根据函数对象来的,下面通过一段代码来演示一下。
function fun() { let a = 12; } console.log(fun); console.log(fun.__proto__); console.log(fun.__proto__.__proto__); console.log(fun.__proto__.__proto__.__proto__); console.log(fun.__proto__.constructor); console.log(fun.__proto__.constructor.__proto__); console.log(fun.__proto__.constructor.__proto__.__proto__); console.log(fun.__proto__.constructor.__proto__.__proto__.__proto__);
上述的打印结果如下所示,其打印结果与上述函数对象的链路完全一致。
17.3 两个机制
上述讲述了原型链的定义及其流程,那么对于其上的属性是按照什么流程查找和修改该的呢?
17.3.1 属性查找机制
当查找对象的属性时,如果实例对象自身不存在该属性,则沿着原型链往上一级查找,找到时则输出,不存在时,则继续沿着原型链往上一级查找,直至最顶级的原型对象Object.prototype,如还是没找到,则输出undefined;
const obj1 = { a: 10 }; const obj2 = { b: 20 }; Object.setPrototypeOf(obj2, obj1); // 由于obj2自身不存在a属性,但是其原型obj1上存在,所以输出其上的值10; console.log(obj2.a); // 10 // 由于b属性在obj2本身,输出20; console.log(obj2.b); // 20 // c属性在obj2和其原型上都不存在,则输出undefined。 console.log(obj2.c); // undefined
17.3.2 属性修改机制
只会修改实例对象本身的属性,如果不存在,则进行添加该属性,如果需要修改原型的属性时,则可以用: b.prototype.x = 2;但是这样会造成所有继承于该对象的实例的属性发生改变。
const obj1 = { a: 10 }; const obj2 = { b: 20 }; Object.setPrototypeOf(obj2, obj1); console.log(obj2); // {b: 20} console.log(obj1); // {a: 10} obj2.b = 30; obj2.a = 50; // 修改b属性生效,修改a属性在其本身添加了a属性 console.log(obj2); // { b: 30, a: 50 } console.log(obj1); // { a: 10 } obj2.__proto__.a = 20; // 直接修改原型上属性生效 console.log(obj1); // { a: 20 }