在基礎面前,一切技巧都是浮雲。javascript
要求寫出控制檯的輸出.html
function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = { demo: 5 }; this.show = function () { console.log(this.a , this.b , this.c.demo ); } } function Child() { this.a = 2; this.change = function () { this.b.push(this.a); this.a = this.b.length; this.c.demo = this.a++; } } Child.prototype = new Parent(); var parent = new Parent(); var child1 = new Child(); var child2 = new Child(); child1.a = 11; child2.a = 12; parent.show(); child1.show(); child2.show(); child1.change(); child2.change(); parent.show(); child1.show(); child2.show();
prototype
屬性,指向構造函數的原型對象,實例會共享同一個原型對象;__proto__
指向構造函數的原型對象;this
的指向問題,常見的狀況包含以下幾種:
window
new
操做符生成實例時,構造函數中的this指向實例call
和apply
方法中,顯示指定this
的綁定爲指定上下文Object.prototype
和null
,若是實例自身及整個原型鏈都不存在所查找的屬性則返回undefined
基本沒什麼可解釋的。
直接取值就能得出答案1 [1,2,1] 5
;前端
Child
的構造函數本來是指向Child
的
題目中顯式將Child
類的原型對象指向了Parent
類的一個實例,這是javascript面向對象編程中常見的繼承方式之一。此處須要注意Child.prototype
指向的是Parent
的實例parent
,而不是指向Parent
這個類
直接在控制檯操做輸出答案可得11 [1,2,1] 5
java
此處使人迷惑的是this.b指向的數組最後一列爲何是
1
而不是11
?編程
先來看一下child1的樣子:
數組
當執行child1.show()
這個方法時,因爲child1
做爲Child
的實例,是擁有a這個屬性的,因此show()
方法中的this.a
會直接指向這個屬性的值,也就是11
,而不會繼續沿原型鏈取到__proto__
所指的對象上的a屬性;架構
接着尋找this.b
,因爲child1
是沒有b這個屬性的,因此會沿原型鏈取到parent
上的b屬性,其值是一個數組,前2項是常量沒什麼好說的,數組的最後一項是一個引用,而此處的指針並非一個動態指向,由於在new Parent()
這一步的時候它已經被執行過一次,肯定指向了parent.a
所指向的資源,也就是child1.__proto__
中的a屬性所指向的資源,即數值1。app
須要注意的是:框架
1.從代碼上看,
child1.__proto__.b
數組的第三項是指向child1.__proto__.a
的,那咱們此時修改child1.__proto__.a
的值,是否會影響child1.show()
的結果呢:
答案是木有影響,爲何看起來指向同一個地址的屬性卻出現值不同的情形?由於parent
實例生成的時候,this.a
指向了一個原始值2,因此this.b
中的第三項其實是被賦值了一個原始值,故此處乍看起來像是引用類型的賦值,實則不是。原始值賦值會開闢新的存儲空間,使得this.a
和this.b[2]
的值相等,可是卻指向了堆內存裏的不一樣地址。更多詳細解釋能夠參見【擴展閱讀】中推薦的博文。函數
2.那怎樣讓
child1.__proto__.b
數組的第三項也輸出11
呢?
Parent
類定義中,b屬性數組的第三項是指向a屬性的值的,意味着在Parent
實例化以前這個引用是動態指向的,因此只要在Parent
實例化以前改變類定義中this.a
的值,就能夠達到想要的效果,若是在Parent
已經實例化,則只能顯式修改*.b[2]
這個屬性的值。get/set
方法,是的每當a屬性的值發生變化時,同步修改b[2]
的值,代碼和運行結果以下所示:若是理解了上面的解釋,那麼此處同理便可得出答案:12 [1,2,1] 5
接着代碼執行了: child1.change(); child2.change();
parent
是一個Parent
類的實例,Child.prorotype
指向的是Parent
類的另外一個實例,二者在堆內存中是兩份資源,互不影響,因此上述操做不影響parent
實例,
輸出結果保持不變:1 [1,2,1] 5
;
child1
執行了change()
方法後,發生了怎樣的變化呢?
this.b.push(this.a)
因爲this的動態指向特性,this.b會指向Child.prototype
上的b數組,this.a會指向child1
的a屬性,因此Child.prototype.b
變成了[1,2,1,11];
this.a = this.b.length
這條語句中this.a
和this.b
的指向與上一句一致,故結果爲child1.a
變爲4;
this.c.demo = this.a++
因爲child1
自身屬性並無c這個屬性,因此此處的this.c
會指向Child.prototype.c
,this.a
值爲4,爲原始類型,故賦值操做時會直接賦值,Child.prototype.c.demo
的結果爲4,而this.a
隨後自增爲5(4 + 1 = 5).
接着,child2
執行了change()
方法, 而child2
和child1
均是Child
類的實例,因此他們的原型鏈指向同一個原型對象Child.prototype
,也就是同一個parent
實例,因此child2.change()
中全部影響到原型對象的語句都會影響child1
的最終輸出結果
this.b.push(this.a)
因爲this的動態指向特性,this.b會指向Child.prototype
上的b數組,this.a會指向child2
的a屬性,因此Child.prototype.b
變成了[1,2,1,11,12];
this.a = this.b.length
這條語句中this.a
和this.b
的指向與上一句一致,故結果爲child2.a
變爲5;
this.c.demo = this.a++
因爲child2
自身屬性並無c這個屬性,因此此處的this.c
會指向Child.prototype.c
,故執行結果爲Child.prototype.c.demo
的值變爲child2.a
的值5,而child2.a
最終自增爲6(5 + 1 = 6).
接下來執行輸出命令,最終結果將輸出:
child1.show():5 [1,2,1,11,12] 5
child2.show():6 [1,2,1,11,12] 5
this.c.demo = this.a++
出錯,本覺得這裏會傳引用,但實際是傳了值,分析後明白由於this.a
指向的是一個原始值,故此處至關於將原始值賦值給對象屬性,因此賦值後child.c.demo
的值不會再受到child.a
的變化的影響。若是child.a
是一個引用類型,那麼結果會變成什麼樣子呢?child.a
指向一個對象(即引用類型):Child.prototype.c
的值會隨着child1.a
的變化而變化,由於此時child1.a
的值是一個引用類型,賦值過程會使得Child.prototype.c
和child1.a
指向同一份資源的內存空間地址。對於原始類型和引用類型更詳細的解說,能夠參考篇尾擴展閱讀中的博客。1.基礎知識原本就是零散的細節,必須本着死磕到底的心態進行學習。
2.基礎知識是最枯燥的,也是真正拉開人和人之間差距的東西,也是你想進入大廠必需要跨過的門檻,重要卻不緊急。一樣是菜鳥,有的人3-5年後成爲了前端架構師,有的人3-5年後還在用層出不窮的新框架給按鈕綁事件,想成爲怎樣的人,就要付出怎樣的努力,大多數時候都是沒毛病的。基礎很重要!很重要!很重要!
3.基礎這個東西是要不斷看的,像紅寶書(javascript高級程序設計)
和犀牛書(javascript權威指南)
這種書,最好多過幾遍,一些難以理解的現象,每每是因爲對底層原理理解不到位形成的,買來新書直接用來墊高顯示器你不心疼的嗎?喜馬拉雅上有一個免費的陪你讀書系列節目,30多期的音頻通篇講解了紅寶書的內容,對不喜歡看書的童鞋絕對是一大福音。