淺談JS原型

JS原型學習筆記,若有錯誤,還請留言~~node

  • 全局對象window
  • 什麼是原型
  • JS中一切皆爲對象?

全局對象window

在談window以前,試想一個簡單的問題,打開瀏覽器在控制檯輸入console.log("Hello"); 並按下回車鍵,咱們理所應當看到了控制檯給咱們返回的結果Hello,那麼,console.log()這個方法是怎麼來的呢?其實瀏覽器中已經爲咱們內置了一個全局對象叫作window,window做爲全局對象,表明腳本正在運行的瀏覽器窗口。console.log()是一種簡寫的格式,更加精確的寫法爲:window.console.log()。window下則封裝了多種 多樣的屬性,這些屬性中,有些是瀏覽器自帶的屬性,有些則是ECMAScript標準規定的屬性。ECMAScript規定的全局對象爲global,可是在瀏覽器下則是window。
數組


這裏須要注意的是,瀏覽器中的全局對象是window,可是在其餘的環境下,例如node.js就不同了。瀏覽器下,在控制檯輸入 window能夠查看全局對象的全部屬性。而在node.js環境中,則須要使用 global,且瀏覽器中規定的屬性在node.js環境下天然是無效的。
當咱們打開瀏覽器,系統天然會爲瀏覽器分配內存,瀏覽器將內存分給各個頁面,頁面中的HTML,CSS,JS,插件等會分配到一部份內存空間。對於JS來講,一開始window對象就已經存在於堆內存中了。

什麼是原型

先上圖:
瀏覽器


這是堆內存中window全局對象的部份內存模型圖,紅色框部分爲函數,黑色框部分爲prototype對象,先無論函數中的prototype屬性以及對象中的__proto__屬性的含義。咱們先聲明一個對象,並調用 toString()方法,看一看內存中到底會發生什麼。

var obj = new Object();
複製代碼


咱們看到咱們在聲明瞭一個空對象以後,內部自帶了一個__proto__屬性,且使用命令: obj.__proto__ === Object.prototype 結果會返回 true

那麼也就是說:


obj對象的__proto__屬性指向了Object.prototype這樣一個對象。咱們來看一看Object.prototype裏面有什麼。


在Object.prototype中的toString屬性指向一個函數,偏偏是咱們想要使用的toString()這樣一個方法。如今咱們在大概能夠明白:在咱們聲明一個對象obj時,這明明是一個空對象,可是這個對象卻能夠調用toSting(),valueOf()等方法,是由於在咱們聲明對象時,瀏覽器爲咱們這個對象添加了一個屬性 __proto__,這個屬性指向了一個 Object.prototype這樣一個對象,Object.prototype對象中有着Object所共有的一些屬性。其實這個prototype就是原型,比起原型,我更喜歡叫它共有屬性對象。那麼 __proto__是什麼呢? __proto__是瀏覽器自動賦予 對象的一個屬性,這個屬性指向着一個函數的原型,固然最後的root必然是Object.prototype。
例如:

var n = new Number(15);
n.toString(16);// "f"
複製代碼

上述代碼的含義是,將數值類型的n轉化爲16進制後,再將其轉化爲字符串,15在16進制中對應的值爲f,轉化爲字符串以後的結果爲"f"。很顯然,Object原型中的toString()方法是沒有辦法將一個值按照進制轉化,再變爲字符串的,也就是說,Number類型的toString(),不一樣於Object原型的toString()方法。假若是Java,咱們會想到方法的重寫,可是JS則不同,咱們繼續從內存模型的角度來分析到底發生了什麼:
bash


首先 var n = new Number(15); n爲一個對象,若是不明白爲何n的值會在堆內存中,能夠參考個人文章 JS內存模型。咱們能夠看到,對象n的 __proto__指向了Number.prototype,在Number.prototype中有什麼呢?


咱們能夠看到,Number.prototype也有一個toString的屬性,指向了toString()這樣一個函數。而且


Number.prototype.toString === Object.prototype.toString返回的結果爲false,也就是說,Number.prototype中的toString是一個「重寫」的屬性,當咱們聲明瞭對象n,並調用toString()方法時,首先,瀏覽器會在n這個對象中尋找toString這個屬性,若是沒有它就會在n這個對象的 __proto__屬性所指向的原型中尋找, n.__proto__指向了 Number.prototype。 由於Numebr.prototype即Number原型也是一個對象, Number.prototype.__proto__則指向了root即 Object.prototype。若是在Number原型中沒有toString這個屬性,那麼順其天然地就會在Object原型中尋找這個屬性。固然本例中,在Number的原型中 Number.prototype找到了toString這個屬性,天然會調用它所指向的Number獨有的toString()方法。剛剛描述的過程當中,這種指向的關係好似 鏈表,而在JS中,咱們能夠形象地稱做爲「 原型鏈」。
接下來,咱們再思考一個問題: 函數是對象嗎 ?雖然 typeof一個函數返回的結果爲「function」,可是咱們一再強調JavaScript裏的數據類型只有七種,不管是數組仍是函數,它們的本質都是對象。既然明確了函數是一個對象,那麼在上文中,我曾經暗示prototype是一個函數所擁有的屬性,__proto__是一個對象所擁有的屬性,那麼函數既然是對象,那麼換言之,一個函數中天然擁有兩種屬性了。咱們再將內存模型圖完善一些:


在上圖中咱們看到,若是一個函數做爲一個函數而言,它自身的屬性爲prototype,這個屬性指向它所對應的原型。若是將一個函數做爲對象來看,它的屬性__proto__則指向了Function.prototype,這種指向的關係仔細想想也合情合理,一個函數會提供本身的共有屬性,可是一個函數本質上也是一個對象,它被構造出來須要一個構造函數。值得一提的是 Funcion.__proto__ === Funciton.prototype。 這能夠形象地理解爲:本身造本身,本身賦予本身了屬性。其實理解不了上圖也可有可無,咱們只須要記住原型裏面的一個規則便可:

內置函數.__proto__ === Function.prototype;
複製代碼

一切皆爲對象?

JS中一切皆爲對象?很顯然這句話是錯的,用最簡單的代碼就能夠證實:函數

var n = 15;
typeof n;// "number"
複製代碼

JS中,有七種數據類型,在上面的代碼中,咱們聲明瞭var n = 15;,在使用typeof n時,咱們也能夠看到返回的類型是number。可是,咱們卻能夠這樣作:post

n.toString(16);// "f"
複製代碼

和上面介紹的var n = new Number(15)聲明n的方式不一樣。n的typeof 返回的是一個「number」,若是使用var n = new Number(15)聲明n,那麼使用typeof n返回的結果就會是「object」。在聲明一個對象時,瀏覽器會爲這個對象自動添加__proto__這樣一個屬性指向原型,好調用相應的方法,既然咱們聲明瞭var n = 15;且能夠調用toString()這樣一個方法,那不仍是說明n其實是一個對象嗎?實際上,並非這樣的,n天然是一個number類型的數字,只不過這裏面另有蹊蹺。學習

var n = 15;
n.toString(16);
複製代碼

當咱們調用toString()方法時,在堆內存中會生成一個臨時對象,咱們暫時叫它temp。也就是說這個過程是這樣的:spa

var n = 15;
// 調用toString()方法時,會在堆內存中產生一個臨時變量temp
// var temp = new Number(n);
// 將temp.toString(16)的結果記錄下來
// 臨時變量temp隨即被"抹殺掉"
複製代碼

一個數值型,字符串型等普通類型的變量能夠調用原型中的方法,並不能說明它們類型的本質是對象,由於「建立臨時變量機制」,讓許多人對此有了誤解,實際上數值型便是數值型,字符串型是字符串型,JS當中一切皆對象很顯然是一個謬論。再看一個例子:prototype

var a = 1;
// 問:執行此條語句 a.xxx = 2 是否會執行成功? 
複製代碼

其實只要理解了"臨時對象"這個概念,就不難回答這個問題,在聲明var a = 1;後,咱們已經肯定了a的類型是Number,在執行語句a.xxx = 2;時,在堆內存中會生成一個臨時對象,對於這個臨時對象來講,執行a.xxx = 2;就至關於添加了一個屬性xxx其值爲2,因此這條語句天然能執行成功。固然這個臨時對象會馬上被「抹殺」,當咱們再次在控制檯輸入a.xxx查看這個值時,返回的結果天然是undefined。
插件

相關文章
相關標籤/搜索