两段小代码
第一段:
function Outer() {
this.a = 1;
}
function Inner() {
}
var outer = new Outer();
Inner.prototype = outer;
var inner = new Inner();
inner.a += 1;
console.log(inner.a, outer.a);
第二段:
function Outer() {
this.data = {
a: 1
};
}
function Inner() {
}
var outer = new Outer();
Inner.prototype = outer;
var inner = new Inner();
inner.data.a += 1;
console.log(inner.data.a, outer.data.a);
请问下为什么两段代码运行结果不一样呢?在第二段代码中,在inner中修改变量为何会影响到outer?
樓上都沒說到點上。
簡而言之,
結果等價於
並沒有對 inner 進行任何賦值操作,只是對 inner.data 進行了 GetValue。
而
inner.a
在不同上下文當中,含義有差異,主要體現在並沒有原型鏈尋址的過程。
詳細原因見下文。
c.d += b
是c.d = c.d + b
的縮寫,其中c.d
的位置必須是一個 LeftHandSideExpression。然而
c.d + b
中的c.d
和c.d = ...
中的c.d
是不同的,比如這裏c.d
是一個 PropertyReferance,c.d + b
會對c.d
進行 GetValue,從而遍歷c
的原型鏈尋找 屬性名爲"d"
的值而
c.d = ...
則會對c
的屬性"d"
賦值,而c
又不存在屬性d
,因此創建了新屬性,名爲d
。至於
c.d.e
當中的c.d
,則始終會被 GetValue,即便是c.d.e = ...
也仍舊是訪問了原型鏈上的c.d
。簡而言之這個問題的核心在於屬性尋址的機制:
而
加粗的操作就是導致這一區別的關鍵,使得
c.d.e
中的c.d
在任何上下文中都有相同的含義,而單純的c.d
在c.d + b
和c.d = ...
中有了區別。具體區別如下:
而 PutValue 中:
可見賦值的時候並不會像 GetValue 時那樣進行原型鏈尋址,而是直接修改了外層對象。
javascript中的继承,归根结底 就是 对象的赋值引用;
第一段中 a 只是 Number 类型,赋值是按 按值传递的;
第二段中 data 是 Object 类型, 赋值是 按共享传递的;
按共享传递的 可以理解为 按安全指针传递,安全指针不能去解除引用和改变对象,但可以去修改该对象的属性值。
运行下面的代码:
不知道你能不能理解,具体可以看下 javascript 的 赋值策略
因为data是对象,继承了对象,两个a都是这个对象的引用
简单数据类型,例如Number,在对象继承的时候,会做值copy,所以你改变父对象或子对象的这些相关属性,其实改变的是各自的属性值
与此对应的是如果是引用类型,例如object,array,function等,在对象继承的时候,会copy这些对象的引用地址,而不是他们实际的值,这意味着我们在父对象和子对象中的属性,指向的都是同一个引用,所以更改这个引用对象内部的属性,会同时影响父子对象。
这里有一个例外是不可变数据类型,如String,由于String的不可更改性,尽管继承的时候是引用,但是你修改它的时候其实是新创建了字符串,由于javascript的prototype继承覆盖机制,所以并不会造成引用问题,简化起见,你可以将String理解为简单数据类型。
另外在你的示范代码中的继承方法其实是有问题的:假设你的Outer内部如果有dom操作行为,那么在继承的时候,由于你实例化了Outer,代码就会被执行,而这次操作是错误的。可以参考 javascript设计模式书中的继承方法, 点击这里,同时它解释了这个方法最后三行的含义,我觉得很好。
data 是对象,对象是引用类型
bumfod 正解!