大話javascript 1期:作用域和作用域鏈

一、什麼是作用域?

作用域是你的代碼在運行時,各個變量、函數和對象的可訪問性。(可產生作用的區域)

二、JavaScript中的作用域

在 JavaScript 中有兩種作用域

  • 全局作用域
  • 局部作用域

當變量定義在一個函數中時,變量就在局部作用域中,而定義在函數之外的變量則從屬於全局作用域。每個函數在調用的時候會創建一個新的作用域。

1.全局作用域

當你在文檔中(document)編寫 JavaScript 時,你就已經在全局作用域中了。JavaScript
文檔中(document)只有一個全局作用域。定義在函數之外的變量會被保存在全局作用域中
// 作用域默認爲全局作用域
var name = 'andy';

全局作用域裏的變量能夠在其他作用域中被訪問和修改

var name = 'andy';
console.log(name); // 輸出 'andy'
function logName() {
    console.log(name); // 'name' 變量可以在這裏和其他地方訪問
}
logName(); // 輸出 'andy'

2.局部作用域

定義在函數中的變量就在局部作用域中。並且函數在每次調用時都有一個不同的作用域。這意味着同名變量可以用在不同的函數中。因爲這些變量綁定在不同的函數中,擁有不同作用域,彼此之間不能訪問。
// 全局作用域
function someFunction() {
    // 局部作用域 ##1
    function someOtherFunction() {
        // 局部作用域 ##2
    }
}
 
// 全局作用域
function anotherFunction() {
    //局部作用域 ##3
}

3.塊語句(JS沒有塊級作用域)

塊級聲明包括if和switch,以及for和while循環,和函數不同,它們不會創建新的作用域。在塊級聲明中定義的變量從屬於該塊所在的作用域。也就是說在for、if、while等語句內部的聲明的變量與在外部聲明是一樣的,在這些語句外部也可以訪問和修改這些變量的值。
if (true) {
    //這裏的if條件不會創建一個新的作用域
    var name = 'Hammad'; // name 這個變量仍在全局作用域
}

console.log(name); // logs 'Hammad'

ECMAScript 6 引入了let和const關鍵字。這些關鍵字可以代替var

var name = 'Hammad';

let likes = 'Coding';
const skills = 'Javascript and PHP';

和var關鍵字不同,let和const關鍵字支持在塊級聲明中創建使用局部作用域(塊級作用域)

if (true) 
    // 這個 'if' 塊語句沒有創建一個塊級作用域

    // name 變量處於全局作用域,因爲由var關鍵字聲明
    var name = 'Hammad';
    // likes 變量處於塊級作用域因爲由let關鍵字聲明
    let likes = 'Coding';
    // skills 變量處於塊級作用域因爲由const關鍵字聲明
    const skills = 'JavaScript and PHP';
}

console.log(name); // 輸出 'Hammad'
console.log(likes); // Uncaught ReferenceError: likes is not defined
console.log(skills); // Uncaught ReferenceError: skills is not defined

一個應用中全局作用域的生存週期與該應用相同。局部作用域只在該函數調用執行期間存在

4.詞法作用域

所謂的 詞法( 代碼 )作用域, 就是代碼在編寫過程中體現出來的作用範圍. 代碼一旦寫好, 不用執行, 作用範圍就已經確定好了.
這個就是所謂詞法作用域.這意味着函數運行在定義它的作用域中,而不是在調用它的作用域中

在 js 中詞法作用域規則:

  • 函數允許訪問函數外的數據.
  • 整個代碼結構中只有函數可以限定作用域.
  • 作用規則首先使用提升規則分析
  • 如果當前作用規則中有名字了, 就不考慮外面的名字

詞法作用域

var用來將變量定義在詞法作用域中(也就是function中)

function someFunc(){
    var a;
}

a就被函數someFunc框在了詞法作用域中

塊級作用域

letconst用來將變量定義在塊級作用域中(也就是花括號中)

if(true){
    let b;
}

b就被if的花括號框在了塊級作用域中

5.作用域鏈

可以發現只有函數可以製造作用域結構. 那麼只要是代碼, 至少有一個作用域, 即全局作用域. 凡是代碼中有函數,那麼這個函數就構成另一個作用域. 如果函數中還有函數, 那麼再這個作用域中就 又可以誕生一個作用域. 那麼將這樣的所有的作用域列出來,可以有一個結構: 函數內指向函數外的鏈式結構.

作用域嵌套

作用域是可以嵌套的,任務一中提到的詞法作用域和塊級作用域都可以嵌套其他作用域
(塊級作用域僅對ES6而言)

function someFunc(){
    function inner(){
    }
}

inner就是嵌套在someFunc(詞法作用域)中的詞法作用域

if(true){
    while(false){
    }
}

while就是嵌套在if(塊級作用域)中的塊級作用域

function someFunc(){
    if(true){
    }
}

if就是嵌套在someFunc(詞法作用域)中的塊級作用域

作用域中的變量訪問

所有的嵌套作用域都遵循以下規則:
內部作用域有權訪問外部作用域,反之不成立。

栗子:

function someFunc(){

var outerVar = 1;
function inner(){
    var innerVar = 2;
}

}
inner有權訪問innerVarouterVar,但是someFunc只能訪問到outerVar

多重嵌套作用域

作用域是可以任意嵌套的,但是都要遵循上面的規則。
再附加一個規則:
兄弟作用域不可相互訪問

栗子:

function someFunc(){
    function inner(){
    }
    function inner2(){
    }
}

innerinner2都是someFunc中的作用域,正如someFunc不能訪問inner們的作用域一樣,inner們之間也不能相互訪問。

作用域樹

從上向下看這個嵌套作用域,就是棵樹!
看代碼:

function someFunc() {
  function inner() {
  }
  function inner2() {
    function foo() {
    }
  }
}

看樹:

   someFunc()
       |
      / \
     /   \
    /     \
   ↓       ↓
inner()  inner2()
           |
           ↓
         foo()

要記住的是:inner作用域可以訪問外部作用域,但是反之不成立; foo()可以訪問inner2()中的變量,inner2()可以訪問someFunc()中的變量,這棵樹倒過來似乎更有意義,就成了鏈!!

作用域鏈

從最裏面看到最外面就構成了作用域鏈

   someFunc()
       ↑
        \
         \
          \
         inner2()
           ↑
           |
         foo()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章