paledream
V2EX  ›  问与答

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

  •  
  •   paledream · Aug 8, 2017 · 3019 views
    This topic created in 3203 days ago, the information mentioned may be changed or developed.

    如下代码:

    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 复制到了实例内部?

    求高人指点。

    Supplement 1  ·  Aug 8, 2017
    谢谢各位,我看了各位的回答后感觉应该是:实例向原型链查找到后,将属性拷贝到实例中去,然后就是 js 按值传递时基础类型与引用类型的不同带来的影响了。
    18 replies    2017-08-08 10:11:29 +08:00
    geelaw
        1
    geelaw  
       Aug 8, 2017
    用字符串可以给出一个简单的理解:

    this.name += "hello"

    等价于

    this.name = this.name + "hello"

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

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

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

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

    除非你是开发 JS 解释器的,否则你可以忘记这两者的区别,而去抽象地理解。
    M3oM3oBug
        11
    M3oM3oBug  
       Aug 8, 2017 via Android
    如果需要达到实例共享同一个属性,可以创建一个立即执行的匿名函数,也就是一个闭包,里面包含的私有属性可以被共享到
    CDL
        12
    CDL  
       Aug 8, 2017
    @paledream 实例上没有定义的值会到原型上去查找,实例定义的值会覆盖原型的值,就跟作用域相似,后面数组的值会变是因为 js 的数组和对象是引用值,修改的话就会直接影响到原型的值了
    jevirs
        13
    jevirs  
       Aug 8, 2017
    可是你的代码好污啊。。
    SuperMild
        14
    SuperMild  
       Aug 8, 2017 via iPhone
    没仔细看,但看见有 this 就觉得头大,JS 能用闭包解决的问题就尽量不要用 this
    dumbass
        15
    dumbass  
       Aug 8, 2017   ❤️ 1
    我运行了下你的代码发现,在你执行`me.grow()`时,此时的`this`指向实例本身,相当于`me.year=year+1`,会在`me`上创建一个属性`year`,这是一个 实例属性`me.year`,而你新创建的实例`you`上并没有`you.year`这个属性,所以会去它的`__.proto__`(也就是 Boy.prototype )上找,然后`year`就是 18。不知道是不是这样理解的。
    stzz
        16
    stzz  
       Aug 8, 2017   ❤️ 1
    代码是挺污的....
    一般情况下只有在创建情况下才能设置 prototype 的值.
    第一次执行 this.year++; 时, 就是 this.year=this.year+1, 右边是继承查找到的值,左边相当于 this.year=19 设置一个本地属性,形成属性遮蔽..
    code4life
        17
    code4life  
       Aug 8, 2017
    @CDL 感谢,学习了。
    paledream
        18
    paledream  
    OP
       Aug 8, 2017 via Android
    @stzz 我觉得应该如你所说
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3058 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 52ms · UTC 06:50 · PVG 14:50 · LAX 23:50 · JFK 02:50
    ♥ Do have faith in what you're doing.