欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解JavaScript中的作用域

 更新時間:2023年08月31日 09:15:44   作者:阿宇的編程之旅  
作用域是JavaScript中一個重要的概念,它決定了變量和函數(shù)在代碼中的可訪問性和可見性,了解JavaScript的作用域?qū)τ诰帉懜咝?、可維護的代碼至關(guān)重要,本文將深入介紹JavaScript作用域相關(guān)的知識點,其中包括作用域類型,作用域鏈,變量提升以及閉包等

什么是作用域

JS中的作用域是一個存儲變量、函數(shù)以及對象的位置,每個變量、函數(shù)和對象都被存儲在一個特定的作用域中,它是指變量和函數(shù)在代碼中的可訪問范圍,作用域決定了代碼中哪些部分可以訪問特定的變量和函數(shù),通過作用域,我們可以將變量和函數(shù)封裝在不同的作用域中,使其在合適的范圍內(nèi)可訪問

就像上圖中的示例,F(xiàn)oo1無法獲取Foo2中的var4變量

作用域類型

常見的作用域類型包括全局作用域(Global Scope)、局部作用域(Local Scope)和塊級作用域(Block Scope),JS也不例外

全局作用域

全局作用域是在整個代碼中都可訪問的作用域。在瀏覽器環(huán)境中,全局作用域通常是指window對象;在node環(huán)境下,則是globalThis或global

在全局作用域中聲明的變量或函數(shù)是全局變量或全局函數(shù),在代碼任何地方都可以訪問和使用

比如上面圖示中的Foo4可以訪問到全局作用域的變量及函數(shù)

const var1 = "var1";
const var2 = "var2";
function Foo1() {
  const var3 = "var3";
}
function Foo2() {
  const var4 = "var4";
  function Foo3() {}
}
function Foo4() {
  console.log(var1, var2, Foo1, Foo2); // var1 var2 [Function: Foo1] [Function: Foo2]
}
Foo4();

局部作用域

JS中的局部作用域一般代指函數(shù)作用域(Function Scope),它是在函數(shù)內(nèi)部聲明的作用域,函數(shù)內(nèi)部的變量和函數(shù)只能在函數(shù)內(nèi)部訪問,外部無法直接訪問

就像上述Foo3的效果,可以訪問到全局以及當(dāng)前所在函數(shù)的作用域的變量及函數(shù)

const var1 = "var1";
const var2 = "var2";
function Foo1() {
  const var3 = "var3";
}
function Foo2() {
  const var4 = "var4";
  function Foo3() {
    console.log(var4, Foo3); // var4 [Function: Foo3]
  }
  Foo3();
}
Foo2();

塊級作用域

塊級作用域是在代碼塊(通常是由大括號{}包裹起來的部分)內(nèi)聲明的作用域。比如if(){...}、for(...){...}、try{...}等

ES6之前

在ES6之前,由于變量都是使用var聲明的,所以沒有塊級作用域此類概念,只有全局作用域和函數(shù)(局部)作用域。那么需要模擬塊級作用域如何怎么操作呢?

答案是使用立即執(zhí)行函數(shù)表達式(IIFE):通過將代碼包裝在匿名函數(shù)中并立即執(zhí)行該函數(shù),可以創(chuàng)建一個獨立的作用域,使得內(nèi)部聲明的變量在函數(shù)外部不可訪問

(function () {
  var var5 = "var5";
  console.log(var5); // var5
})();
console.log(var5); // var5 is not defined

ES6以后

在es6以后,官方推出了塊級作用域的概念,使用let和const關(guān)鍵字聲明的變量具有塊級作用域,它們只能在聲明的代碼塊內(nèi)部訪問。

if (true) {
  let var5 = "var5";
  console.log(var5); // var5
}
console.log(var5); // var5 is not defined

作用域鏈

作用域鏈(Scope Chain)是JS用于解析標(biāo)識符(變量和函數(shù))的機制,它是由多個嵌套的作用域組成的,它決定了變量和函數(shù)的查找順序。

上面我們說到局部作用域時談到的Foo3可以訪問全局以及當(dāng)前函數(shù)作用域中的標(biāo)識符這個特點就歸功于作用域鏈這個概念。當(dāng)訪問一個變量時,JS引擎會先從當(dāng)前作用域開始查找,如果找不到這個名稱的標(biāo)識符則繼續(xù)向上一級作用域查找,直到找到變量或達到全局作用域為止,如果在全局作用域中仍然找不到,則認為該標(biāo)識符未定義。

變量提升

變量提升的概念出現(xiàn)在面試題中的頻率十分高,對于開發(fā)者來說也是不可忽略的重要知識點;

基礎(chǔ)概念

變量提升是JS在代碼執(zhí)行前將變量和函數(shù)聲明提升到作用域頂部的行為,它由JavaScript引擎在代碼執(zhí)行前的編譯階段處理。變量提升影響了整個作用域范圍內(nèi)的代碼,它允許我們在聲明之前使用變量,但是需要注意一點:只有變量聲明被提升,賦值不會提升。了解了上述概念后,我們思考下面的代碼:

console.log(var6); // undefined
var var6 = 10;

上面的代碼相當(dāng)于

var var6;
console.log(var6); // undefined
var6 = 10;

優(yōu)先級問題

當(dāng)同一個作用域中同時出現(xiàn)同名的函數(shù)和變量時,函數(shù)提升的優(yōu)先級更高,也就是說函數(shù)會在變量之上聲明,參考下面的代碼

var a = 10;
function a() {}
console.log(a); // 10

可以看成是

var a; // 函數(shù)a
var a; // 變量a
a = function () {};// 使用function聲明函數(shù)可以看成是聲明+賦值
a = 10;
console.log(a); // 10

何以見得?思考以下代碼

function a() {}
var a;
console.log(a); // [Function: a]

當(dāng)把a的賦值去除時,函數(shù)的賦值順序就可以得到驗證,相當(dāng)于:

var a; // 函數(shù)a
var a; // 變量a
a = function () {};
console.log(a); // [Function: a]

閉包

定義

當(dāng)函數(shù)開始執(zhí)行時,函數(shù)中的變量以及函數(shù)會壓入棧中,那么此時如果當(dāng)前的作用域中有另一個函數(shù)正在使用該作用域的變量,該變量占用的內(nèi)存也不會被垃圾回收機制回收,這個現(xiàn)象就是閉包

換句話說,閉包是指函數(shù)能夠"記住"并訪問其創(chuàng)建時的詞法環(huán)境,在函數(shù)定義的詞法作用域之外執(zhí)行同樣適用

思考以下代碼

const foo = (function iife() {
  const num = 10;
  function foo() {
    return num;
  }
  return foo;
})();
console.log(foo()); // 10

上述代碼中使用立即執(zhí)行函數(shù)iife作為外部函數(shù)的作用域,它返回內(nèi)部函數(shù)foo,而foo函數(shù)使用了iife函數(shù)中的num變量,形成了閉包,最后在iife函數(shù)的外部使用foo時依然可以訪問num變量

特點

  • 即使外部函數(shù)已經(jīng)執(zhí)行完畢,內(nèi)部函數(shù)依然可以訪問外部函數(shù)作用域中的變量(當(dāng)棧將函數(shù)彈出時,變量依然處于內(nèi)存中)
  • 閉包可以持有對外部變量的引用,使得外部變量的值在內(nèi)部函數(shù)中保持活動狀態(tài)(不被垃圾回收機制回收)
  • 閉包中的內(nèi)部函數(shù)可以修改并更新外部變量的值
  • 閉包的函數(shù)可以獲取到創(chuàng)建時的整個作用域鏈的標(biāo)識符
  • 閉包可能會導(dǎo)致內(nèi)存泄漏,被閉包引用的變量無法被垃圾回收機制處理

使用場景

封裝私有變量

JS中沒有TS的private關(guān)鍵詞,無法直接定義私有變量,但是可以通過閉包產(chǎn)生私有環(huán)境作用域(ES2022后引入了#關(guān)鍵字,用于定義私有變量,相比于使用閉包,更直觀和方便)

const Animal = (function () {
  const name = "dog";
  function Animal() {}
  Animal.prototype.getName = function () {
    return name;
  };
  return Animal;
})();
console.log(new Animal().getName()); // dog

延長變量周期

延長變量的生命周期也是閉包的特性之一,該效果通過內(nèi)部函數(shù)對外部作用域的可訪問性實現(xiàn)

function delayMessage(msg) {
  return function () {
    return msg;
  };
}
const msg = delayMessage("msg");
console.log(msg()); // msg

模塊化、命名空間

我們使用JS中的立即執(zhí)行函數(shù)實現(xiàn)了命名空間功能,達到模塊化的效果

var moduleA = (function () {
  var privateVariable = "Hello";
  // 私有函數(shù)
  function privateMethod() {
    console.log(privateVariable);
  }
  return {
    // 公共函數(shù)
    publicMethod: function () {
      privateMethod();
    },
  };
})();
moduleA.publicMethod(); // Hello
console.log(moduleA.privateVariable); // undefined

緩存

閉包還可以用于創(chuàng)建緩存函數(shù),以提高函數(shù)的執(zhí)行效率。緩存函數(shù)可以將輸入?yún)?shù)與其對應(yīng)的結(jié)果保存在內(nèi)部,避免重復(fù)計算

function createCache() {
  var cache = {};
  return function (key, val) {
    if (!cache[key]) {
      cache[key] = val;
      console.log("保存");
    }
    return cache[key];
  };
}
var cacheModule = createCache();
cacheModule("num1", "123"); // 保存
cacheModule("num2", "456"); // 保存
cacheModule("num1", "123");
cacheModule("num2", "456");

ES6的作用域

在ES6以后引入了const、let和箭頭函數(shù),這三者對作用域分別有什么影響呢?

const、let

塊級作用域

上面說到const、let的出現(xiàn)奠定了JS中的塊級作用域的概念,使if、for等語句中也存在作用域這一功能;然而不僅僅是條件語句、循環(huán)語句,使用{...}定義的范圍都是塊級作用域,這意味著在塊級作用域內(nèi)部聲明的變量只在該作用域內(nèi)有效,并且在作用域外部無法訪問

{
  let msg = "hello";
  console.log(msg); // hello
}
console.log(msg); // msg is not defined

變量提升

使用let和const聲明的變量不會被提升到作用域的頂部,它們只能在聲明后才能被訪問;這點與var不太一樣

暫時性死區(qū)

暫時性死區(qū)指的是在變量聲明前訪問變量會拋出錯誤。只有在變量聲明語句執(zhí)行完成之后,變量才會進入有效狀態(tài),才能被訪問和使用。這點效果與上面的變量提升效果一樣

不可重復(fù)聲明

在同一個作用域中不能被重復(fù)聲明,否則會報錯;而使用var定義變量時后聲明的會覆蓋先聲明的

這三個特點可以參考下面的代碼:

console.log(var1); // 在賦值前使用了變量“var1”
const var1 = 11;
let var2 = 22; // 無法重新聲明塊范圍變量“var2”
let var2 = 22;

箭頭函數(shù)

箭頭函數(shù)在JavaScript中仍然與普通函數(shù)一樣有函數(shù)作用域的概念

題外話

動態(tài)作用域與詞法作用域

作用域的種類有兩種:分別是動態(tài)作用域和詞法作用域(靜態(tài)作用域)。上述我們介紹的是詞法作用域,什么是詞法作用域?

詞法作用域

詞法作用域是基于代碼的靜態(tài)結(jié)構(gòu)來確定變量的訪問規(guī)則。也就是說它由變量和函數(shù)在代碼中的聲明位置而不是調(diào)用的位置來確定,思考下面的代碼

function foo1() {
  var var1 = 10; // foo2可訪問的作用域
  // 聲明foo2的作用域
  return function foo2() {
    console.log(var1);// 10
  };
}
var foo2 = foo1();
function foo3() {
  var var1 = 20; // foo2不可訪問的作用域
  // 執(zhí)行foo2的作用域
  foo2();
}
foo3();

在foo1中聲明了函數(shù)foo2,在foo3中執(zhí)行foo2,可以看到,foo2取的var1是聲明foo2的作用域中的變量(10)。這個現(xiàn)象說明JS采用的是詞法作用域。

動態(tài)作用域

那么反之,如果還是上述代碼,foo2取的var1是執(zhí)行foo2的作用域中的變量(20),就說明語言采用的是動態(tài)作用域

總結(jié)

JavaScript作用域與變量提升是編寫高質(zhì)量代碼所必須掌握的重要概念。本文介紹了作用域的定義、作用和目的,以及JavaScript中的不同作用域類型,包括全局作用域、函數(shù)作用域和塊級作用域。我們還討論了變量提升的概念、原理和影響范圍,以及作用域鏈和閉包的關(guān)系。此外,還探討了ES6的作用域,并對比了其與早期作用域的區(qū)別。最后針對動態(tài)作用域與詞法作用域作了一個簡單的說明。

以上就是詳解JavaScript中的作用域的詳細內(nèi)容,更多關(guān)于JavaScript作用域的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 自己編寫的支持Ajax驗證的JS表單驗證插件

    自己編寫的支持Ajax驗證的JS表單驗證插件

    創(chuàng)建一個JavaScript表單驗證插件,可以說是一個繁瑣的過程,涉及到初期設(shè)計、開發(fā)與測試等等環(huán)節(jié)。實際上一個優(yōu)秀的程序員不僅是技術(shù)高手,也應(yīng)該是善假于外物的。本文介紹的這個不錯的JavaScript表單驗證插件,支持ajax驗證,有需要的小伙伴可以參考下
    2015-05-05
  • 前端高頻面試題之JS中堆和棧的區(qū)別和瀏覽器的垃圾回收機制

    前端高頻面試題之JS中堆和棧的區(qū)別和瀏覽器的垃圾回收機制

    本文給大家分享前端高頻面試題JS中堆和棧的區(qū)別和瀏覽器的垃圾回收機制,本文分文別類給大家介紹了棧(stack)和堆(heap)的區(qū)別基本類型和引用類型的相關(guān)知識,瀏覽器垃圾回收機制包括基本概念給大家介紹的非常詳細,需要的朋友參考下吧
    2023-10-10
  • javascript高仿熱血傳奇游戲?qū)崿F(xiàn)代碼

    javascript高仿熱血傳奇游戲?qū)崿F(xiàn)代碼

    這篇文章主要介紹了javascript高仿熱血傳奇游戲的實現(xiàn)代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2018-02-02
  • 仿jQuery的siblings效果的js代碼

    仿jQuery的siblings效果的js代碼

    取一個DOM元素的兄弟節(jié)點,仿jQuery的siblings方法,用原生JS代碼完成
    2011-08-08
  • js腳本編寫簡單刷票投票系統(tǒng)

    js腳本編寫簡單刷票投票系統(tǒng)

    這篇文章主要為大家詳細介紹了js腳本編寫簡單刷票投票系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • JavaScript控制音頻和視頻的播放、暫停、音量

    JavaScript控制音頻和視頻的播放、暫停、音量

    HTML<video>元素用于在HTML或者XHTML文檔中嵌入媒體播放器,用于支持文檔內(nèi)的視頻播放,你也可以將<video>標(biāo)簽用于音頻內(nèi)容,在前端中實現(xiàn)音頻和視頻播放通常涉及使用HTML5的<audio>和<video>元素以及JavaScript來控制這些媒體元素的播放、暫停、音量等屬性
    2023-10-10
  • JavaScript實現(xiàn)簡單驗證碼

    JavaScript實現(xiàn)簡單驗證碼

    這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)簡單驗證碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • 微信小程序上拉加載和下拉刷新功能實現(xiàn)

    微信小程序上拉加載和下拉刷新功能實現(xiàn)

    這篇文章主要介紹了微信小程序上拉加載和下拉刷新功能實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-06-06
  • javascript 判斷頁面訪問方式電腦或者移動端

    javascript 判斷頁面訪問方式電腦或者移動端

    這篇文章主要介紹了 判斷頁面訪問方式電腦或者移動端的相關(guān)資料,這里提供了三種方法,需要的朋友可以參考下
    2016-09-09
  • JS扁平化輸出數(shù)組的2種方法解析

    JS扁平化輸出數(shù)組的2種方法解析

    這篇文章主要介紹了JS扁平化輸出數(shù)組的2種方法解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09

最新評論