晝貓筆記 JavaScript — 作用域技巧!!

貓晝發表於2019-01-16

簡單理解

var zm = function (x) {
    var code = `bb`
    return code
};

 

學過js的老哥們都知道,當這樣簡單的一個函式進入瀏覽器,瀏覽器開始解釋程式碼,會將window分兩個模組:儲存模組、執行模組。
儲存模組,找到所有的varfunction關鍵字,給這些變數新增記憶體地址
執行模組,程式碼從上到下執行,遇到變數就回去儲存模組查詢是否有該變數
如果有該變數,就看是否賦值,如果賦值了就是後面的值,沒有賦值就是undefined
如果沒找到 結果就是xxx is not defined
對於作用域可能對於部分人來說是難以理解的知識點,今天呢就列舉5個知識點,只要記住這幾句話,作用域對你來說就是小意思

一、“JavaScript中無塊級作用域”

在Java或C#中存在塊級作用域,即:大括號也是一個作用域。

public static void main (){
    if(1==1){
        String name = "seven";
    }
    System.out.println(name);
}
// 報錯

 

在JavaScript語言中無塊級作用域

function Zhoumao() {
    if(1 == 1){
        var name = `晝貓`
    }
    console.log(name);
}
Zhoumao()
// 輸出:晝貓

補充:標題之所以新增雙引號是因為JavaScript6中新引入了 let 關鍵字,用於指定變數屬於塊級作用域。


二、JavaScript採用函式作用域

在JavaScript中每個函式作為一個作用域,在外部無法訪問內部作用域中的變數。

function Zhoumao() {
    var name = `晝貓`
}
Zhoumao()
console.log(name);
// 報錯:ReferenceError: name is not defined

 


三、JavaScript的作用域鏈

由於JavaScript中的每個函式作為一個作用域,如果出現函式巢狀函式,則就會出現作用域鏈。

name = `晝貓`
function out() {
    var name = `zhoumao`
    function inner() {
        var name = `hello`
        console.log(name);
    }
    inner()
}
out()

 

如上述程式碼則出現三個作用域組成的作用域鏈,如果出現作用域鏈後,那麼尋找變數時候就會出現順序,對於上述例項:

當執行console.log(name)時,尋找順序根據作用域鏈從內到外的優先順序尋找,如果內層沒有就逐步向上找,直到window為止,window有就使用沒有就is not defined。


四、JavaScript的作用域鏈執行前已建立

JavaScript的作用域在被執行之前已經建立,日後再去執行時只需要按照作用域鏈去尋找即可。

示例一:

name = `zhoumao`
function out() {
    var name = `晝貓`
    function inner() {
        console.log(name);
    }
    return inner;
}
var code = out();
code();
// 輸出結果:晝貓

 

上述程式碼,在函式被呼叫之前作用域鏈已經存在:
全域性作用域 -> out函式作用域 -> inner函式作用域

當執行code()時,由於其代指的是inner函式,此函式的作用域鏈在執行之前已經被定義為:全域性作用域 -> out函式作用域 -> inner函式作用域,所以,在執行code()時,會根據已經存在的作用域鏈去尋找變數。

示例二:

name = `zhoumao`
function out() {
    var name = `晝貓`
    function inner() {
        console.log(name);
    }
    name = `hello`
    return inner;
}
var code = out();
code();
// 輸出結果:hello

 

上述程式碼和示例一的目的相同,也是強調在函式被呼叫之前作用域鏈已經存在:

全域性作用域 -> out函式作用域 -> inner函式作用域

不同的是,在執行 var code = out()時,out作用域中的name變數的值已經由晝貓改變為hello,所以之後再執行code()時,就只能找到hello

示例三:

name = `晝貓`;
function one(){
    console.log(name);
}
function two(){
    var name = "zhoumao"; 
    return one;
}
var code = two();
code();
// 輸出結果: 晝貓

 

上述程式碼,在函式被執行之前已經建立了兩條作用域鏈:

全域性作用域 -> one函式作用域
全域性作用域 -> two函式作用域

當執行code()時,code代指的one函式,而one函式的作用域鏈已經存在:全域性作用域 -> one函式作用域,所以,執行時會根據已經存在的作用域鏈去尋找。


五、宣告提前

在JavaScript中如果不建立變數,直接去使用,則報錯:

console.log(name)
// 報錯:ReferenceError: name is not defined

 

如果只建立不賦值,則返回undefined

var name;
console.log(name)
// 輸出:undefined

 

在函式中如果這麼寫:

function Test() {
    console.log(name);
    var name = `晝貓`;
}
Test()
// 輸出:undefined

 

在上述程式碼中不報錯而是輸出undefined,是因為JavaScript的函式在被執行之前,會將其中的變數全部宣告,而不賦值。所以,相當於上述示例中,函式在“預編譯”時,已經執行了var name;所以上述程式碼中輸出的是undefined

類似這種寫法

function Test() {
    var name
    console.log(name);
    name = `晝貓`;
}
Test()

文章來自公眾號 晝貓筆記

相關文章