第一部分 作用域和閉包(1-3章)
代碼在執行前,會經過三個步驟
分詞-詞法分析
- 将由字元組成的【代碼字串】分解為【有意義的代碼塊】,被稱為【詞法單元】
- 比如,var a = 2; 通常會被分解為幾個詞法單元:var、a、=、2。被稱為【詞法單元流數組】
解析-文法分析
- 将詞法單元流數組轉換為一套代表了程式文法的【抽象文法樹AST】,數組中的每一項被轉化為了AST樹中的節點
代碼生成
- 将AST轉化為一組【機器指令】,用來建立一個叫 a 的變量(記憶體配置設定),并将值 2 存儲在 a 中
相比較,js引擎更加複雜,會在三個階段中進行【運作性能優化】,包括【備援元素優化】
在js中,多數編譯發生在【代碼執行前幾微秒】
作用域是根據名稱查找變量的規則
- 引擎,負責整個 js 程式的編譯和執行過程
- 編譯器,負責文法分析和代碼生成
- 作用域,是一套規則,收集維護由所有聲明的辨別符組成的查詢,并确定目前執行的代碼是否有權限通路
變量的指派
- 首先,編譯器會詢問目前作用域是否存在該變量,如果沒有就聲明一個
- 然後,在運作時引擎會在作用域中查詢該變量,如果有就指派
- 仍以 a=2 為例,引擎會為變量 a 進行【LHS查詢(左查)】
- LHS查詢會試圖找到變量 a 的容器本身,并對其指派。另一種【LHR查詢(右查)】則隻是簡單的查找變量
- 【變量指派】和【調用函數時傳參】,這兩種操作都會導緻【LHS查詢】
小測試,找出LHS查詢和LHR查詢
function foo(a){
var b = a;
return a + b;
}
var c = foo(2);
//LHR查詢
foo(2
= a
a..
..b
//LHS查詢
c =
b =
a = 2 (傳參,隐式變量配置設定)
ReferenceError 和 TypeError
- 如果【RHS查詢】在作用域中找不到需要的變量,引擎就會抛出 ReferenceError ,代作作用域判别失敗
- 試圖引用 null 或是 undefined 屬性,會引發 TypeError,代表作用域判别成功,但是操作是非法的
eval 和 with
- eval 會動态插入一個變量聲明,這會對詞法作用域的環境進行修改
- with 被當作重複引用一個對象中的多個屬性時的快捷方式,不必重複引用對象本身。這種方式相當于建立了新的詞法作用域
- 這兩種方式,當引擎在進行編譯時,不能進行相關的性能優化,【不要使用】
//eval
eval("var c = 666");
console.log(c);
//width
var obj = {
a:1,
b:2,
c:3
}
with(obj){
a = 111;
b = 222;
c = 333;
}
console.log(obj);//{ "a": 111, "b": 222, "c": 333 }
匿名函數自執行表達式 | Immediately invoked function expression
- 有兩種寫法,都是一樣的,如下
- 如果要在其内部,擷取函數對象,隻有一種方法,如下
- 不要用【匿名函數自執行表達式】,要使用【具名函數自執行表達式】,如下
(function(){console.log(1)})();//1
(function(){console.log(2)}());//2
// arguments.callee在函數内部調用函數的方法
(function(){
console.log(arguments.callee);//
})();
//使用可描述的名稱,能讓代碼不言自明
(function printName(){console.log("name")})();
(function printColor(){console.log("color")}());
try catch 屬于塊級作用域
使用塊級作用域提升性能
- 如下,由于 setTimeout 引用了 color 變量,如果沒有大括号,代碼塊一中的變量會一直保留給 setTimeout,不會被垃圾回收機制處理
- 而實際上,setTimeout 中隻引用了 color,代碼塊一完全可以在執行完後釋放,是以可以手動加上塊級作用域
function doSomething(data){
console.log(data);
}
// 代碼塊一
{
const data = [1,2,3];
doSomething(data);
}
const color = "green";
setTimeout(function(){
//如果不需要data,則可以将代碼塊一,放在大括号中,這樣垃圾會收機制會釋放出這一部分記憶體
//console.log(data);
console.log(color);
},2000)
變量提升
小測試,輸出啥
a = 2;
var a;
console.log(a);//2
-不是undefined,有變量提升
小測試,再試一下
console.log(a);//undefined
var a = 2;
- 不是2,也不會報ReferenceError,而是undefined
- 因為,【變量聲明存在提升,但變量指派不會】