function Super() {
this.val = 1;
this.arr = [1];
}
function Sub() {...}
Sub.prototype = new Super();
var sub1 = new Sub();
var sub2 = new Sub();
sub1.val = 2;//不会影响 sub2
sub1.arr.push(2);//会影响 sub2
上面的代码有点我无法理解,想请教下!!!
先讲下我的理解:
new Super
后,导致Sub.prototype
通过call
得到了val
和arr
。
new Sub
后的实质变化也只是sub1
的__proto__
指向了Sub.prototype
也就是说,sub1.val
,sub1.arr
这都是访问Sub.prototype
上的。
这特么访问就都是一个链上的东西了。
为何sub1.arr
能影响,而sub1.val
却不影响呢?
不能因为一个是值类型,一个是引用类型就有区别吧!!!!
是 new 的时候对值类型复制了吗?可是我翻了下没这描述。。。。。求解答
1
zzuieliyaoli 2018-01-26 14:23:47 +08:00
"new Sub 后的实质变化也只是 sub1 的 __proto__ 指向了 Sub.prototype" 不对,
应该为 “ sub1、sub2 的__proto__指向了 Sup.prototype ” sub1.__proto__.arr === sub2.__proto__.arr // true |
2
mskf 2018-01-26 14:24:18 +08:00
先不管继承,根据你的描述,你的问题应该是这个
function Super() { this.val = 1; this.arr = [1]; } var a = new Super(),b= new Super() a.arr.push(2) b.arr//1,2 为什么这里受影响了 --- 首先你要知道 function Super(){ this.publicProperty = 1 //公有属性,所有实例公有的 this.publicProperty = 1 //公有属性,所有实例公有的 } Super.staticProperty = |
3
zzuieliyaoli 2018-01-26 14:26:39 +08:00
#1 我的回复是错,请忽略
|
4
Egg 2018-01-26 14:27:10 +08:00
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain<br/>
在这个最下面的地方讲了一下 prototype 说到 new Super() 实际用的是 call 这个方法 也就是说 <br/> Super 下 this.val 给 call 到了 sub1 sub2 两个重新定义的变量当中<br/> Super 下 this.arr 因为是数组 所以 sub1 sub2 中的 arr 指向依旧是 Super 中的 arr<br/> |
5
mskf 2018-01-26 14:27:15 +08:00
@mskf 妈蛋还没写完,敲 TAB 敲到回车了,继续
--- 首先你要知道 function Super(){ this.publicProperty = 1 //公有属性,所有实例公有的 this.publicPropertyArr = [] //公有属性,所有实例公有的 } Super.staticProperty = 2//静态属性,不能用 this 调用 Super.prototype.prototypeProperty = 3 // 原型属性,实例化时赋予对象 --- 你的问题在于 publicProperty 这个属性为什么看上去不是公用的 因为它是值对象。。。 |
6
xuecat OP @zzuieliyaoli @mskf <br/>
我先修正俩句 “ sub1、sub2 的__proto__指向了 Sup.prototype ”” "也就是说,sub1.val,sub1.arr,sub2.val,sub2.arr 这都是访问 Sub.prototype 上的。" 我能理解 sub1.arr.push(2)会影响 sub2,不能理解 sub1.val 不影响 sub2 我认为它们访问的 arr 和 val 来源就是 Sup.prototype 上的,所以都是一样的。 我如此认为的依据: new 的定义: ```js var obj = {}; obj.__proto__ = Base.prototype; Base.call(obj); ``` 也就是说 new Sub 后,实质也就只是让__proto__指向了 prototype,和 call 换上下文。 |
7
bramblex 2018-01-26 14:33:14 +08:00
sub => { __prop__: {val: 1, arr: [1] } }
sub2 => { __prop__:{val:1, arr:[1] }} sub.val => 1 sub.__prop__ === sub2.__prop__ => true sub.val = 2 sub => { val: 1, __prop__: {val:1, arr:[1] }} sub2 => { __prop__:{val:1, arr:[1] }} sub.arr.push(2) sub => { val: 1, __prop__: {val:1, arr:[1,2] }} sub2 => { __prop__:{val:1, arr:[1,2] }} 你需要知道的是, 这个 val / arr 到底在什么位置 |
8
mskf 2018-01-26 14:33:28 +08:00 1
@xuecat
嗯。。。这样说把,如果 sub1.val 影响了 sub2.val ,是不是可以说明 sub1 和 sub2 的指针指向了同一个地址 |
9
gogo81745 2018-01-26 14:33:56 +08:00 via Android 6
sub1.var = 2
这里赋值了,sub1 多了一个叫 var 的私有属性,并不是修改了原型链上的 var 因此原型链上的 var 没有改变,而下次访问 sub1.var 时直接读私有属性不读原型链上的了 而对数组的并不是赋值,修改的就是原型链上的数组。 如果你改成 sub1.arr = [1, 2],那就不会影响 sub2 |
14
gogo81745 2018-01-26 14:50:03 +08:00 via Android
|
15
Egg 2018-01-26 14:54:19 +08:00
@gogo81745 这个也可以 都好神奇 又学到一点新东西
不过感觉还是在刚开始定义的时候 方法和数组用 prototype 来定义继承会好一点 |
16
KuroNekoFan 2018-01-26 15:21:55 +08:00
关键是理解原型链,实例和基本类型,引用类型
|
17
bucky 2018-01-26 15:28:52 +08:00
还是拥抱 es6 吧
|
18
timwei 2018-01-26 16:10:22 +08:00
function Super() {
this.val = 1; this.arr = [1]; } function Sub(arr) { Super.call(this, arr) } Sub.prototype = new Super(); var sub1 = new Sub(); var sub2 = new Sub(); sub1.arr.push(2); // [1,2] sub2.arr // [1] |
19
tjsdtc 2018-01-26 16:54:09 +08:00
这是属性设置的屏蔽问题,补充一点:
现假设设置 foo.a = 1,而 a 不存在 foo 对象中而存在其原型链中时,有三种情况: 1. a 属性是普通属性,此时会在 foo 对象上生成一个 a (就是楼主的情况)。 2. a 属性是只读属性( writable: false ),此时设置会直接无效,foo 对象中不会添加,严格模式下报错。 3. a 属性是一个 setter,则直接调用 setter,foo 对象中不会添加。 |
20
isbase 2018-01-26 17:43:46 +08:00
|
21
xilixjd 2018-01-26 23:39:30 +08:00
7 楼完美解释
|
22
yunfeihe 2018-02-15 01:11:50 +08:00
## 关于 sub1.arr.push(2);//会影响 sub2 的问题
Sub.prototype = new Super(); 这一句,创建了一个新的 Super 实例,并且让 Sub.prototype 指向(并非复制)这个 Super 实例。 var sub1 = new Sub(); var sub2 = new Sub(); new 只是一个语法糖,等同于在一个构造函数里加了两段代码 var yourFunction = function(){ this = Object.create(yourFunction.prototype); //new 后隐性执行的代码 ..... 实际代码 ..... return this; //new 后隐性执行的代码 } 假如楼主没有在 Sub()函数里写入自定义代码(只是为了便于理解,实际上不可能)则 var sub1 = new Sub() 等价于 var sub1 = Object.create(Sub.prototype) var sub2 = new Sub() 等价于 var sub2 = Object.create(Sub.prototype) 这样就很容易解释关于 array 的问题了,当执行 arr.push 时,编译器先查找 sub1 本身,然后根据原型链查询由 Object.create(Sub.prototype)创建的对象(也就是 Sub.__proto__所指的对象),这个对象也就是刚开始创建的 Super 实例,而 sub1 和 sub2 指向同一个 Super 实例,所以可以相互影响。 ## 关于 sub1.val = 2;//不会影响 sub2 这个问题 楼主应该很容易判断出来,对于一个双层函数,外层函数执行了 var a = 1,当内层函数对 a 这个变量进行操作时,有无在内层函数里同样声明 var a,a 的值可能大不相同(声明了即是内层函数变量,否则是外层)。 但是对象是没有 var sub1.val 这样的声明语句的,所以当你执行 sub1.val=2 时,不仅仅是赋值,可以理解为是声明且赋值。声明了 sub1 的私有属性 val,并且赋值为 2,覆盖掉了原型的 val。事实上如果楼主尝试 delete sub1.val 并且再次执行 sub1.val 会发现值又等于 1 了。 ps:楼主要是写过 python3 的闭包函数,上面的话应该很容易理解。 |