引言
滿滿的乾貨,面試必bei系列,參考大量資料,並集合自己的理解以及相關的面試題,對JS核心知識點中的作用域、閉包、this、上下文進行了梳理。由於篇幅有限,這裏只對我認爲最重要的知識做了介紹,一些常識性的東西大家可以參考高程。
上下文(execution context)
又叫執行環境,環境。
執行環境定義了變量或者環境有權訪問的其他數據,據定了它們的各自行爲 --高程
一個函數執行的時候,會產生一個屬於自己的執行環境。環境裏面有一個變量對象variable object(VO),OA裏面存放着環境中定義的所有變量和函數,作用域鏈(scope chain),this。
函數執行,環境產生被推入環境棧,函數執行完,環境出棧並被銷燬(閉包例外),把控制權返回給之前的執行環境。
作用域
js中的作用域是靜態作用域,靜態作用域又叫做詞法作用域,採用詞法作用域的變量叫詞法變量。詞法變量有一個在編譯時靜態確定的作用域。詞法變量的作用域可以是一個函數或一段代碼,該變量在這段代碼區域內可見(visibility);在這段區域以外該變量不可見(或無法訪問)。詞法作用域裏,取變量的值時,會檢查函數定義時的文本環境,捕捉函數定義時對該變量的綁定。--wiki
作用域是一套規則
,用於確定在何處以及如何查找變量(標識符)。--你不知道的javascript
作用域就是執行的時候,給環境變量賦值的一種規則,這種規則在函數定義的時候就已經確定了,和運行無關
。
js只有全局作用域和函數作用域,沒有塊作用域。
變量提升
我們把定一個變量的行爲分爲兩個過程,聲明和定義
var a = 1
//實際執行的是下面兩步
var a
a = 1
var的變量聲明會提升,沒有var就是全局變量,let沒,const有變量提升
var的函數聲明和賦值都提升
a //undefined 因爲a的聲明已經提升到最上面了
var a = 1
f() //alert 1
function f () {
alert (1)
}
有幾個特殊的地方雖然平時不會這麼寫,但是面試題會遇到:
- 函數體中,return後面的代碼不進行變量提升,但是return下面的代碼要進行變量提升
- 不管條件是否成立,都要進行變量提升;
- 匿名函數不進行 變量提升;
- 如果變量名字發生重複,那麼不再重複聲明,但是要重新定義;
執行環境和作用域的關係
很多人都分不清楚執行環境和作用域的關係。其實很簡單,作用域和上下文完全是兩個不相干的東西。
作用域是一種規格,聲明函數的時候就已經確定了。
執行環境是函數執行的時候產生的,函數在執行環境中執行。大家看下面例子
alert(a) //a is not defined
執行的時候VO裏面沒有a,因爲根據VO作用域鏈【windows】,按照規則找不到a。
var a = 1
alert(a) // alert 1
執行的時候,根據規則,從VO作用域鏈【windows】頭部window作用域開始找a,找到a了,a爲1,則vo中a設置爲1,所以alert 1
var a = 1
function foo() {
var a = 100
alert(a)
}
foo() // alert 100
執行的時候,根據規則,從VO作用域鏈【windows-foo】頭部foo作用域開始找a,找到a了,a爲100,則vo中a設置爲100,所以alert 100
var a = 1
function foo() {
alert(a)
}
foo() // alert 100
執行的時候,根據規則,從VO作用域鏈【windows-foo】頭部foo作用域開始找,沒找到a。根據規則,沿上層作用域(也就是window)開始找,找到a了,aw爲1。則vo中a設置爲1,所以alert 1
函數執行的完整過程
- 開闢一片棧空間,根據作用域生成上下文
- 形參賦值
- 變量提升
- 代碼從上到下運行
- 執行完畢後,判斷有沒有閉包,沒有的話上下文銷燬
練習題
1.函數傳參本質
解析可參考:https://segmentfault.com/a/11...
var ary = [1,2,3,4];
function sum(ary) {// 私有的;
ary[0] =100;
ary = [];// 在JS中,遇到{}、[]都會開闢一個新的空間地址
ary[0] = 10;
console.log(ary)
}
sum(ary); // [10]
console.log(ary);// [100,2,3,4]
2.特殊變量提升
var foo=1;
function bar(){
if(!foo){
var foo=10;
}
console.log(foo);
}
bar(); //10
3.作用域、變量提升
console.log(a);
var a=12;
function fn(){
console.log(a); //注意這裏的a在本執行環境獲取不到,根據作用域鏈搜索全局執行環境的a
a=13; //同理修改的也是全局執行環境的a
}
fn();
console.log(a);
// undefined 12 13
console.log(a);
var a=12;
function fn(){
console.log(a);
var a=13;
}
fn();
console.log(a);
// undefined undefined 12