1. 一級函數 first class functions 1. 函數是一個對象 1. 2. 函數是一級函數,函數可以 1. 存儲在變數中\(函數表達式\) 2. 從一個函數返回 3. 作為參數傳遞給另一個函數\(回調\) 3. 高階函數 higher order function 1. 返回另一個 ...
一級函數 first-class functions
函數是一個對象
函數是一級函數,函數可以
存儲在變數中(函數表達式)
從一個函數返回
- 作為參數傳遞給另一個函數(回調)
高階函數 higher-order function
返回另一個函數的函數 或 接受其他函數作為參數的函數 被稱為高階函數
function alertThenReturn() { alert('Message 1!'); return function () { alert('Message 2!'); }; } const innerFunction = alertThenReturn(); alertThenReturn(); // 顯示 'Message 2!' innerFunction(); //顯示 'Message 2!' alertThenReturn()(); //顯示 'Message 1!' 然後顯示 'Message 2!'
回調callback
作用域
詞法作用域lexical scope和執行環境execution context
塊作用域和函數作用域稱為詞法作用域
- 當一個函數被運行時,會創建一個新的運行時作用域。這個作用域表示該函數的上下文,就是可供該函數使用的一組變數。這就是運行時作用域,即執行環境。
執行環境包括:
函數的參數
函數內聲明的本地變數
父函數作用域內聲明的變數
- 全局變數
函數作用域 function scope
塊級作用域 block scope
ES6用let 和 const 關鍵字實現塊級作用域
var x = 10; // 這裡輸出 x 為 10 { let x = 2; // 這裡輸出 x 為 2 } // 這裡輸出 x 為 10
const
- 此聲明創建一個常量。常量的值不能通過重新賦值來改變,並且不能重新聲明
- 作用域可以是全局或本地聲明的塊
let
- 聲明一個塊級作用域的本地變數,並且可選的將其初始化為一個值。
- 為什麼使用let
- 像數學里的描述,let x be an arbitrary
暫存死區
通過let聲明的變數直到他們的定義被執行時才初始化,在初始化前訪問該變數會導致ReferenceError.
該變數處在自頂部到初始化處理的暫存死區。
如以下代碼中的ReferenceError
function do_something() { console.log(bar); // undefined console.log(foo); // ReferenceError var bar = 1; let foo = 2; }
用var關鍵字聲明的變數不具備塊級作用域的特性,在{ }外依然能被訪問到
var x = 10; // 這裡輸出 x 為 10 { var x = 2; // 這裡輸出 x 為 2 } // 這裡輸出 x 為 2
函數作用域
- 函數可以訪問自己的所有變數和外部的所有全局變數
var globalNumber = 5; function globalIncrementer() { const localNumber = 10; globalNumber += 1; return globalNumber; } console.log(globalIncrementer()); // 6 console.log(globalIncrementer()); // 7 console.log(globalIncrementer()); // 8 console.log(localNumber); // ReferenceError: localNumber is not defined // 這裡localNumber在log函數的外部,因為無法取到localNumber的值,const定義的塊級作用域
作用域鏈 scope chain
-
- 在訪問變數時,JS引擎將遍歷作用域鏈(查找變數的順序是線性的),首先查看最內層,然後查看外層作用域,最後在必要時到達全局作用域。
- Window對象
- 聲明的任何全局變數都是作為window對象(全局對象)的屬性被訪問的,它表示作用域鏈的最外層。
-
變數陰影variable shadowing
創建的變數與作用域中的另一個變數具有相同名稱時,局部作用域的變數會shadow外部作用域中的變數
var money = '¥'; function myMoney() { var money = '$'; console.log(money); } myMoney(); console.log(money);
- 指向'$'的變數是在函數內部聲明的,將shadow位於外部作用域的同名變數,即指向'¥'的全局變數
- 如果函數內部的沒有變數聲明,只有一個賦值,則會造成scope shadowing
- 在不同執行環境中的變數之間有任何重名重疊,會通過從內部作用域到外部作用域
遍歷作用域鏈來解決。
閉包
詞法作用域lexical scoping
- 'lexical' refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available.詞法作用域通過源代碼(自己寫的)中變數聲明的位置來確定變數在此處否可用。
閉包 closure
詞法環境(又一個坑)A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upom the lexical nesting structure of ES code. 詞法環境是一個規範類型,是標識符與特定變數和函數基於ES代碼的詞法嵌套結構的關聯。一個詞法環境由環境記錄和可能為空的外部詞法環境引用組成。
function makeFunc() { var name = 'count'; function func2() { console.log(name); } return func2; } var output = makeFunc(); output(); // 'count'
- func2還未執行,被func1返回。一般來說,該段代碼不能正常運行,因為局部變數name在func1執行完畢後,name將不能再被訪問。但是,Why it works?
- 因為JS中的函數會形成閉包。函數保留對其作用域的訪問的這個過程被稱為閉包。
- 閉包是由函數和創建該函數的詞法環境組合而成。
- 在這裡“詞法環境”是指在JS文件中編寫的代碼。
- output是func2函數實例的引用,而func2實例仍可訪問其詞法作用域中的變數,即可以訪問name.
- 因為JS中的函數會形成閉包。函數保留對其作用域的訪問的這個過程被稱為閉包。
函數保留其作用域
標識符是指用來標識某個實體的一個符號,在不同的應用環境下有不同的含義。在編程語言中,標識符是用戶編程時使用的名字,用於給變數、常量、函數、語句塊等命名,以建立起名稱與使用之間的關係。
當使用標識符時,作用域鏈將被檢查,以檢索標識符的值。作用域鏈對於函數訪問代碼中的標識符來說非常強大的工具。