Javascript高階之變數、作用域與記憶體
阿新 • • 發佈:2020-12-19
變數、作用域與記憶體
原始值與引用值
-
理解要點
- ECMAScript中所有函式的引數都是按值傳遞的
- 函式外的值會被複制到函式內部的引數中
- 關於引用值(物件),也是按值傳遞的
- 理解
- 物件中儲存的值是物件的key,也就是指標
- 函式中按值傳遞時,可以給指標賦值,影響全域性變數
- 但是改變不了物件的key
- 可以用物件的動態屬性來理解
- 理解
-
確定型別
- 如果變數是給定引用型別的例項,則instanceof操作符返回true
- 如果用instanceof檢測原始值,則始終會返回false
let obj= null; let obj2= {}; let fun= Function.prototype; let fun2= new Function(); let arr= []; let str= ''; let reg= new RegExp(); let date= new Date(); console.log(obj instanceof Object); // false console.log(obj2 instanceof Object); // true console.log(fun instanceof Function); // false console.log(fun2 instanceof Function); // true console.log(arr instanceof Array); // true console.log(str instanceof String); // false console.log(reg instanceof RegExp); // true console.log(date instanceof Date); // true
執行上下文與作用域
-
執行上下文
- 每個上下文都有一個關聯的變數物件
- 所有變數和函式都存在於這個物件上
- 這個物件就是上下文棧,在其所有程式碼執行完畢之後,就會被銷燬
- var定義的全域性變數和函式,會成為window物件的屬性和方法
-
作用域鏈
- 上下文中的程式碼在執行的時候,會建立變數物件的一個作用域鏈
- 作用域鏈決定了各級上下文中的程式碼在訪問變數和函式時的順序
- 如果上下文是函式,則其活動物件用作變數物件
- 活動物件最初只有一個定義變數:arguments
- arguments是包含函式形參的一個物件
- 全域性上下文沒有這個變數
-
作用域鏈增強
- 在作用域鏈前端新增一個變數物件
- with語句
- 會向作用域鏈前端新增指定的物件
- try/catch語句中的catch塊
- 會建立一個新的變數物件,這個變數物件會包含要丟擲的錯誤物件的宣告
- with語句
- 在作用域鏈前端新增一個變數物件
變數宣告
-
使用var的函式作用域宣告
- 變數會被自動新增到最接近的上下文
- 未宣告的變數,會自動被新增到全域性上下文
- var宣告會被拿到函式或全域性作用域的頂部,叫做提升
-
使用let的塊級作用域宣告
- 塊級作用域由最近的一對包含花括號{}界定
- let在同一作用域內不能宣告兩次
- let非常適合在迴圈中宣告迭代變數
-
使用const的常量宣告
- const宣告的變數必須同時初始化為某個值
- const宣告只應用到頂級原語或者物件
- 使用
Object.freeze()
垃圾回收
-
標記清理
- 先給當前不使用的值加上標記,再回來回收它們的記憶體
-
引用計數
- 對每個值都記錄它被引用的次數
- 當引用次數為0時,垃圾回收程式會釋放引用數為0的值的記憶體
-
迴圈引用
- 物件A有一個指標指向物件B,而物件B也引用了物件A
- 只要涉及COM物件,就無法避免迴圈引用的問題
記憶體管理
-
解除引用
- 如果資料不再必要,那麼把它設定為null,從而釋放其引用
-
通過const和let宣告提升效能
- 塊作用域比函式作用域更早讓垃圾回收程式介入
-
隱藏類和刪除操作
- 在建構函式中一次性宣告所有的屬性,因此例項共享隱藏類
- 把不想要的屬性設定為null,可以保持隱藏類不變和繼續共享
-
記憶體洩漏
- 沒有使用關鍵字宣告變數
- 定時器的回撥通過閉包引用了外部變數
- 閉包造成的記憶體洩漏
-
靜態分配和物件池
- 避免動態分配操作