目錄
1. 數據類型
JS的數據類型分
- 基本類型(String、Number、Boolean、Null、Undefined)
- 引用類型
屬性 | 基本類型 | 引用類型 |
---|---|---|
定義 | 簡單的數據段 | 可能有多個值構成的對象 |
值 | 可以操作保存在內存中的實際值 | 保存在內存中的對象,按引用訪問 |
動態屬性 | undefined | 可以添加屬性/方法(people.name) |
2. 變量的創建
變量:將值賦給某個東西,這個東西就叫~
- 靜態類型(強制類型):聲明一個變量存放指定類型的值。eg:java
- 動態類型(弱類型):允許一個變量在任何時刻修改成任何類型的值。eg:javascript
TypeScript 相對於 JavaScript ,提供了靜態類型,提高程序正確性。(但筆者認爲,如果不出現類型錯誤的情況下,動態類型在任何時刻反而更加靈活)
大概說說JS引擎進行編譯的步驟
- 分詞/詞法分析(eg:var a =1會分解成var、a、=、2,這些代碼塊稱詞法單元)
- 解析/語法分析(將詞法單元流【數組】轉換成一個由元素逐級嵌套所組成的代表了程序語法的結構樹即 “抽象語法樹”【AST】)
- 代碼生成(將AST轉換成可執行代碼的過程)
a. 引擎每一次遇到聲明,它就把聲明傳到作用域創建一個綁定。並且對每個聲明的變量進行分配內存,默認值爲undefined。
// 聲明一個名字a,值爲1
let a = 1
創建變量過程
- 創建了一個名字a,用來保存後面的值
- 設置變量a的作用範圍(作用域)
- 在預處理階段會被提到函數頂部,此時a爲undefined【聲明提升–變量都是undefined,函數則是該方法,其他會報錯XX is not defined】
- 最後區分不同數據類型進行賦值:
a. 基本類型把值直接保存在變量中
b. 引用類型把對象保存在計算機內存中,同時創建訪問內存的地址即引用,把引用保存到變量中
總結:變量的賦值操作會執行兩個動作,首先編譯器會在當前作用域中聲明一個變量(如 果之前沒有聲明過),然後在運行時引擎會在作用域中查找該變量,如果能夠找到就會對 它賦值。
3. 不同類型變量複製原理
基本類型變量複製原理
對於 基本類型的變量來說,兩個變量之間可以 參與任何操作而不相互影響。
存在棧中。
let a = 6
let b = a
b = 8
console.log(b) // 8
console.log(a) // 6
引用類型變量複製原理
對於引用類型變量,複製實際上是指針指向存儲在堆中的一個對象,改變其中一個變量,就會影響另一個變量。
let person1 = {
name: '小明'
}
let person2 = person1
person2.name = '小紅'
console.log(person1.name) // 小紅
console.log(person2.name) // 小紅
ECMAScript中所有函數的參數都是按值傳遞的。【參數會複製給一個局部變量(arguments對象的一個元素),在參數傳遞引用類型的時候,會把這個值在內存中的地方複製給這個局部變量,因此這個局部變量的改變也會影響函數外部】
4. 檢測變量類型
typeof 操作符是檢測類型最佳的操作,但是對Object對象不友善。
採用 instanceof 操作符可以區分檢測object類型。
兩者結合纔是最好驗證類型的方法
// typeof
let genre;
console.log(typeof genre) // undefined
genre = 'haha'
console.log(typeof genre) // string
genre = 33
console.log(typeof genre) // number
genre = NaN
console.log(typeof genre) // number
genre = true
console.log(typeof genre) // boolean
genre = undefined
console.log(typeof genre) // undefined
genre = null
console.log(typeof genre) // object
genre = { name: 'cere', gender: 'man' }
console.log(typeof genre) // object
genre = [1, 2, 3]
console.log(typeof genre) // object
genre = function () { console.log(1) }
console.log(typeof genre) // function
// instanceof
genre = null
console.log(genre instanceof Object) // false
genre = { name: 'cere', gender: 'man' }
console.log(genre instanceof Object) // true
genre = [1, 2, 3]
console.log(genre instanceof Array) // true
注意幾點
- typeof null返回的是object,這個應該算JS的bug,但似乎不會修改(太多代碼依賴這個bug,修改導致大量問題)
- NAN 是number類型
null與undefined區別可以參考鏈接:阮一峯談undefined與null的區別
5. 垃圾收集
爲什麼要有垃圾收集機制
之前我們提到過創建變量,或者複製給其他變量等操作的時候都會分配一份內存。如果代碼越來越壯大,程序運行起來會申請大量的內存空間,如果不及時清理,內存會使用完(內存溢出),導致程序崩潰。
JS的垃圾收集機制
JavaScript不用手工跟蹤內存情況,它的自動垃圾收集機制,可以實現自動管理無用內存。垃圾收集器會按照固定的時間間隔,週期性的執行,找到不再繼續使用的變量,然後釋放佔用的內存。
JS垃圾收集策略
垃圾收集器跟蹤變量並且判斷有沒有用,如果沒用則在標籤打上標記,以備垃將來收回佔用的空間。
垃圾收集的策略有很多種,比如標記清除,引用計數,循環引用,分代回收等。瀏覽器中的實現基本上都是標記清除(或類似策略),只不過垃圾收集的間隔互不相同。
標記清除(mark-and-sweep)
原理:當變量進入環境(比如函數中聲明一個變量),將這個變量標記(可以用任何方式標記)爲“進入環境”,此時內存佔有。而當變量離開環境時候,標記爲“離開環境”,然後等待垃圾收集器下次完成內存清楚工作,銷燬帶標記的值並回收它們所戰友的內存空間。
引用計數(reference counting)
引用計數的含義是跟蹤記錄每個值被引用的次數。當賦給其他值或其他操作的時候,引用次數不斷加1。相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數就減1。當這個引用次數變成0時,則說明沒有辦法再訪問這個值,將其所佔的內存空間給收回來。