为什么第一个例子可以修改原型链中的值?

为什么第一个例子可以修改原型链中的值,而第二个不可以?
此题和下面这个链接应该是同一原理吧,可我没太看明白
关于Object.create()与原型链的面试题?

有同学回答是原型链的原因,有同学说是引用的问题,求解?

var fun = function () {

    };

    fun.prototype = {
        info: {
            name: 'peter',
            age: 25
        }
    };

    var a = new fun();
    var b = new fun();

    a.info.name = 'jack';
    b.info.name = 'tom';
    console.log(a.info.name)//tom
    console.log(b.info.name)//tom

    var fun = function () {

    }

    fun.prototype = {
        name: 'peter',
        age: 25
    }

    var a = new fun();
    var b = new fun();

    a.name = 'jack';
    b.name = 'tom';
    console.log(a.name)//jack
    console.log(b.name)//tom
阅读 5.8k
10 个回答

因为第二个你实际上是在ab对象上都添加了name属性,你看看a.__proto__.name就知道了。第一个则是修改的prototype

读和写不是一个路线。
读会在原型链上一直找下去。
而对于要赋值的属性或者方法会先找到它所在的位置。有则赋值,无则增加。(如果路径不存在,会报错)。
也就是赋值操作(写)的最后一步是不会查找原型链的。
//
a.info.name = 'jack';
b.info.name = 'tom';
相当于name的位置是a.info和b.info上。指向了同一个原型。所以a.info.name 和b.info.name 操作的其实都是同一个原型上的值。
//
a.name = 'jack';
b.name = 'tom';
在a上找name,没找到,直接添加name属性并赋值(而不会再去原型链查找)。b同。
//
期待其他人能给个专业的文档支持

题主如果充分点理解原型链的话,这问题就不是问题了。

第一个的原型指向

{
    info: {
            name: 'peter',
            age: 25
        }
}

当我们访问a跟b的时候,访问他们的info属性其实都是原型那边的info,即同一个;

a.info === b.info //true

但是第二个则不同了 ,原型链指向的是

{
  name: 'peter',
  age: 25
}

当我们刚生成a、b时,a.name===b.name;
但是当你给他们的name赋值时,实际上,这个新的name不再是原型链的name了,而且归于各自对象实例的属性。

当然如果在第一个中,你如果给a.info = {name:'d'} ; 那a的info就是自己的属性而不是原型链上的那个对象里的info了,这时候要想访问原型链的info,就得通过a.__proto__.info访问了。

实质上应该是对象的浅引用问题,直接帮你改一下格式就看出来了。

var fun = function() {};

var info = {
  name: 'peter',
  age: 25
};

fun.prototype.info = info;

var a = new fun();
var b = new fun();

a.info.name = 'jack';
b.info.name = 'tom';
console.log(a.info.name) //tom
console.log(b.info.name) //tom
console.log(info) // 对象的浅引用,直观
var fun = function() {}

fun.prototype.name = 'peter';
fun.prototype.age = 25;

var a = new fun();
var b = new fun();

a.name = 'jack';
b.name = 'tom';
console.log(a.name) //jack
console.log(b.name) //tom

更新,主要是补充解释一下啊。

第一段的意思是给fun的原型链覆盖了一个对象,对象中info属性为另一个对象。就像我改写的这个样子,不覆盖fun的原型链,直接添加了一个info的属性。

当你在两次new对象后,a和b都继承了原型链上info,然后去修改a和b的info.name,然后a和b上都没有info这个属性,就去原型链上找,找到了,直接指向的是info那个对象,然后分别都修改了那个对象。
所以,你两次console出来是一样的。

直接上代码证明吧:
clipboard.png

第二段中直接给原型链添加了name属性,赋值为一个字符串,字符串不存在浅引用。字符串和数字、布尔一样,只有在操作的时候才转变成对象,操作完成就存成了相应的数据类型的数据。

在修改的时候,先找本身,是undefined,然后找原型链,是peter一个字符串,由于没有引用关系,当直接修改name属性值的时候直接从本身进行了修改。然后你console的时候,自然就是不一样的。

以下仅为个人理解。
首先对象的引用是当你调用a.name时,会先在a对象下找,如果没找到,会去a.__proto__的对象下找,以此递归寻找。
而你给a.name赋值时,它不会修改a.__proto__上所对应的name,而是直接在a对象下新建一个name
第一个例子中你原型链上的info并没有被改变,它的info实际上是指向

{
    name: 'peter',
    age: 25
}

这个对象的,而你用a.info.name='jack';的时候实际上在a.__proto__上找到了info,然后在info里找到了nameinfo不能被修改但是info所指的对象能被修改,所以就出现了问题中的现象。
你可以将a.info='jack',然后去看fun.prototype.info,你就会发现info并没有被修改。

对对象的属性进行修改,会shadow其所委托到的原型上的同名属性,就像第二个所示,但是第一个的本质不是在操作你的新对象,是在操作原型上的info属性

其实这个问题和原型链无关,和类型赋值有关。object是做一个引用的copy,基本类型是做值的copy。其实就是相当于下面的代码:

var a = { v: 'a' }
var b = a
var c = a
b.v = 'b'
console.log(a.v) // 'b'
console.log(b.v) // 'b'
console.log(c.v) // 'b'
c.v = 'c'
console.log(a.v) // 'c'
console.log(b.v) // 'c'
console.log(c.v) // 'c'
var a = 'a'
var b = a
var c = a
b = 'b'
console.log(a) // 'a'
console.log(b) // 'b'
console.log(c) // 'a'
c = 'c'
console.log(a) // 'a'
console.log(b) // 'b'
console.log(c) // 'c'

类似的,原来的代码改成这样看看:

var fun = function () {}
fun.prototype = {
    info: {
        name: 'peter',
        age: 25
    }
}

var a = new fun()
var b = new fun()

a.info.name = 'jack'
console.log(fun.prototype.info.name) // jack
console.log(a.info.name) // jack
console.log(b.info.name) // jack
b.info.name = 'tom'
console.log(fun.prototype.info.name) // tom
console.log(a.info.name) // tom
console.log(b.info.name) // tom

a.name="jack"//赋值操作,a本身没有这个属性,那么创建并覆盖原型上继承的name属性
console.log(a.name)//获取属性的值,但是a身上没有的话,就往上查找
a.info.name="ww"
分为两步(a.info)//a没有,往上查找,原型上有{},获得对对象的引用
(a.info).name=""//修改这个引用的对象的值
这是我的理解。

因为a.info和b.info本身是两个东西,但是指向同一个对象,a.info.name和b.info.name修改的是那个对象的属性。

后面那个a.name和b.name也是两个东西,所以修改任意一个,不会影响另一个

这样想就好了,给实例ab添加属性,他会覆盖原型链中的同名属性。
但是第一个他是给啊a.info和b.info这两个实例添加属性啊。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏