最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript作用域與作用域鏈優(yōu)化方式

 更新時(shí)間:2022年07月22日 09:33:02   作者:??Hardy_?  
這篇文章主要介紹了JavaScript作用域與作用域鏈優(yōu)化方式,圍繞主題展開JavaScript作用域以及作用域鏈的那些事情,以及針對(duì)它們的一些我們?cè)诖a中優(yōu)化小技巧,需要的朋友可以參考一下

前言

作用域和作用域鏈?zhǔn)撬蠮avaScript開發(fā)人員每天都要接觸和應(yīng)用的內(nèi)容。不管是面試中的作用域鏈的面試考察,還是日常代碼研發(fā)中變量與作用域鏈的構(gòu)建,它的身影幾乎無處不在。它就像一頂優(yōu)秀廚師的廚師帽,只要我們走進(jìn)廚房,我們就要將它整理好,套在頭上。沒有它整潔干凈的戴在頭上,你就不是一名好的JavaScript工程師。

其實(shí),作為一名前端工程師,我也曾經(jīng)疑惑過:基本上所有的計(jì)算機(jī)語言都具有作用域的概念,但是為何JavaScript開發(fā)人員總是對(duì)作用域這個(gè)概念執(zhí)著不已?直到,我多次在編寫代碼過程中遇到涉及到作用域的問題后,我才漸漸了解這個(gè)問題并去仔細(xì)研究。

而這篇文章,就是想要和大家聊聊有關(guān)JavaScript作用域以及作用域鏈的那些事情,以及針對(duì)它們的一些我們?cè)诖a中優(yōu)化小技巧。

內(nèi)容

對(duì)于幾乎所有的編程語言來說,最基本的功能之一,就是儲(chǔ)存變量當(dāng)中的值并且能在之后對(duì)這個(gè)值進(jìn)行訪問和修改。這種能力的引入,是程序的狀態(tài)存在的基礎(chǔ)。但是,能力的引入需要我們解決幾個(gè)問題,例如:變量存儲(chǔ)在哪里?以何種形式存儲(chǔ)?需要讀取和修改變量的時(shí)候,以什么方式獲取到這個(gè)變量?

很明顯,為了解決這些問題,我們需要一套設(shè)計(jì)良好的規(guī)則來存儲(chǔ)變量,并且之后可以方便的找到這些變量。與此同時(shí),整套完整規(guī)則的設(shè)計(jì)就會(huì)衍生出額外規(guī)則概念。而作用域,就是這套規(guī)則下衍生出來的概念。

作用域

我們可以把作用域理解為上面講到的這套規(guī)則下的限定范圍。作用域的職責(zé)是,在這段限定范圍中根據(jù)這套設(shè)計(jì)好的規(guī)則存儲(chǔ)所聲明的變量,并且提供修改該變量的支持。在變量的訪問權(quán)限安全上,作用域還承擔(dān)著保護(hù)當(dāng)前作用域內(nèi)的變量不被外部作用域訪問的權(quán)限保護(hù)作用。

通過類比,我們可以把作用域想象成一個(gè)氣泡。在這個(gè)氣泡里所聲明的變量成員被包含在其中。每個(gè)氣泡都配備有一位有原則的管家,將所有的成員管理起來,并針對(duì)他們聲明的位置和要求對(duì)它們提供保護(hù)。當(dāng)氣泡中代碼語句想要訪問和修改變量成員時(shí),管家會(huì)結(jié)合變量成員的要求關(guān)聯(lián)對(duì)應(yīng)訪問和修改操作。

隨著ECMAScript標(biāo)準(zhǔn)的不斷發(fā)展和完善,JavaScript目前存在著四種作用域類型:

  • 全局作用域(Global Scope): JavaScript語言環(huán)境的最頂級(jí)作用域,在語言環(huán)境初始化時(shí)創(chuàng)建。
  • 模塊作用域(Module Scope): 由ECMAScript模塊標(biāo)準(zhǔn)(ES Module)引入,在解析ECMAScript模塊時(shí)創(chuàng)建。
  • 函數(shù)作用域(Function Scope): 在函數(shù)聲明function() {}或者() => {}時(shí)創(chuàng)建。
  • 塊級(jí)作用域(Block Scope): 由ECMAScript2015的變量聲明標(biāo)識(shí)符letconst引入,在使用這兩者進(jìn)行變量聲明時(shí),根據(jù)最近的一對(duì)花括號(hào){}創(chuàng)建。
/* 全局作用域 start,JavaScript語言環(huán)境初始化時(shí)就被創(chuàng)建 */
/* 模塊作用域 start,作為ES Module解析和執(zhí)行時(shí)被創(chuàng)建 */
let name = 'Wu';
{
  /* 塊級(jí)作用域 start,const進(jìn)行變量聲明在最近的花括號(hào){}內(nèi)創(chuàng)建 */
  const prefix = Hardy;
  name = prefix + name;
  /* 塊級(jí)作用域 end */
}
export function sayMyName(myName) {
  /* 函數(shù)作用域 start,函數(shù)聲明時(shí)自動(dòng)創(chuàng)建,初始化默認(rèn)包含函數(shù)的形參變量 */
  if (!myName) {
    /* 塊級(jí)作用域 start */
    const noNameAnswer = 'Sorry!';
    console.log(noNameAnswer);
    return;
    /* 塊級(jí)作用域 end */
  }
  const wordPrifix = 'Hi! My Name is ';
  const answer = wordPrifix + myName + '.';
  console.log(answer);
  /* 函數(shù)作用域 end */
}
/* 模塊作用域 end */
/* 全局作用域 end */

作用域的嵌套

作用域在使用上具有嵌套特征。一個(gè)作用域能夠在自身內(nèi)部創(chuàng)建一個(gè)新作用域從而形成內(nèi)部和外部作用域的嵌套關(guān)系。

全局作用域作為JavaScript的初始作用域,是所有其他作用域最外層的作用域。另外,每一個(gè)ES Module都具有模塊自己的頂級(jí)作用域(top-level scope),模塊中的頂級(jí)作用域變量和函數(shù)都包含在這個(gè)模塊頂級(jí)作用域中,而模塊作用域的外部作用域是全局作用域。而函數(shù)作用域和塊級(jí)作用域則相對(duì)比較靈活,可以相互嵌套。

作用域的一些實(shí)現(xiàn)細(xì)節(jié)

在JavaScript中,每一個(gè)函數(shù)、代碼塊{...}以及script腳本被運(yùn)行前,都會(huì)有一個(gè)相對(duì)應(yīng)的稱為詞法環(huán)境(Lexical Environment) 的內(nèi)部關(guān)聯(lián)對(duì)象被創(chuàng)建。

詞法環(huán)境由兩部分組成:

  • 環(huán)境記錄(Environment Record):一個(gè)存儲(chǔ)所有局部變量作為其屬性(包括一些執(zhí)行上下文信息,例如this的值)的對(duì)象。
  • 外部詞法環(huán)境引用(Outer):對(duì)外部詞法環(huán)境的引用,以此關(guān)聯(lián)外部詞法環(huán)境。

代碼執(zhí)行的過程中,每一個(gè)局部變量和局部函數(shù)的聲明,都會(huì)作為一個(gè)屬性字段被添加到環(huán)境記錄中,后續(xù)對(duì)變量和函數(shù)的讀取則通過對(duì)應(yīng)標(biāo)識(shí)符在環(huán)境記錄中進(jìn)行查找。

根據(jù)上面的概念,我們可以通過下面的對(duì)象結(jié)構(gòu)理解詞法環(huán)境:

  lexicalEnvironment = {
    environmentRecord: {
      <identifier>: <value>,
      <identifier>: <value>,
    },
    outer: <Reference to the parent lexical environment>,
  }

再來通過下面的代碼例子來理解詞法環(huán)境:

/*
  當(dāng)前模塊運(yùn)行時(shí),模塊的詞法環(huán)境被創(chuàng)建,
  moduleLexicalEnvironment = {
    environmentRecord: {
      name: <uninitialized>,
      sayName: <reference to function object>,
    },
    outer: <globalLexicalEnvironment>,
  }
*/
let name = 'Hardy';
/*
  變量聲明和賦值,修改環(huán)境記錄的字段屬性值,
  moduleLexicalEnvironment = {
    environmentRecord: {
      name: 'Hardy',
      sayName: <reference to function object>,
    },
    outer: <globalLexicalEnvironment>,
  }
*/
function sayName(myName) {
  /*
    執(zhí)行函數(shù)時(shí),函數(shù)的詞法環(huán)境被創(chuàng)建,
    functionLexicalEnvironment = {
      environmentRecord = {
        myName: 'Hardy',
      },
      outer: <moduleLexicalEnvironment>,
    }
  */
  /* 通過讀取環(huán)境記錄的對(duì)應(yīng)標(biāo)識(shí)符字段屬性值獲取myName的變量值 */
  console.log(myName);
}
sayName(); // Hardy

我們來分析下上面的代碼例子:

根據(jù)聲明提前的特性,變量name和函數(shù)sayName都會(huì)在模塊的詞法環(huán)境創(chuàng)建時(shí)被添加在環(huán)境記錄中。但是,由于let的暫時(shí)性死區(qū)特性,變量name在自身聲明和初始化賦值之前處于不可引用和未初始化狀態(tài)。函數(shù)的聲明則不同,除了聲明提前外還會(huì)初始化函數(shù)的引用。這就是我們可以在函數(shù)執(zhí)行聲明語句前調(diào)用函數(shù)的原因。另外,函數(shù)的詞法環(huán)境在被創(chuàng)建時(shí),對(duì)應(yīng)函數(shù)的參數(shù)會(huì)被初始化在環(huán)境記錄中,并且會(huì)被賦值上調(diào)用函數(shù)時(shí)的所傳值或者函數(shù)參數(shù)的默認(rèn)值。

outer引用方面,模塊詞法環(huán)境moduleLexicalEnvironmentouter引用指向JavaScript最外部的全局詞法環(huán)境globalLexicalEnvironment,而函數(shù)詞法環(huán)境functionLexicalEnvironmentouter引用指向外部的模塊詞法環(huán)境moduleLexicalEnvironment。

我們可以看出,詞法環(huán)境是JavaScript對(duì)作用域概念的內(nèi)部技術(shù)實(shí)現(xiàn)。它是JavaScript引擎創(chuàng)建一個(gè)執(zhí)行上下文時(shí),創(chuàng)建用來存儲(chǔ)變量和函數(shù)聲明的環(huán)境。代碼執(zhí)行過程中,通過它訪問到存儲(chǔ)在其內(nèi)部的變量和函數(shù)。在代碼執(zhí)行完畢后,執(zhí)行上下文會(huì)從堆棧中被銷毀回收,而詞法環(huán)境也會(huì)根據(jù)情況的被銷毀(如果詞法環(huán)境被其他外部的詞法環(huán)境所引用,則不會(huì)被銷毀回收,例如閉包)。

作用域鏈

作用域可以嵌套,嵌套在內(nèi)部的作用域可以訪問外部的作用域所聲明的變量和函數(shù)。通過上面詞法環(huán)境的介紹,我們大概清楚,作用域的這種嵌套關(guān)系是通過詞法環(huán)境的外部詞法環(huán)境引用outer來關(guān)聯(lián)實(shí)現(xiàn)的。這種詞法環(huán)境的外部引用的關(guān)聯(lián)關(guān)系,構(gòu)建了一條單向的詞法環(huán)境的鏈條。這就是我們常說的作用域鏈。

本質(zhì)上,作用域鏈?zhǔn)荍avaScript引擎給所執(zhí)行代碼維護(hù)的一條詞法環(huán)境鏈條。代碼執(zhí)行中對(duì)外部作用域的變量的引用,通過這一條鏈條進(jìn)行變量的查找、讀取、修改。

代碼執(zhí)行中對(duì)某個(gè)變量的訪問大致如下:

  • 當(dāng)代碼要訪問一個(gè)變量時(shí),首先會(huì)搜索當(dāng)前內(nèi)部詞法環(huán)境。如果搜索成功,就返回對(duì)一個(gè)變量值或變量引用,結(jié)束搜索。如果搜索不到,則通過outer引用繼續(xù)搜索外部詞法環(huán)境,以此類推,直到全局詞法環(huán)境。
  • 如果在任何地方都找不到這個(gè)變量,那么在嚴(yán)格模式下就會(huì)報(bào)錯(cuò)。

根據(jù)上面的概念,我們來看看下面的例子:

let phrase = 'Hello';

function sayHello(name) {
  /*
    函數(shù)的作用域鏈,
    functionLexicalEnvironment{ name: 'Hardy' } ==outer==>
    moduleLexicalEnvironment{ phrase: 'Hello' } ==outer==>
    globalLexicalEnvironment

    變量name從當(dāng)前functionLexicalEnvironment中查找到并獲取,
    變量phrase沿作用域鏈查找,從moduleLexicalEnvironment中查找到并獲取
  */
  console.log(`${phrase}, ${name}!`);
}
sayHello('Hardy'); // Hello, Hardy!

上面例子中,函數(shù)sayHello在內(nèi)部引用了namephrase兩個(gè)變量,函數(shù)被調(diào)用的執(zhí)行時(shí)會(huì)創(chuàng)建functionLexicalEnvironment > moduleLexicalEnvironment > globalLexicalEnvironment的作用域鏈。

其中,變量name作為函數(shù)參數(shù)屬于當(dāng)前函數(shù)作用域的局部變量,變量可以直接從當(dāng)前函數(shù)的詞法環(huán)境functionLexicalEnvironment中查找到并返回相關(guān)信息。而變量phrase屬于外部作用域中聲明的變量,存儲(chǔ)在外部的模塊詞法環(huán)境moduleLexicalEnvironment中。函數(shù)sayHello引用變量phrase,會(huì)首先從在自身函數(shù)詞法環(huán)境functionLexicalEnvironment中進(jìn)行查找,查找不到后,會(huì)沿外部詞法環(huán)境引用outer找到模塊詞法環(huán)境moduleLexicalEnvironment,并從中繼續(xù)進(jìn)行變量的查找,查找到了并返回變量的相關(guān)信息。

值得注意的是console.log()是全局內(nèi)置對(duì)象console上的方法,對(duì)該方法的調(diào)用需要引用console。這個(gè)變量的引用會(huì)沿作用域鏈一直查找到全局詞法環(huán)境globalLexicalEnvironment中,從中查找到并返回相關(guān)變量信息。

變量標(biāo)識(shí)符解析和引用的過程就是沿作用域鏈迭代查找變量是否在作用域鏈節(jié)點(diǎn)中并返回變量相關(guān)信息的過程。

相關(guān)優(yōu)化

綜合上面的標(biāo)識(shí)符的解析過程和作用域以及作用域鏈的關(guān)系,我們可以了解到,變量標(biāo)識(shí)符解析的性能是和變量標(biāo)識(shí)符所處在作用域鏈中的位置是息息相關(guān)的。變量標(biāo)識(shí)符所出的作用域節(jié)點(diǎn)越靠近整個(gè)作用域鏈的前端,則需要沿作用域鏈迭代查找的次數(shù)就越少,變量標(biāo)識(shí)符解析的速度就會(huì)越快,性能就越好。

這種標(biāo)識(shí)符解析性能的規(guī)律,讓我們可以得出以下使用變量的優(yōu)化點(diǎn):

  • 對(duì)于頻繁引用的外部作用域的變量,可以根據(jù)情況在當(dāng)前作用域內(nèi)聲明賦值為局部變量后使用。
  • 減少作用域增強(qiáng)with語句的使用。

外部作用域變量標(biāo)識(shí)符的多次引用,會(huì)造成執(zhí)行過程中的標(biāo)識(shí)符解析沿作用域鏈查找的頻繁執(zhí)行,這種查找在第一次解析引用時(shí)是必須的,但是后續(xù)解析引用卻是重復(fù)的。將外部作用域變量通過在當(dāng)前作用域內(nèi)聲明賦值為局部變量,可以優(yōu)化后續(xù)查找的需要經(jīng)過的作用域鏈節(jié)點(diǎn)個(gè)數(shù),得到一定的性能提升。

with語句可以在當(dāng)前作用域鏈前端臨時(shí)添加一個(gè)詞法環(huán)境,從而在位置構(gòu)建和使用新的作用域鏈。但是這方式問題也很顯而易見:作用域鏈被加長(zhǎng)了,除了被添加到前端的詞法環(huán)境中的存儲(chǔ)的變量外,其他變量的標(biāo)識(shí)符解析性能都會(huì)變差。因此,我們應(yīng)該減少with語句的使用。

總結(jié)

隨著JavaScript語言的發(fā)展,語言中的作用域的種類也變得豐富起來,不再局限于函數(shù)作用域作為最小變量聲明范圍來使用,而是可以基于更小范圍的跨級(jí)作用域來管理我們的變量引用范圍。變量的管理變得更加的靈活、安全。

作用域鏈?zhǔn)亲饔糜蜴溓短椎慕Y(jié)構(gòu)產(chǎn)物,所有變量標(biāo)識(shí)符的解析和引用會(huì)沿著作用域鏈進(jìn)行查找。而詞法環(huán)境,是JavaScript對(duì)于作用域的內(nèi)部技術(shù)實(shí)現(xiàn)。深入了解詞法環(huán)境后,也讓我們更清楚代碼在解析變量標(biāo)識(shí)符時(shí)的內(nèi)部執(zhí)行過程。也根據(jù)這個(gè)過程,我們大概總結(jié)出了兩點(diǎn)關(guān)于作用域和變量使用的性能優(yōu)化點(diǎn)。

作用域的使用作為每一位JavaScript開發(fā)人員的必修課,了解得深入才能在使用它的時(shí)候不再迷茫。它就像空氣,存在于JavaScript的許多地方,值得我們?nèi)ズ煤昧私狻?/p>

到此這篇關(guān)于JavaScript作用域與作用域鏈優(yōu)化方式的文章就介紹到這了,更多相關(guān)JS 作用域鏈優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JS中mouseup事件丟失的原因與解決辦法

    JS中mouseup事件丟失的原因與解決辦法

    這篇文章主要跟大家介紹了關(guān)于Javascript中mouseup事件丟失的原因與解決辦法的相關(guān)資料,文中給出詳細(xì)的示例代碼供大家參考學(xué)習(xí),需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。
    2017-06-06
  • javascript實(shí)現(xiàn)點(diǎn)擊按鈕切換圖片

    javascript實(shí)現(xiàn)點(diǎn)擊按鈕切換圖片

    這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)點(diǎn)擊按鈕切換圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • JavaScript中數(shù)組嵌套對(duì)象排序方法的示例詳解

    JavaScript中數(shù)組嵌套對(duì)象排序方法的示例詳解

    在?JavaScript?中,可以使用?sort()?方法對(duì)包含嵌套對(duì)象的數(shù)組進(jìn)行排序,本文將通過三個(gè)簡(jiǎn)單的示例為大家進(jìn)行簡(jiǎn)單的介紹,需要的可以參考下
    2024-03-03
  • uniapp中vuex的應(yīng)用使用步驟

    uniapp中vuex的應(yīng)用使用步驟

    Vuex是一個(gè)專為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式,它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化,下面這篇文章主要給大家介紹了關(guān)于uniapp中vuex的應(yīng)用使用,需要的朋友可以參考下
    2022-08-08
  • 全面解析Bootstrap中form、navbar的使用方法

    全面解析Bootstrap中form、navbar的使用方法

    這篇文章主要為大家詳細(xì)解析了Bootstrap中form、navbar的使用方法,感興趣的朋友可以參考一下
    2016-05-05
  • 可多次使用的仿126郵箱選項(xiàng)卡的源碼

    可多次使用的仿126郵箱選項(xiàng)卡的源碼

    可多次使用的仿126郵箱選項(xiàng)卡的源碼...
    2007-10-10
  • 結(jié)合ES6?編寫?JavaScript?設(shè)計(jì)模式中的結(jié)構(gòu)型模式

    結(jié)合ES6?編寫?JavaScript?設(shè)計(jì)模式中的結(jié)構(gòu)型模式

    這篇文章主要介紹了結(jié)合ES6編寫JavaScript?設(shè)計(jì)模式中的結(jié)構(gòu)型模式,設(shè)計(jì)模式是軟件設(shè)計(jì)中常見問題的解決方案,這些模式很容易重復(fù)使用并且富有表現(xiàn)力
    2022-07-07
  • 在javaScript中檢測(cè)數(shù)據(jù)類型的幾種方式小結(jié)

    在javaScript中檢測(cè)數(shù)據(jù)類型的幾種方式小結(jié)

    在用javaScript編程的過程中,我們經(jīng)常會(huì)遇到這樣一個(gè)問題,就是需要檢測(cè)一個(gè)數(shù)據(jù)或變量的類型,本篇文章主要介紹了在javaScript中檢測(cè)數(shù)據(jù)類型的幾種方式小結(jié),有興趣的可以了解一下。
    2017-03-03
  • js chrome瀏覽器判斷代碼

    js chrome瀏覽器判斷代碼

    今天發(fā)現(xiàn)chrome還是有一些獨(dú)一無二的東西,基于它們,我們就可以搞出基于特征的判定了。
    2010-03-03
  • 基于Bootstrap表單驗(yàn)證功能

    基于Bootstrap表單驗(yàn)證功能

    這篇文章主要為大家詳細(xì)介紹了基于Bootstrap表單驗(yàn)證功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11

最新評(píng)論

朝阳市| 百色市| 宽城| 沂源县| 且末县| 湖口县| 湘西| 长沙市| 临清市| 彭阳县| 宁化县| 江北区| 绥芬河市| 商都县| 汝州市| 奉新县| 马尔康县| 扎兰屯市| 万安县| 怀集县| 东源县| 佳木斯市| 西宁市| 澄江县| 江北区| 明水县| 花莲县| 南平市| 微博| 余庆县| 长丰县| 贵州省| 噶尔县| 杨浦区| 嘉禾县| 五河县| 那坡县| 哈密市| 苗栗市| 杭州市| 烟台市|