計算機語言能夠分爲三類,機器語言、彙編語言、高級語言。高級語言又能夠簡單分爲解釋類和編譯類。這個知道就夠了。編程
機器語言: 計算機所能識別的二進制語言,通常也不會直接拿來用於編程,沒法理解且難以記憶 彙編語言: 底層程序能夠直接理解的指令,通常是英文縮寫,通常簡短、簡單(功能簡單),只能作一些很是細微的操做,複雜的操做每每伴隨着大量的指令,我等通常接觸很少 高級語言: 簡單理解,咱們日常接觸到的據說過的基本都是高級語言。高級語言又分爲解釋類和編譯類。解釋類語言和編譯類語言都須要轉換成機器語言才能被機器執行,區別在於轉換的時間,解釋類語言也就是通常通稱的腳本語言,依賴於解釋器,邊解釋邊執行,編譯類是所有轉換(編譯)好了,打包拿出去執行。JavaScript不須要編譯,可是須要在本身的解釋器上執行,好比咱們經常使用的瀏覽器。
有過C語言基礎的人必定據說過這麼一句話,指針是C語言的靈魂。可是彷佛在JavaScript中,並無接觸過所謂的指針的概念,但JavaScript的解釋器(JavaScript引擎)無論用什麼編寫,有的是C,有的是C++,也或者是Java,甚至是JavaScript自舉實現的JavaScript解釋器(元循環虛擬機),都逃不脫內存和指針,在JaVaScript中也有體現。因此瞭解下這些概念,對於學習JavaScript,總歸是有好處的。瀏覽器
計算機內存最小單位是位,8位一個字節(byte),實際計算機中,內存最小單位是字節(byte),通俗來說,能夠把內存條當作內存,計算機運行時臨時存儲數據的地方。也能夠把它看作一個大的圖書館,有無數個格子,每一個格子都有一個編號,這個編號就是地址,每一個地址對應惟一一段內存。咱們一般所說的系統爲數據分配一段內存
,也就是給這個數據分配格子,一個格子放不下,那就給分三五個格子來放。而指針則是固定佔用4個格子(4byte),裏面放的不是真實的數據,而是放的另一段內存的地址,只是個地址。函數
Undefined、Null、Boolean、Number、String和Symbol(ES6)學習
原始數據類型實在太簡單,因此只簡單劃幾個重點:指針
原始數據類型不可改變 原始數據類型一個很是重要的特徵就是不可改變
。原始數據類型是保存在棧中的,一旦聲明一個變量 var a = "老李";
解釋器會申請一塊內存來存儲"老李"
,a是標識符(標識符存儲的是內存地址,但在通過解釋/編譯後會就不須要了),若是此時其餘操做嘗試修改a,好比a += ",你好!";
,此時打印a,雖然會輸出"老李,你好!";
可是實際這其中改變的是a的地址指向,解釋器會爲"老李,你好!"
申請一片新的內存來存儲,a中存儲的指向改變了,原先的"老李"
所佔用的內存會被回收但自己並未被改變。code
原始數據類型是按值傳遞 原始數據類型的賦值,也是新開闢一片內存地址來存放新的指針和值,好比:var b = a;
雖然看起來b和a同樣,但實際上b和a一點點關係都沒有,各自有各自的標識符和佔用的內存,只是恰好值相同。ip
Object
這裏的Object不是狹義的Object,包含JavaScript的預約義引用類型:Object、Array、Date、Boolean、Number、String
,對,這些都是引用數據類型。也就是一個數字/日期/布爾值/字符串他們的字面量聲明方式和構造函數聲明方式會形成徹底不同的結果。也就是上面劃的兩個重點,這裏須要從新劃一下。內存
引用數據類型能夠改變 我想說的不是這句廢話,引用數據類型和原始數據類型存儲在 棧和堆中,棧中存儲的是標識符和指針
,堆中存儲的纔是實際地址。畫個表(表也懶得畫了,下面的湊活看)吧:字符串
var a = 1; var a1 = a; var b = {value: 1}; var b1 = b; var c = new Object(1); var c1 = c; var d = new Number(1); var d1 = d; 棧 堆 a 1 無 a1 1(和a的1佔用的不一樣的內存) 無 b 指針,{value: 1}所佔的內存地址 {value: 1} b1 指針,指向和b相同,但指針自己存儲在新的內存中 同上,且是同一塊內存中的相同的值 c 指針,1所佔的內存地址 1(object) c1 指針,1所佔的內存地址,但指針自己存儲在新的內存中 同上,且是同一塊內存中的相同的值 d 指針,1所佔的內存地址 1(number) d1 指針,1所佔的內存地址,但指針自己存儲在新的內存中 同上,且是同一塊內存中的相同的值
簡單講,原始數據類型,永遠是一個標識符對應一個值(一段內存)且都存放在棧裏;引用數據類型永遠是一個標識符對應一個指針指向同一個值(同一段內存),前兩者存儲在棧中,最後一個在堆中。來幾個例子幫助理解下:虛擬機
var a = 1; var a1 = a; a1 = 2; console.log(a, a1); //1 2
這個很好理解你們各自有各自的標識符和內存地址,你變你的,不影響我。下一個例子:
var b = {value: 1}; var b1 = b; b1.value = 2; console.log(b.value, b1.value) //2 2
也好理解,b和b1指向的是同一個東西,簡單理解就是b和b1共用同一個東西。b1改完了b再來訪問,拿到的確定是改後的。下一個例子:
var c = new Object(1); var c1 = c; c1 = 2; console.log(c, c1) //{[[PrimitiveValue]]: 1} 2 PrimitiveValue就是指的原始值,這裏其實只是將原始值1包裝成Object,而這個原始值1依然不可更改
也很簡單,c1 = 2;這句話從新聲明瞭c1,表明着指針的c1被回收了,c1被從新聲明爲一個原始數據類型,這時候兩者不要緊了,若是是換一個方法,好比:
var c = new Object(1); var c1 = c; c1.value = 1; console.log(c, c1) //{value: 1,[[PrimitiveValue]]: 1} {value: 1,[[PrimitiveValue]]: 1}
這樣就沒問題了。
原始數據類型是按值傳遞的,引用數據類型是按引用傳遞的,看例子:
var a = 1; var b = {value: 1}; function foo(v){ v = "new Value"; } foo(a);
在函數調用時,其實進行了以下操做:
將a賦值給v,也就是v = a;
,建立了a的副本,此時v獲得了本身的內存,其中存放着1這個值,而後拿"new Value"去覆蓋1,然而這一切發生在a的副本上,和a徹底沒有關係,因此此時打印出a,仍是1。接着:
foo(b);
這個結果也很好猜,b也沒有變,一樣建立一個副本,副本有標識符、指針,指針指向的也是b的指針指向的值,可是v = 」new Value「;
執行時,這個副本就被從新賦值位一個string了,原先那個指針被回收。接着:
var a = 1; var b = {value: 1}; var c = new Number(1); function foo(v){ v.value = "new Value"; } foo(a); foo(b); foo(c);
a依然是1,且不說原始數據不能被添加屬性,即使被添加也只是a的副本的事,和a沒一點關係;
b在函數執行中一樣建立了副本,副本一樣有指針,指向了{value: 1}
所在的實際地址,此時經過指針改變了存儲`{value:
1}`的內存中的value值,因此此時再經過b去訪問,同樣是輸出改變後的值;
c和b原理同樣,只不過c原本沒有value屬性,因此會被添加一個value屬性,因此上面的abc結果分別是:
console.log(a); // 1 console.log(b); // {value: "new Value"} console.log(c); // Number {value: "new Value", [[PrimitiveValue]]: 1}