JavaScript 預編譯與作用域
阿新 • • 發佈:2021-08-15
預編譯
JavaScript執行步驟
檢查通篇的語法錯誤 -> 預編譯 -> 解釋執行
暗示全域性變數
變數不宣告直接賦值,掛載到 window 物件下
a = 1;
console.log(a); // 1
function test() {
var b = c = 1;
}
test();
console.log(c); // 列印 1,c 未宣告,直接賦值,暗示全域性變數
console.log(window.b); // undefined,列印物件不存在屬性,返回 undefined
console.log(b); // 報錯
預編譯過程
函式宣告整體提升
變數宣告提升,賦值不提升
test(); // 放在前面可以執行
function test() {
}
console.log(a); // 打印出 undefined
var a = 1; // 宣告提升,賦值不提升
GO
global object,全域性上下文,即 window 物件
在全域性執行的前一刻,生成 GO,即變數宣告提升和函式宣告提升
步驟:尋找變數宣告 -> 尋找函式宣告 -> 順序執行
var a = 1; // (1)
function a() {
console.log(2);
}
console.log(a); // (2)
// 全域性預編譯
// 變數宣告 a -> GO{a: undefined}
// 函式宣告 a(){} -> GO{a: a(){}}
// 全域性執行
// 執行 (1) -> GO{a: 1}
// 執行 (2),列印 1
console.log(a); // (1)
var a = 1; // (2)
console.log(a); // (3)
function a() {
console.log(2);
}
// 全域性預編譯
// 變數宣告 a -> GO{a: undefined}
// 函式宣告 a(){} -> GO{a: a(){}}
// 全域性執行
// 執行 (1),列印 a(){...}
// 執行 (2) -> GO{a: 1}
// 執行 (3),列印 1
AO
activation object,函式上下文,即活躍物件
每個函式都有自己的 AO,函式執行前一刻生成,執行完以後銷燬
步驟:尋找形參和變數宣告 -> 形參實參對映 ->尋找函式宣告 -> 順序執行
注意:AO 中有 var a 宣告,不會找 GO 裡的 a
function test(a) {
console.log(a); // (1)
var a = 1; // (2)
console.log(a); // (3)
function a() {}
console.log(a); // (4)
var b = function() {} // (5)
console.log(b); // (6)
function d() {}
}
test(2);
// 全域性預編譯
// 函式宣告 test(){} -> GO{a: test(){}}
// 全域性執行
// 執行到 test(2),函式 test 預編譯
// 變數宣告 a, b -> test_AO{a: undefined, b: undefined}
// 引數對映 -> test_AO{a: 2, b: undefined}
// 函式宣告 -> test_AO{a: a(){}, b:undefined, d: d(){}}
// 函式 test 執行
// 執行 (1),列印 a(){}
// 執行 (2) -> test_AO{a: 1, b: undefined, d: d(){}}
// 執行 (3),列印 1
// 執行 (4),列印 1
// 執行 (5) -> test_AO{a: 1, b: (){}, d: d(){}}
// 執行 (6),列印 (){}
// 函式 test 執行完畢,test_AO 銷燬
練習
a = 1; // (1)
function test() {
console.log(a); // (2)
a = 2; // (3)
console.log(a); // (4)
if(a) { // (5),預編譯時不看 if,因為沒有執行該句
var a = 3;
}
console.log(a); // (6)
}
test();
var a;
// 全域性預編譯
// 變數宣告、函式宣告 -> GO{a: undefined, test: test(){}}
// 全域性執行
// 執行 (1) -> GO{a: 1, test: test(){}}
// 執行到 test(),函式 test 預編譯
// 變數宣告 -> test_AO{a: undefined}
// 函式 test 執行
// 執行 (2),列印 undefined
// 執行 (3) -> AO{a: 2}
// 執行 (4),列印 2
// 執行 (5) -> AO{a: 3}
// 執行 (6) -> 列印 3
// 函式 test 執行完畢,test_AO 銷燬
作用域
函式的屬性
函式是一種引用型別
有一些原生屬性可以利用,也有一些屬性不能訪問,是js引擎內部固有的隱式屬性
[[scope]] 就是 JS 內部隱式屬性,是函式儲存作用域鏈的容器
function test() {
}
console.log(test.name); // test
console.log(test.length); // 0
https://www.houdianzi.com/ vi設計公司
作用域鏈
在函式宣告時,生成 JS 內部隱式屬性 [[scope]],該屬性的第 0 位儲存全域性執行期上下文 GO 的一個引用
在函式執行前一刻,[[scope]] 第 0 位儲存函式執行期上下文 AO,後一位儲存外層函式的 AO,最後儲存 GO 引用。如果沒有外層函式,則第 0 位 AO,第 1 位 GO。在尋找宣告時,都會由 0 位向後尋找,即先看自己,再看外層,最後看全域性
在函式執行完畢時,從 [[scope]] 中銷燬 AO
function a() {
funtion b() {
var b = 2; // (3)
}
var a = 1; // (2)
b();
}
var c = 3; // (1)
a();
// 全域性預編譯
// -> GO{c: undefined, a: a(){}}
// -> a.SCOPE = [
// GO{c: undefined, a: a(){}}
// ]
// 全域性執行
// 執行 (1)
// -> GO{c: 3, z a(){}}
// -> a.SCOPE = [
// GO{c: 3, a: a(){}}
// ]
// 執行到 a(),函式 a 預編譯
// -> a.SCOPE = [
// a_AO{a: undefined, b: b(){}},
// GO{c: 3, a: a(){}}
// ]
// -> b.SCOPE = [
// a_AO{a: undefined, b: b(){}},
// GO{c: 3, a: a(){}}
// ]
// 函式 a 執行
// 執行 (2)
// -> a.SCOPE = [
// a_AO{a: 1, b: b(){}},
// GO{c: 3, a: a(){}}
// ]
// 執行到 b(),函式 b 預編譯
// -> b.SCOPE = [
// b_AO{b: undefined},
// a_AO{a: 1, b: b(){}},
// GO{c: 3, a: a(){}}
// ]
// 執行 (3)
// -> b.SCOPE = [
// b_AO{b: 3},
// a_AO{a: 1, b: b(){}},
// GO{c: 3, a: a(){}}
// ]
// 函式 b 執行完畢,b_AO 銷燬
// -> b.SCOPE = [
// a_AO{a: 1, b: b(){}},
// GO{c: 3, a: a(){}}
// ]
// 函式 a 執行完畢,a_AO 銷燬
// -> b.SCOPE 銷燬
// -> a.SCOPE = [
// GO{c: 3, a: a(){}}
// ]