js 的 new 和原型链一个问题请教,感谢!!!! - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
xuecat
V2EX    Javascript

js 的 new 和原型链一个问题请教,感谢!!!!

  •  
  •   xuecat 2018-01-26 14:11:35 +08:00 4801 次点击
    这是一个创建于 2814 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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得到了valarr
    new Sub后的实质变化也只是sub1__proto__指向了Sub.prototype

    也就是说,sub1.val,sub1.arr这都是访问Sub.prototype上的。
    这特么访问就都是一个链上的东西了。
    为何sub1.arr能影响,而sub1.val却不影响呢?
    不能因为一个是值类型,一个是引用类型就有区别吧!!!!

    是 new 的时候对值类型复制了吗?可是我翻了下没这描述。。。。。求解答

    22 条回复    2018-02-15 01:11:50 +08:00
    zzuieliyaoli
        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
    mskf
        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 =
    zzuieliyaoli
        3
    zzuieliyaoli  
       2018-01-26 14:26:39 +08:00
    #1 我的回复是错,请忽略
    Egg
        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/>
    mskf
        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 这个属性为什么看上去不是公用的
    因为它是值对象。。。
    xuecat
        6
    xuecat  
    OP
       2018-01-26 14:32:21 +08:00
    @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 换上下文。
    bramblex
        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 到底在什么位置
    mskf
        8
    mskf  
       2018-01-26 14:33:28 +08:00   1
    @xuecat
    嗯。。。这样说把,如果 sub1.val 影响了 sub2.val ,是不是可以说明 sub1 和 sub2 的指针指向了同一个地址
    gogo81745
        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
    Egg
        10
    Egg  
       2018-01-26 14:39:53 +08:00
    @gogo81745 如果一定要用到 push 这个方法的话有什么解决方案吗
    mskf
        11
    mskf  
       2018-01-26 14:40:53 +08:00   1
    @Egg Super.prototype.arr = [1]
    xuecat
        12
    xuecat  
    OP
       2018-01-26 14:42:01 +08:00
    @gogo81745 果然是这样的,确实是多了一个属性。万谢!!!

    他们也确实都访问到原型链上了
    Egg
        13
    Egg  
       2018-01-26 14:47:19 +08:00
    @mskf 谢谢
    gogo81745
        14
    gogo81745  
       2018-01-26 14:50:03 +08:00 via Android
    @Egg
    你是指使用 push,并且不影响原型链上的数组吗?
    sub1.arr = sub1.arr.slice()
    sub1.arr.push(2)
    我想到的最简单的方法就这种,不知道有没有什么奇技淫巧
    Egg
        15
    Egg  
       2018-01-26 14:54:19 +08:00
    @gogo81745 这个也可以 都好神奇 又学到一点新东西
    不过感觉还是在刚开始定义的时候 方法和数组用 prototype 来定义继承会好一点
    KuroNekoFan
        16
    KuroNekoFan  
       2018-01-26 15:21:55 +08:00
    关键是理解原型链,实例和基本类型,引用类型
    bucky
        17
    bucky  
       2018-01-26 15:28:52 +08:00
    还是拥抱 es6 吧
    timwei
        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]
    tjsdtc
        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 对象中不会添加。
    isbase
        20
    isbase  
       2018-01-26 17:43:46 +08:00
    xilixjd
        21
    xilixjd  
       2018-01-26 23:39:30 +08:00
    7 楼完美解释
    yunfeihe
        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 的闭包函数,上面的话应该很容易理解。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2379 人在线   最高记录 6679   /span>     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 15:41 PVG 23:41 LAX 08:41 JFK 11:41
    Do have faith in what you're doing.
    ubao 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