V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
paledream
V2EX  ›  问与答

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

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

    如下代码:

    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
    bojackhorseman
        15
    bojackhorseman  
       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   ·   实用小工具   ·   1042 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 20:19 · PVG 04:19 · LAX 12:19 · JFK 15:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.