js - 执行上下文和作用域以及闭包
首先,咱們通常被"執行上下文","執行上下文環境","上下文環境","執行上下文棧"這些名詞搞混。那我們一一來揭秘這些名字的含義。
這一塊一直比較晦澀難懂,還是需要仔細去斟酌斟酌。
什么是執行上下文(也叫做“執行上下文環境”,“上下文環境”)?
咱們還是先看代碼。
console.log(a) // undefined var a = 100fn('bella') // 'bella' 20 function fn(name) {age = 20console.log(name, age)var age }console.log(b); // 這里報錯 // Uncaught ReferenceError: b is not defined第一個console輸出 undefined,說明瀏覽器在執行console.log(a)的時候,已經知道a的存在的,但是不知道a的值。
第二個fn("bella")輸出 "bella" 20,說明瀏覽器在執行的時候已經知道fn函數了,并且執行了
第三個console報錯,b is noe defined。說明沒有找到b
那么可以看出來,瀏覽器在執行之前做了一些準備工作。
那做了些什么準備工作:
- 全局上下文環境: 變量定義,函數聲明
- 函數上下文環境(函數內部):變量定義,函數聲明,this,arguments
下面這個例子很多地方都用來講解執行上下文和上下文棧。
1 // 這是一個壓棧出棧的過程--執行上下文棧 2 let a = 10; // 1、進入全局上下文環境 3 let fn; 4 let bar = function(x) { 5 let b = 5 6 fn(x + b) // 3、進入fn函數上下文環境 7 }; 8 fn = function(y) { 9 let c = 5 10 console.log(y + c) 11 } 12 13 bar(10) // 2、 進入bar函數上下文?這里引出了執行上下文棧的概念,上下文棧就是壓棧和出棧的過程。
1.在代碼執行之前,首先創建全局上下文環境
// 全局上下文環境 a: undefinedfn: undefinedbar: undefined,this: window
2.然后執行代碼,在代碼執行到12之前,全局上下文中的變量在執行中被賦值
// 全局上下文環境a: 10fn: functionbar: function,this: window
然后執行13行代碼,調用bar函數,會創建一個新的執行上下文環境。并將這個bar上下文環境壓棧,并設置為活動狀態
// bar函數上下文環境 b: undefinedx: 10arguments: [10]this: window
3.然后執行到第6行代碼,調用fn的時候,會創建一個新的執行上下文。并將這個fn上下文環境壓棧,并設置為活動狀態。
// fn函數上下文環境 c: undefined y: 15 arguments: [15]this: window
4.fn執行完畢后,調用fn函數生成的fn上下文環境出棧,銷毀。然后bar出棧銷毀。然后全局上下文出棧銷毀
?
?
理解完了執行上下文,再看看this
相信都知道這句話,誰調用函數,this就指向誰。那么我們理解下this:
var a = {name: 'A',fn: function() {console.log(this.name)}}a.fn() // this === a a.fn.call({name: 'B'}) // this === {name: 'B'}var fn1 = a.fnfn1() // this === windowthis: this的值只有在執行的時候才能確認,定義的時候不能確認。因為this是執行上下文的一部分,而執行上下文需要再代碼執行之前確定。
this執行會有不同,主要集中在這幾個場景中:作用域
ES6之前沒有塊級作用域,除了全局作用域,函數會創建自己的作用域。 作用域在函數定義的時候已經確定了,不是在函數調用確定(區別于執行上下文環境,this是執行上下文環境中的) 作用域只是一個“地盤”,其中沒有變量。變量是通過作用域對應的執行上下文環境中的變量對象來實現的。所以作用域是靜態的,而執行上下文是動態的。 有閉包存在的時候,一個作用域存在兩個上下文環境也是有的。 也就是說,作用域只是用于劃分你在這個作用域里面定義的變量的有效范圍,出了這個范圍就無效
作用域鏈
函數在定義的時候就確定了函數體內部自由變量的作用域 自由變量:比如a,在fn作用域使用,但是并沒有在fn作用域定義。這就是自由變量 let a = 100 function fn() {let b = 20function bar() {console.log(a + b) // a是自由變量}return bar }let x = fn(), b = 200 x() 那么自由變量是如何得到的?這就引出了作用域鏈 bar要取得a的值,就要在bar函數的作用域中取值,如果沒有,就往上找,找到fn作用域內,也沒有定義a,繼續往上找,就找到全局作用域,找到就結束了。這就是作用域鏈 講完這些,我們再通過一個例子來理解閉包 1 function F1() { 2 var a = 100 3 return function () { 4 console.log(a) 5 } 6 } 7 var f1 = F1() 8 var a = 200 9 f1()?
自由變量將從作用域鏈中去尋找,但是 依據的是函數定義時的作用域鏈,而不是函數執行時,以上這個例子就是閉包。 怎么理解依據的是函數定義時的作用域鏈,而不是函數執行時這句話? 調用第9行之后 如果按照執行時,就輸出的時200 但是作用域都是在定義時就生成了,所以f1回去再定義function的作用域去找,因此輸出100. 理解閉包之后,我們就看看閉包的主要場景:- 函數作為返回值,上面的例子就是
- 函數作為參數傳遞
?
轉載于:https://www.cnblogs.com/thonrt/p/10333581.html
總結
以上是生活随笔為你收集整理的js - 执行上下文和作用域以及闭包的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 20150728月度会议
- 下一篇: Django(6)