1. 程式人生 > >二,3分鐘快速理解js中的【原型 / 原型鏈】。

二,3分鐘快速理解js中的【原型 / 原型鏈】。

二,3分鐘快速理解js中的【原型 / 原型鏈】。

原型/原型鏈前端面試高頻出現,極為重要!!!

相信大名鼎鼎的JS三座大山【原型/原型鏈】,【閉包/作用域】,【非同步/單執行緒】在前端童鞋中是無人不知無人不曉了。曾經學習JS的時候看了N多篇相關的文章,根本記不住什麼是原型/原型鏈,一大堆亂七八糟的東西擾亂視線。今天寫一下自己對原型/原型鏈的理解,如有不對之處,還請各位童鞋指出。

要理解JS中的【原型/原型鏈】不能從字面意思去理解,不然很容易迷糊。咱們先不要管概念,直接講原理;
回顧一下上一篇中寫到了JS的【變數型別】,回顧一下js的變數型別分類【值型別】和【引用型別

(1)值型別:字串(String)、數字(Number)、布林值(Boolean)、Null、Undefined

(2)引用型別:物件(Object)、陣列(Array)、函式(Function)

值型別引用型別有什麼區別?

1).每一個值型別都有一個獨立的記憶體區域儲存自己的值,呼叫它的時候呼叫的是他的值,而引用型別呼叫的是記憶體中的地址。

下面看例子1:
var a = 1, b = a;
console.log(b) // 1
b = 2; console.log(a) // 1改變b的值再列印a的值我們看看
這裡不管怎麼改變b的內容,a永遠不會被b影響,a的值永遠是1;因為a和b都有一個獨立的記憶體區域儲存自己的值,呼叫它的時候呼叫的是他的值。

再看例子2:
var a = {x:1,y:2}, b = a;

宣告一個變數a,再宣告一個變數b賦值為a
console.log(b); // {x:1,y:2}列印b,發現b和a的內容一樣
console.log(a===b) // true確認一下,a絕對等於b
b.x = 3, b.y = 4; console.log(a);改變b的值,再列印a,奇蹟出現了
console.log(a); // {x:3,y:4}
我們改變b的值這時候a竟然也改變了?什麼情況?
之前提到了引用型別呼叫的是記憶體中的地址而不是呼叫它的值,所以你第一次宣告一個引用型別的時候(宣告a)會給a開闢一塊記憶體空間用來存放a,再宣告b=a的時候不會再次開闢一塊記憶體空間,而是直接把變數b指向到了存放a的記憶體空間中,此時a和b公用一塊記憶體空間;沒錯,JS因為一些設計上的原因它就是這樣的機制。
這裡把a換成陣列或者函式,只要屬於引用型別都是這種機制。

當然還有一個誤區:
var a = {x:1,y:2}, b = a;
b = {x:3,y:4};
console.log(a) // {x:1,y:2}
為什麼這裡a的值就不會改變了呢?因為你沒有去改變b的值,而是給b重新賦值。這時候相當於給b重新開闢了一塊記憶體空間而不是改變b和a公用的記憶體空間裡的內容。這就是值型別引用型別區別之一

那麼他們還有什麼區別呢?有!
所有的引用型別都有一個__proto__屬性(proto前後各兩個下劃線),我們稱之為【隱式原型

從上圖可以看出,我們宣告一個變數a為一個物件(引用型別)的時候它會多出一個屬性__proto__

再看第二張圖
在這裡插入圖片描述
我們聲明瞭物件,陣列和函式,發現函式有一個prototype屬性而物件和陣列沒有。因為所有的函式都有一個prototype屬性,我們稱之為【顯式原型】;這裡函式也是引用型別,所以函式既有顯式原型又有隱式原型。這就叫做原型;那麼什麼是原型鏈呢?看第三張圖
在這裡插入圖片描述
我們寫一個建構函式P,然後在P的顯式原型上設定一個屬性name,值為字串’czj’
然後new一個建構函式P的例項賦值給變數a,再列印一個a的name屬性,結果是’czj’。很奇怪對不對,我們明明沒有設定a的name屬性。這是為什麼呢?這裡涉及到兩個JS的設計規則

一.)就是一個函式的隱式原型__proto__指向它建構函式的顯式原型prototype
所以a.__proto__ === P.prototype // true

二.)當JS在一個函式中找一個屬性或者方法沒有找到的時候就會去那個函式的原型中去尋找。
當我們列印a.name的時候a本身沒有name屬性所以會去a.__proto__屬性中去尋找,又因為函式的隱式原型__proto__絕對等於它建構函式的顯式原型prototype所以a.__proto__ === P.prototype,而我們之前在P.prototype中設定過name屬性,所以a.name === 'czj'。就這樣函式a有原型P,那麼P呢?P.__proto__ === Function.prototype // trueP的建構函式其實是Function。就這樣函式a的建構函式是P,P的建構函式是Function物件new出來的,而他們又各自都有各自的原型,那麼就形成了一條原型鏈。這就叫做原型鏈!

另外提一點,值得注意的是原型鏈並不是無限存在下去的,原型鏈的頂端是Object.prototype
其實還有很多如instanceof, constructor跟原型鏈的關係沒有寫,這個留到以後的文章中再說吧。

下一篇寫一下【閉包/作用域