直接先贴题目吧数组
function A() { this.name = 'a' this.color = ['green', 'yellow'] } function B() { } B.prototype = new A() var b1 = new B() var b2 = new B() b1.name = 'change' b1.color.push('black')
console.log('b1', b1) 函数
console.log('b2', b2)this
console.log(b1.name) // change
console.log(b2.name) // a
console.log(b1.color) // ['green', 'yellow', 'black']
console.log(b2.color) // ['green', 'yellow', 'black']spa
这其实就是咱们的常见继承模式之一,原型继承,为什么会出现这样的状况呢?
最大的疑惑是,为什么两个实例对象b1,b2里面的color属性都被修改了?
又为什么b1.name = change 而b2.name 却没有发生改变。
首先,你得有原型链继承的知识点,有了这个知识点后,咱们再理解下咱们常常挂在嘴边的基本数据类型和
引用数据类型,他们的存储方式和读取方式有何异同,带着这一些疑惑,咱们再看下栈内存和堆的概念prototype
看了上图,一目了然的看到,无论是基本数据类型仍是引用类型,实际都是存在栈内存的,只不过引用数据类型还会指向一个具体的堆。他们的区别我简单就阐述这么一些。翻译
再剖析上面的代码是如何执行或者指向的。
首先:b1和b2实例化是存在栈内存里面,而后指向了堆内存的两个对象。但这两个对象的原型指向了同一个实例对象,这个实例对象一样是存在栈内存中的(而后指向了一个对象)。因此简而言之,b1和b2的原型对象指向了同一个实例对象(A的实例对象)。code
b1.name = 'change'
实际是在b1的实例对象增长一个属性name,并将name属性赋值为change,但它并无修改原型链上的name属性。对象
b1.color.push('black')
这里涉及到原型链向上查找属性的知识点,实例对象b1里面并无color属性,因而去原型链上寻找,结果在A的实例对象找到了color,但此时的color属性指向的是一个引用类型,而b1和b2都继承于A这个实例对象,根据引用类型color指向一个堆数组来看,当b1修改了原型上的color属性,实际也就修改了b2上面的color属性。再强调下,由于color属性是原型对象上的一个引用类型属性,指向了同一个数组对象blog
那么如何规避color属性被指向同一个引用类型的问题呢?
实际咱们上面就是运用到继承里面的一个原型链继承的方法。
还有一种继承是构造函数继承
稍微修改下:继承
function B() { A.call(this) }
通过改动后,咱们每次在实例化B的时候,会将实例化对象的引用对象做为参数传递到B这个构造函数,在间接调用A函数的时候,也修改了A执行的时候this的执行问题。此时this的执行再也不是window而是实例化的对象b1或b2.
分析下,咱们不写A.call(this)这句代码的时候,A的实例对象实际就是指向A实例对象的自己,这句话理解起来有点傲,执行了A.call(this)后,这里翻译过来我想应该是这样的,不知道是否有错?
调用A,而不是实例化A,即A(),但此时A的this并非window对象,这里没有实例A因此,this不是A实例化指向的那个引用对象。而后咱们认为的修改了A这个函数执行时执行上下文的this执行,这个this此时成了b1或者b2实例对象。
此时咱们再修改b1.color的时候,一样会去原型上去找color熟悉,但此时原型链上的this已经指向了b1这个实例化对象,因此当咱们修改b1.color的时候,实际只修改了b1这个实例化对象对应原型上的那个对象。而b2.color并不会被改变。侧重理解this的指向问题