1. 程式人生 > 其它 >JavaScript 預編譯與作用域

JavaScript 預編譯與作用域

預編譯

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(){}}
//       ]