JS 创建实例时,为什么原型里的基础类型不会被共享?

2017-08-08 00:02:17 +08:00
 paledream

如下代码:

function Boy() {};

Boy.prototype.grow = function () {
    this.year++;
    this.girlfriend.push('hands');
    console.log(this.girlfriend);
    console.log(this.year);
};

Boy.prototype.year = 18;
Boy.prototype.girlfriend = [];

var me = new Boy();
me.grow();
me.grow();

var you = new Boy();
you.grow();

输出是

[ 'hands' ]
19
[ 'hands', 'hands' ]
20
[ 'hands', 'hands', 'hands' ]
19

以下是我的心路历程:

求高人指点。

2293 次点击
所在节点    问与答
18 条回复
geelaw
2017-08-08 00:05:49 +08:00
用字符串可以给出一个简单的理解:

this.name += "hello"

等价于

this.name = this.name + "hello"

字符串是不可变的,this.name + "hello" 和原先的 this.name 不是同一个对象,改变的是 this.name 指向的 **是** 谁,而不是它指向 **的** 谁。

参考 C# 值类型语义。
KeepPro
2017-08-08 00:13:22 +08:00
我记着 prototype 是一个指针来着。然后这个应该是 js 的两种 值引用和地址引用 造成的区别吧。
paledream
2017-08-08 00:17:00 +08:00
@geelaw 你的意思是不是类似基础类型的按值传递?
CDL
2017-08-08 00:21:55 +08:00
因为你只是改变的实例的值,并不会影响到原型的初始值
paledream
2017-08-08 00:31:04 +08:00
@CDL 实例在初始化时,将原型中的非引用属性复制到了实例中,这个意思吗?
构造函数中并没有 year 这个属性,调用 grow 函数时应该是去原型里去寻找 year 呀
paledream
2017-08-08 00:33:49 +08:00
@CDL 如果是 funtion Boy() {this.year = 18}这样我就和你的想法一样了
momocraft
2017-08-08 00:37:54 +08:00
基礎類型都是 immutable 的, 而且 equality 即 identity. 此時其實無所謂是不是"共享", 因為無法區分.

this.year++; 這句從 prototype 讀, 然後寫到了 instance. 即你說的 "this 指针将 year 复制到了实例内部?"
FrankFang128
2017-08-08 00:46:19 +08:00
console.dir(me)
console.dir(you)
FrankFang128
2017-08-08 00:58:33 +08:00
geelaw
2017-08-08 01:02:01 +08:00
@paledream 你可以认为基础类型按值传递,也可以认为基础类型不可变,这两个效果是一样的。

简单地说,无论是 year 是一个指向 int const 的指针,还是就是 int,没有区别。

除非你是开发 JS 解释器的,否则你可以忘记这两者的区别,而去抽象地理解。
M3oM3oBug
2017-08-08 01:25:10 +08:00
如果需要达到实例共享同一个属性,可以创建一个立即执行的匿名函数,也就是一个闭包,里面包含的私有属性可以被共享到
CDL
2017-08-08 09:05:26 +08:00
@paledream 实例上没有定义的值会到原型上去查找,实例定义的值会覆盖原型的值,就跟作用域相似,后面数组的值会变是因为 js 的数组和对象是引用值,修改的话就会直接影响到原型的值了
jevirs
2017-08-08 09:30:07 +08:00
可是你的代码好污啊。。
SuperMild
2017-08-08 09:37:57 +08:00
没仔细看,但看见有 this 就觉得头大,JS 能用闭包解决的问题就尽量不要用 this
bojackhorseman
2017-08-08 09:56:27 +08:00
我运行了下你的代码发现,在你执行`me.grow()`时,此时的`this`指向实例本身,相当于`me.year=year+1`,会在`me`上创建一个属性`year`,这是一个 实例属性`me.year`,而你新创建的实例`you`上并没有`you.year`这个属性,所以会去它的`__.proto__`(也就是 Boy.prototype )上找,然后`year`就是 18。不知道是不是这样理解的。
stzz
2017-08-08 09:56:57 +08:00
代码是挺污的....
一般情况下只有在创建情况下才能设置 prototype 的值.
第一次执行 this.year++; 时, 就是 this.year=this.year+1, 右边是继承查找到的值,左边相当于 this.year=19 设置一个本地属性,形成属性遮蔽..
code4life
2017-08-08 09:57:49 +08:00
@CDL 感谢,学习了。
paledream
2017-08-08 10:11:29 +08:00
@stzz 我觉得应该如你所说

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/381201

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX