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 === window

this: this的值只有在執行的時候才能確認,定義的時候不能確認。因為this是執行上下文的一部分,而執行上下文需要再代碼執行之前確定。

this執行會有不同,主要集中在這幾個場景中:
  1. 作為構造函數執行,構造函數中
  2. 作為對象屬性執行,上述代碼中a.fn()
  3. 作為普通函數執行,上述代碼中fn1()
  4. 用于call apply bind,上述代碼中a.fn.call()

作用域


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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450021.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450021.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450021.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring之JDBCTemplate

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 一、Spring對不同的持久化支持: Spring為各種支持的持久化技術,都提供了簡單操作的模板和回調 ORM持久化技術模…

從螞蟻金服實踐入手,帶你深入了解 Service Mesh

本文整理自螞蟻金服高級技術專家敖小劍在 QCon 上海 2018 上的演講。我是來自螞蟻金服中間件團隊的敖小劍,目前是螞蟻金服 Service Mesh 項目的 PD。我同時也是 Servicemesher 中國技術社區的創始人,是 Service Mesh 技術在國內最早的布道師。我今天給大…

Android應用開發—FragmentManager如何管理fragments

本文主要摘錄自Android中使用FragmentManager管理fragments 和 淺談FragmentManager與fragment之一二事 先講下自己對fragment的理解: 對于fragment,有太多官方文檔和博文來介紹,此處不做轉述:我感覺android提供fragment這種組件…

數組指針 和 指針數組

最近發現公司有些人說怎樣區分 數組指針 和 指針數組 ? 其實 很簡單; 數組指針, 先是(定語 ) (主體), (定語 數組) (主體 指針&#xff09…

在云服務器上注意GeoServer和ShadowDataMap的跨域設置

在云服務器上注意GeoServer和ShadowDataMap的跨域設置 1、對于支持cors的網絡資源 可以在ShadowDataMap的devserverconfig.json里設置相應的跨域資源 提示:geoserver發布的地圖服務雖然同在一個服務器上,但是端口不一樣,同樣需要設置跨域 如&…

Guava ImmutableCollection簡介

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 ImmutableCollection代碼定義 GwtCompatible(emulatedtrue) public abstract class ImmutableCollection<E> extends AbstractCo…

Todo List

fragment里面如何處理back按鍵事件。 fragment里面無法Override onBackPressed接口&#xff0c;如何優雅的處理back press事件&#xff1f;activity如何獲取當前活躍的fragment對象。異步網絡請求如何改造成rxjava&#xff0c;rxjava有設置運行線程的能力&#xff0c;異步請求…

常見的幾種負載均衡算法

1、輪詢將所有請求&#xff0c;依次分發到每臺服務器上&#xff0c;適合服務器硬件相同的場景。優點&#xff1a;服務器請求數目相同&#xff1b; 缺點&#xff1a;服務器壓力不一樣&#xff0c;不適合服務器配置不同的情況&#xff1b; 2、隨機請求隨機分配到各臺服務器上。優…

基于 Token 的身份驗證方法

基于 Token 的身份驗證方法 使用基于 Token 的身份驗證方法&#xff0c;在服務端不需要存儲用戶的登錄記錄。大概的流程是這樣的&#xff1a;客戶端使用用戶名跟密碼請求登錄 服務端收到請求&#xff0c;去驗證用戶名與密碼 驗證成功后&#xff0c;服務端會簽發一個 Token&…

Android應用開發-圖片加載庫Glide

Glide Picasso和Glide之間的區別&#xff1a; Picasso 僅僅緩存了全尺寸的圖像&#xff1b;然而 Glide 緩存了原始圖像&#xff0c;全分辨率圖像和另外小版本的圖像。

excel 表格導入 - java 實現

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 import com.alibaba.druid.support.json.JSONUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; imp…

C語言 API

MySQL的C語言API接口 1、首先當然是連接數據庫&#xff0c;函數原型如下&#xff1a; MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned…

線程組之間的JMeter傳遞變量

下面&#xff0c;我們將看看如何在線程組之間共享和傳遞變量。在開發高級JMeter腳本時&#xff0c;很可能您將擁有多個線程組。每個線程組將執行不同的請求。一個很好的例子是我們需要使用Bearer Tokens對用戶進行身份驗證。一個線程組執行身份驗證并保存令牌。另一個線程組需要…

python第九天(9-33)

一&#xff1a;進程 進程概念 進程就是一個程序運行在一個數據集上的一次動態執行過程進程一般由程序&#xff0c;數據集&#xff0c;進程控制塊組成進程控制塊&#xff1a; 進程控制塊用來記錄進程的外部特征&#xff0c;描述進程的執行變化過程&#xff0c;系統可以利用它來控…

Android Studio出現Failed to open zip file. Gradle's dependency cache may be corrupt問題的解決

刪除了/Users/tycao/.gradle/wrapper/dists目錄下對應的gradle-X.X-all目錄重新sync了

雙機熱備份和負載均衡的區別

1、雙機熱備相當于2臺服務器其中有一臺是另一臺的備機&#xff0c;也可以互為備機&#xff1b;而且這兩臺主機的數據時實時同步的&#xff1b;主機在運行服務時&#xff0c;備機處于檢測狀態&#xff0c;主機發生故障后&#xff0c;備機將接管主機的服務。2、負載均衡是在這2臺…

Python 數據類型--Bytes類型

一、Bytes類型 在Python3以后&#xff0c;字符串和bytes類型徹底分開了。字符串是以字符為單位進行處理的&#xff0c;bytes類型是以字節為單位處理的。 bytes數據類型在所有的操作和使用甚至內置方法上和字符串數據類型基本一樣&#xff0c;也是不可變的序列對象。 bytes對象只…

Android應用開發-onNewIntent()

轉載自Android中 activity 的onNewIntent函數

文件結構

C語言文件名命名的規則 1、文件標識符分為兩部分&#xff0c;即文件名前綴和后綴。文件名前綴的最前面要使用范圍限定符——模塊名&#xff08;文件名&#xff09;縮寫。 2、采用小寫字母命名文件&#xff0c;避免使用一些比較通俗的文件名&#xff0c;如&#xff1a;publi…

POJ 1187 隕石的秘密 (線性DP)

題意&#xff1a; 公元11380年&#xff0c;一顆巨大的隕石墜落在南極。于是&#xff0c;災難降臨了&#xff0c;地球上出現了一系列反常的現象。當人們焦急萬分的時候&#xff0c;一支中國科學家組成的南極考察隊趕到了出事地點。經過一番偵察&#xff0c;科學家們發現隕石上刻…