js关于原型链一段代码的疑问?

var a=function(){
//empty
}
a.prototype.var1=[1,2,3];
var b=new a();
b.var1.push(4);
var c=new a();
alert(c.var1.join(","))

最后输出的是1,2,3,4 非常不解?
给实例b push的4,
为什么实例c 会输出4 ,又不是b.prototype.var1.push(4);
话说为什么改成b.prototype.var1.push(4);
会爆一个Uncaught TypeError: Cannot read property 'var1' of undefined(…)

阅读 4.1k
9 个回答

第一个问题,因为b实例(也就是new a(),也可以说是a的实例)上并没有var1属性,b.var1b上找不到var1这个属性,就去b的父原型——a.prototype上找,找到了,因此b.var1实质上是指向a.prototype.var1。你在b.var1作修改对象的操作(如数组的push或者添加、删除属性)都是直接操作在a.prototype
第二个问题,.prototype是函数上的属性(这里特指构造函数),实例上是没有prototype属性的;如果要在b的父原型上进行操作,应该:

a.prototype.var1.push(4)
// 或者
b.__proto__.var1.push(4)

但这样都会影响到a的新实例(也就是c),如果想每个实例都有自己的var1数组,可以:

// 在构造函数内指定,覆盖父原型上的属性
var a = function() {
    this.var1 = [1, 2, 3]
}

// 或者重新给实例复制一个数组
b.var1 = b.var1.slice()

我觉得,小b是实例a出来的,所有b.var1的时候,就通过原型查找到了在小a原型上的var1这个数组,他们是同一个堆内存,内存地址在一样的,等于是(小a和小b是指向的同一个命名空间的内存地址),那么再实例化一个小c,同样也指向了这个内存地址,所以小c.var1也通过原型链查找到了var1这个空间,join以一个逗号分割每一项。 你后面的问题,b.prototype.var1.push(4);在小b的原型上是没有var1这个属性名的,所以会报错,说不能读取到var1,是没定义的! 希望能对你有帮助!

var a=function(){
//empty
}
a.prototype.var1=[1,2,3];
var b=new a();
b.var1.push(4);
console.log(b.__proto__.var1)
var c=new a();
console.log(b.__proto__.var1 === c.__proto__.var1)
console.log(b.__proto__.var1 === a.prototype.var1)

var b=new a() 所以 b.__proto__指向a.prototype
你直接b.var1会先找b上面有没有var1变量,再找a.prototype上面有没有这个变量,由于指向同一个变量,所以b和c改了会相互影响。

a应该写成A,表明它是一个'类',A.prototype.var1 = [1,2,3]中,var1是个类变量

实例修改了类变量,必然会影响到其他的实例

b.prototype.var1.push(4);会报错是因为b是类a实例化后的一个对象,b并不是一个构造函数,此时b并没有显式的prototype属性

prototype只是a构造函数的属性,你b.prototype是错的,为什么c输出4原因是原型对象特点就是共享,所以你修改了b,在c上也会体现。你可以看看《js高级程序设计》第158页的原型对象问题那一块,例子差不多的

LZ可以看看我的这篇文章https://segmentfault.com/a/1190000003017751

因为他们在内存中都是指向同一个prototype,目的是减少内存开销

图片描述

如果所示,b 和 c 都是通过 构造函数 a new 出来的实例。

推荐问题
宣传栏