原型知识很少单独命题,但更多的是和其它 JS 核心知识结合起来命题。
主要抓手是梳理出一条清晰正确的原型链!
先看一个例子:
var A = function () {};A.prototype.n = 1;var b = new A();A.prototype = {n: 2,m: 3,};var c = new A();console.log(b.n);console.log(b.m);console.log(c.n);console.log(c.m);
大脑运行一下,可能会给出这个答案:
2,3,2,3
实际运行的答案是:
1,undefined,2,3
这里有一个疑问。为什么 b 和 c 的运行结果不同?
当我们用 new 去创建一个实例时,一共做了四件事:
具体案例:
function Person() {this.name = 'zqq';this.age = 28;}var p = new Person();
四件事的具体步骤:
注意:第二步完成后,实例的原型会把构造函数的 prototype 的引用给存(复制)下来
因为 A.prototype 是重新赋值,创建了新的堆内存引用,并不会影响到之前的数据。因此 b.__proto 指向的数据还是之前的并没有改变。
所以 b 的 m 属性,直接输出 undefined。
上例子:
function A() {this.name = 'a';this.color = ['green', 'yellow'];}function B() {}B.prototype = new A();var b1 = new B();var b2 = new B();b1.name = 'change';b1.color.push('black');console.log(b2.name); // 'a'console.log(b2.color); // ["green", "yellow", "black"]
这个例子的关键点在于,修改了 b1 的 name,b2 的 name 保持不变;而修改了 b1 的 color,b2 的 color 却同时修改了!
为什么会这样呢?
其实,原型链逐步向上查找的过程,只会在”读“的时候发生。
如果是一个”写“操作,那么会直接在实例上添加新属性,不会向上查找。
而 color 的如下操作,并不是写(重新赋值)操作,只是修改原有对象:
b1.color.push('black');b1.color.attribute = 'xxx';
真正的写操作是这样的:
b1.color = ['newColor'];
function A() {}function B(a) {this.a = a;}function C(a) {if (a) {this.a = a;}}A.prototype.a = 1;B.prototype.a = 1;C.prototype.a = 1;console.log(new A().a); // 1console.log(new B().a); // undefinedconsole.log(new C(2).a); // 2