JS 创建实例时,为什么原型里的基础类型不会被共享? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
paledream
V2EX    问与答

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

  •  
  •   paledream 2017-08-08 00:02:17 +08:00 2753 次点击
    这是一个创建于 3064 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如下代码:

    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 

    以下是我的心路历程:

    • 简单的原型链继承会造成引用类型数据的共享,是因为这个原因吗?
    • prototype 不是一个对象吗,如果 me 和 you 是指向同一个 prototype 对象的,不是应该共享 year 吗?
    • 还是说 this 指针将 year 复制到了实例内部?

    求高人指点。

    第 1 条附言    2017-08-08 10:08:10 +08:00
    谢谢各位,我看了各位的回答后感觉应该是:实例向原型链查找到后,将属性拷贝到实例中去,然后就是 js 按值传递时基础类型与引用类型的不同带来的影响了。
    18 条回复    2017-08-08 10:11:29 +08:00
    geelaw
        1
    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
        2
    KeepPro  
       2017-08-08 00:13:22 +08:00 via Android
    我记着 prototype 是一个指针来着。然后这个应该是 js 的两种 值引用和地址引用 造成的区别吧。
    paledream
        3
    paledream  
    OP
       2017-08-08 00:17:00 +08:00
    @geelaw 你的意思是不是类似基础类型的按值传递?
    CDL
        4
    CDL  
       2017-08-08 00:21:55 +08:00
    因为你只是改变的实例的值,并不会影响到原型的初始值
    paledream
        5
    paledream  
    OP
       2017-08-08 00:31:04 +08:00
    @CDL 实例在初始化时,将原型中的非引用属性复制到了实例中,这个意思吗?
    构造函数中并没有 year 这个属性,调用 grow 函数时应该是去原型里去寻找 year 呀
    paledream
        6
    paledream  
    OP
       2017-08-08 00:33:49 +08:00
    @CDL 如果是 funtion Boy() {this.year = 18}这样我就和你的想法一样了
    momocraft
        7
    momocraft  
       2017-08-08 00:37:54 +08:00
    基型都是 immutable 的, 而且 equality 即 identity. 此其所是不是"共享", 因法分.

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

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

    除非你是开发 JS 解释器的,否则你可以忘记这两者的区别,而去抽象地理解。
    M3oM3oBug
        11
    M3oM3oBug  
       2017-08-08 01:25:10 +08:00 via Android
    如果需要达到实例共享同一个属性,可以创建一个立即执行的匿名函数,也就是一个闭包,里面包含的私有属性可以被共享到
    CDL
        12
    CDL  
       2017-08-08 09:05:26 +08:00
    @paledream 实例上没有定义的值会到原型上去查找,实例定义的值会覆盖原型的值,就跟作用域相似,后面数组的值会变是因为 js 的数组和对象是引用值,修改的话就会直接影响到原型的值了
    jevirs
        13
    jevirs  
       2017-08-08 09:30:07 +08:00
    可是你的代码好污啊。。
    SuperMild
        14
    SuperMild  
       2017-08-08 09:37:57 +08:00 via iPhone
    没仔细看,但看见有 this 就觉得头大,JS 能用闭包解决的问题就尽量不要用 this
    dumbass
        15
    dumbass  
       2017-08-08 09:56:27 +08:00   1
    我运行了下你的代码发现,在你执行`me.grow()`时,此时的`this`指向实例本身,相当于`me.year=year+1`,会在`me`上创建一个属性`year`,这是一个 实例属性`me.year`,而你新创建的实例`you`上并没有`you.year`这个属性,所以会去它的`__.proto__`(也就是 Boy.prototype )上找,然后`year`就是 18。不知道是不是这样理解的。
    stzz
        16
    stzz  
       2017-08-08 09:56:57 +08:00   1
    代码是挺污的....
    一般情况下只有在创建情况下才能设置 prototype 的值.
    第一次执行 this.year++; 时, 就是 this.year=this.year+1, 右边是继承查找到的值,左边相当于 this.year=19 设置一个本地属性,形成属性遮蔽..
    code4life
        17
    code4life  
       2017-08-08 09:57:49 +08:00
    @CDL 感谢,学习了。
    paledream
        18
    paledream  
    OP
       2017-08-08 10:11:29 +08:00 via Android
    @stzz 我觉得应该如你所说
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2684 人在线 &nbp; 最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 09:24 PVG 17:24 LAX 01:24 JFK 04:24
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86