一道簡單的js繼承面試題來查考你是否真的透徹的瞭解繼承

直接先貼題目吧數組

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的指向問題

相關文章
相關標籤/搜索