作用域鏈和閉包理解

2021-07-25 12:22:24 字數 4094 閱讀 8450

一  作用域鏈1.在理解什麼事作用域鏈之前我們首先要了解幾個概念:變數、變數的宣告、變數宣告提前變數:變數包括兩種,普通變數和函式變數。 

普通變數:

凡是用var標識的都是普通變數

var x=1;               

var object={};

var geta=function(){}; //以上三種均是普通變數,但是這三個等式都具有賦值操作。所以,要分清楚宣告和賦值。宣告是指 var x; 賦值是指 x=1;

函式變數

:函式變數特指的是下面的這種,fun就是乙個函式變數。

function fun(){} ;// 這是指函式變數. 函式變數一般也說成函式宣告。
為了便於理解,我們在這裡將將分開討論普通變數的宣告和函式變數的宣告

普通變數

:宣告是在函式第一行**執行之前就已經完成,而賦值是在函式執行時期才開始賦值。所以,宣告總是存在於賦值之前。

而且,普通變數的宣告時期總是等於undefined.所以如果在賦值之前就呼叫普通變數的話就會是undefined;

alert(x);

var x=3;//undefined

函式變數

:函式宣告也是提前到在函式第一行**之前,所以如果在一段**中先呼叫函式,然後再寫出函式,也是可以被正確執行的,因為函式宣告會提前。

f(5);

function f(n)//5

2 作用域的組成函式的作用域,也就是函式的執行環境,所以函式作用域內肯定儲存著函式內部宣告的所有的變數。

所以函式的變數的**無非三個方面:a:函式的引數

b:函式內部的變數

c:函式外部的作用域的變數

舉個栗子:

var x = 1;

function add(num)

console.log(add(5));//console 7

在函式呼叫的時候函式的作用域才存在,呼叫之前開始建立函式的作用域(執行環境)

建立步驟:a.函式形參的宣告

b.函式內部變數的宣告

c.普通變數的宣告(執行函式外部的變數的宣告)

d.函式內部this指標賦值

..........函式內部**開始執行

這也解釋了為什麼宣告提前,在函式開始執行之前,需要宣告所有函式作用域內變數將其儲存在乙個「變數物件」裡

關於變數的宣告這裡有幾點需要注意:

ps1.函式的形參在宣告時已經指定了值

function show(num)

show(5);//5

ps2.第二步函式變數的宣告,會覆蓋以前宣告過得同名變數

function add(num) 

add(1);//出現10 而不是1

ps.3第三步中普通變數的宣告不會覆蓋以前宣告過得同名變數

var num = 10;

function add(num)

add(1)//1

會覆蓋和不會覆蓋的原因解釋:作用域鏈!

3 作用域鏈的組成

在js的函式是允許巢狀的比如

var a = 1;

function a(d)

return b()+a+b+d;//這裡可以訪問abd

}

//這裡只能訪問a

alert(a(4));

當執行函式b的時候,會從函式內部開始搜尋變數abcd,只找到c然後向包圍他的函式a搜尋再向全域性環境搜尋直到找到標示符

如果執行的是函式a則作用域是a內的變數和全域性變數這是a的執行環境,將a的執行環境裡面的變數儲存在乙個變數物件裡面,作用域鏈的頭從a的變數開始尾巴是全域性變數,在這個過程中開始匹配標示符知道全部找到,如果都找不到,丟擲錯誤,在這裡也解釋了為何會有同名變數的覆蓋

二 閉包

關於閉包好像不是那麼的容易理解,現在的我翻譯幾個外國**上的例子來加深自己的理解:

閉包(與鑲嵌函式有關乙個函式內還定義了另個函式,其中的函式一般是第一型別函式)是一種支援第一函式的方法,可以這樣表達,,當他被宣布為第一函式的時候,他被分配給乙個變數,或者是

作為結果返回到上一級的函式,這個第一函式它能夠訪問他的作用域範圍(作用域鏈向上延伸知道全域性變數)內的變數。現在,有乙個變數!它裡面儲存的是作為第一函式的那個函式

只要這個變數被執行,那麼這個第一函式能訪問到的其他變數就會一直存在,即便是他所在的函式已經執行完畢(通常來說,某個變數只存在他所在的函式被執行階段

,執行完畢後被記憶體釋放)記憶體不會釋放。所以如果你呼叫閉包函式(儲存第一函式的變數)那麼有可能改變了上一級的變數,然後再執行閉包函式是訪問到的上一級的

變數已經是改變後的變數值。

舉個例子:

function sayhello2(name)

return say;

}var say2=sayhello2('bob');

say2();

也可以換種寫法同樣還是這個函式

var say2=undefined;

function sayhello2(name)

} sayhello2('wei');

say();

閉包函式因為作用域內的變數無法釋放會產生一些有意思的現象:

舉個例子:

function a()

return b;

}var c=a();

c(10);//20

c(10);//21

c(10);//22

下面來看另個類似的函式:

var b=function(b)

b(10);//20

b(10);//20

b(10);//20

思考一下為什麼會出現上面兩種情況:理解的重點在於變數的存活時間,第乙個a變數因為閉包函式的存在記憶體無法釋放所以每次呼叫閉包函式a的值都會加1,而下次再呼叫閉包函式

訪問的a的值是加1後的,下面的情況是:函式執行完畢後記憶體被釋放銷毀,再次呼叫函式時重新將變數a放入棧中,所以值是沒有變化的

作用域鏈和閉包

一.作用域鏈 scope chain 1.作用域 函式作用域 scope 外部對內部可見 內部對外部不可見 內部優先 js中只有函式級別的作用域,沒有塊級別的作用域 換句話說,只有在進入或者退出函式的時候,作用域會發生變化 2.執行環境 ec 和作用域鏈 執行環境,定義了執行期間可以訪問的變數和函式...

作用域,閉包,作用域鏈

一,作用域 變數在宣告它的函式及該函式所巢狀的任意函式是有定義的 例var num 2 function fun fun 二,作用域鏈 多個函式巢狀在一起,多個作用域相互巢狀,這是作用域鏈 var num 1 function fun function fun2 fun1 fun2 fun 訪問原則...

作用域 作用域鏈 閉包

閉包概念中的一些專業名詞概念不清晰 作用域鏈 本質是乙個指向變數物件的指標列表。函式的作用域鏈在函式呼叫完成後即被銷毀。變數物件 全域性變數物件 js執行時一直存在 活動變數物件 區域性變數物件,函式執行完畢後銷毀 函式在呼叫時建立本地的活動物件加上函式定義時預建立的作用域鏈,形成乙個新的用於執行函...