都是本人理解,筆記大致概念,不詳細也并非完全正確,所以僅供參考。
作用域
ES5 中,除全局作用域外,每一個函數(shù)塊存在屬于自己的作用域,稱之為函數(shù)作用域,在其內(nèi)部聲明的變量無法被外部使用到。
但可以使用上級作用域的變量,其概念類似原型鏈,一層一層尋找,找到了就使用。
原型鏈參考筆記二:JavaScript 原型鏈的深度剖析
而全局下的變量就屬于全局作用域。
var a = 123;
window.a === a // true
ES6 中新增了一個塊級作用域,主要是給 let 和 const 使用,簡單來說就是,任何被函數(shù) {} 包裹的區(qū)域,如:
if(1){
let a = 1;
const b = 2;
}
console.log(a); // a is not defined
console.log(b); // b is not defined
閉包
簡單來說,就是一個作用域里可以調(diào)用外部作用域的變量和函數(shù),則稱之為閉包。
PS:如果一個函數(shù)訪問了它的外部變量,那么它就是一個閉包。
function a(x) {
var age = x;
return function (name) {
console.log(name, age);
age++;
}
}
var age = 10;
var b = a(age);
b('jack'); // jack 10
b('jack'); // jack 11
b('jack'); // jack 12
所以可以說,a 實現(xiàn)了閉包。
內(nèi)存管理(垃圾收集機制)
內(nèi)存管理的機制就是周期性執(zhí)行這一操作:找出不再使用的變量,釋放其空間。
主要有以下兩種方式。
- 標記清除
- 引用計數(shù)
標記清除
標記清除是JavaScript最常用的內(nèi)存管理機制,即當變量進入環(huán)境時,則標記這個變量為進入,而當變量離開環(huán)境時,則將其標記為離開。
function add(n){
var sum += n; // 進入
return sum; // 離開
}
add(1); // 1
變量的定義和賦空值,也會出發(fā)標記清除:
var user = {
name: 'jack',
age: 20
}; // 在作用域定義變量,標記變量為進入
// some code ...
user = null; //最后定義為null,標記變量為離開,釋放內(nèi)存
引用計數(shù)
引用計數(shù)是不太常見的內(nèi)存管理機制,顧名思義,引用計數(shù)會跟蹤記錄每個值被引用的次數(shù),若是為0則清除。
var user = {
name: 'jack',
age: 20
}; // 引用次數(shù) + 1
// some code ...
user = null; // 最后定義為null,引用計數(shù) -1 ,為0清除
但引用計數(shù)會因為變量相互引用,造成內(nèi)存計數(shù)永不為0,故而雖然已經(jīng)執(zhí)行完了代碼,但是還是不會清除變量。
function a(){
var b = {};
var c = {};
b.test = c;
c.test = b;
}
a(); // 雖然 a 已經(jīng)執(zhí)行完,但是還是不會清楚 b 和 c 。
總結(jié)
雖然大多瀏覽器上JavaScript的內(nèi)存管理機制是標記清除,但是在 IE 瀏覽器(又是它)中,某些對象的內(nèi)存管理機制是引用計數(shù),如IE的 DOM和BOM,(統(tǒng)稱為COM對象),所以在寫代碼時要避免相互引用。
內(nèi)存泄漏
首先,內(nèi)存泄漏并不是不合理的,如閉包就會造成內(nèi)存泄漏,但是大家還是在使用閉包的方式寫函數(shù),所以存在即合理,需要做的,是在合理使用的情況下把不合理的去掉。
常見的造成內(nèi)存泄漏的情況:
- 定義全局對象
- 閉包
- dom清空或刪除時,事件未清除
- 相互引用
由上可知,基本解決方案就是:
- 不要輕易定義全局變量
- 如果有必要,盡量減少閉包函數(shù)。
- 刪除dom時,清除dom以及其子節(jié)點綁定的函數(shù)
- 不要相互引用,即使要相互引用,最后請加一個 a = null 切斷聯(lián)系
PS:不是很全面,要深度研究請網(wǎng)上沖浪,這里僅做一些參考。