摘要:所以的作用域是靜態作用域,也叫詞法作用域。總結是一門基于詞法作用域靜態作用域的語言,會沿著作用域鏈像氣泡一樣向外部尋找變量聲明。又是函數作用域的語言,在中,使用和關鍵字后,能讓變量處于塊作用域中,而且不存在聲明提升。
本文共 1700 字,讀完只需 7 分鐘概述
變量,編程語言中我們用來模擬現實概念的工具,比方說,變量可以表示對象,數組,數字,字符。既然是工具,那么就用工具的適用范圍,這個工具在這個適用范圍中才有效,在編程語言中,我們稱這個適用范圍叫作用域(scope)。
本文會總結 JS 中作用域的相關概念。
什么是作用域
全局作用域
函數作用域
塊級作用域
詞法作用域(靜態作用域)
作用域鏈
一、什么是作用域?作用域, 英文意思是 scope, 我自己的話來理解就是:
變量訪問規則的有效范圍
作用域外,無法引用作用域內的變量
離開作用域后,作用域的變量的內存空間會被清除,比如執行完函數或者關閉瀏覽器。
二、全局作用域先看一段代碼:
foo = "bar"; console.log(window.foo); // bar
在瀏覽器環境中聲明變量,該變量會默認成為全局 windows 對象的屬性。
再看下面這段代碼:
function foo() { name = "bar" } foo(); console.log(window.name) // bar
在函數中,如果不加 bar聲明一個變量,那么這個變量會默認被聲明為全局變量,如果是嚴格模式則會報錯。
全局變量可以在任何地方訪問到,但是有很大的問題存在。
全局變量會造成命名污染,如果在多處對同一個全局變量進行操作,那么就會覆蓋全局變量的定義。同時全局變量數量過多,非常不方便管理。
這也是為什么像jQuery 和 underscore 這樣的類庫,要在全局建立 $ 和 _ 變量,其余私有方法屬性掛載到該全局變量下。
三、函數作用域JS 是函數作用域,在函數中定義一個局部變量,那么該變量只可以在該函數作用域中被訪問。
function doSomething() { var thing = "吃早餐"; } console.log(thing); // Uncaught ReferenceError: thing is not defined
嵌套函數作用域:
function outter() { var thing = "吃早餐"; function inner() { console.log(thing); } inner(); } outter(); // 吃早餐
在外層函數中,嵌套一個內層函數,那么這個內層函數可以向上訪問到外部作用域的變量。
那么,既然內層函數可以訪問到外層函數的變量,那么把內層函數返回后呢?
function outter() { var thing = "吃晚餐"; function inner() { console.log(thing); } return inner; } var foo = outter(); foo(); // 吃晚餐 前面我們提到了,函數執行完后,函數作用域的變量會被垃圾回收,以上代碼可以看出當我們返回了一個訪問了外部函數變量的內部函數,最后外部函數的變量得以保存。 這種當變量存在的函數已經執行結束,但仍在可以訪問的方式就是`閉包`。 閉包的具體實踐,后續文章會詳細說明。四、塊級作用域
JS 在 ES6 之前只有函數作用域,沒有塊級作用域的概念。
看一下代碼:
function doSomething() { for (var i = 0; i < 10; i++) { ... } console.log(i) } doSomething(); // 10
由于 JS 沒有塊級作用域,變量 i 在函數作用域中只有一個,每次 for 循壞都在改變這一個變量。
再看阮一峰老師 ES6 教程里的一段代碼:
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10;
以上代碼中,由于沒有塊級作用域,i 變量全局只有一個,當 for 循壞結束,變量 i 的值等于 10, 所以 a[6]() 對應函數內的變量 i 的打印值就是 10。
ES 6 中通過 let 和 const關鍵字 引用了塊級作用域的概念,所謂塊級作用域,就是以 {}包裹的區域。
我們將阮一峰老師 ES6 教程里的一段代碼改成 let 的形式:
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6;
這時,數組內的索引為6函數內的變量打印值為6,每次循環,會創建新的塊級作用域,然后重新聲明一個新的變量 i;JS 的解釋引擎會記住上次循環的變量值,所以能夠返回正確的結果。
let 和 const 會聲明一個塊級作用域的變量及常量,不易發生變量命名污染的問題,能規避沖突,幫助你寫出簡潔優雅的代碼,建議一直使用。
五、詞法作用域(靜態作用域)詞法作用域,也可以叫做靜態作用域,是什么意思呢?
無論函數在哪里調用,詞法作用域都只由函數被聲明時所處的位置決定。
既然有靜態作用域,那么也有動態作用域。
而動態作用域的作用域則是由函數被調用執行的位置所決定。
var a = 123; function func1() { console.log(a); } function func2() { var a = 456; func1(); } func2(); // 123
以上代碼,最后輸出結果 a 的值,來自于 func1 聲明時所在位置訪問到的 a 值 123。
所以 JS 的作用域是靜態作用域,也叫詞法作用域。
六、作用域鏈在 JS 引擎中,通過標識符查找標識符的值,會從當前作用域向上尋找,直到作用域找到第一個匹配的標識符為止。就是 JS 的作用域鏈
如果嵌套作用域有多個相同標識符,那么,最內部的標識符會覆蓋外層標識符,這叫做“遮蔽效應”
var a = 1; function func1() { var a = 2; function func2() { var a = 3; console.log(a); // 3 } func2(); } func1(); // 3
func2 中變量 a,會從內部開始向外部上層尋找,找到最近的 a 標識符的聲明為止。
JS 是一門基于詞法作用域(靜態作用域)的語言,JS 會沿著作用域鏈像氣泡一樣向外部尋找變量聲明。
JS 又是函數作用域的語言,在 ES6 中,使用 let 和 const 關鍵字后,能讓變量處于塊作用域中,而且不存在聲明提升。
后面的文章會介紹 JS 中的聲明提升和閉包,敬請期待。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/98330.html
摘要:查詢是在作用域鏈中,一級級的往上查找該變量的引用。作用域和作用域鏈作用域的概念,應該兩張圖幾句話就能解釋吧。這個建筑代表程序中的嵌套作用域鏈。一層嵌一層的作用域形成了作用域鏈,變量在作用域鏈中的函數內得到了自己的定義。 javascript作用域和閉包之我見 看了《你不知道的JavaScript(上卷)》的第一部分——作用域和閉包,感受頗深,遂寫一篇讀書筆記加深印象。路過的大牛歡迎指點...
摘要:關于兩個專業術語的討論起自對你不知道的一書的閱讀學習。遇到,編譯器會詢問作用域是否已經有一個該名稱的變量存在于同一個作用域的集合中。摘錄來自你不知道的。 JS 編譯之 LHS RHS 一、前言 最近和朋友聊技術的時候,聊到 LHS RHS,我竟然沒聽說過 沒聽說過。。。 于是成功引起了我的好奇心。 關于兩個專業術語的討論起自對《你不知道的JavaScript》一書的閱讀學習。 二、編譯...
摘要:對象數組初始化表達式,闖關記之上文檔對象模型是針對和文檔的一個。闖關記之數組數組是值的有序集合。數組是動態的,根闖關記之語法的語法大量借鑒了及其他類語言如和的語法。 《JavaScript 闖關記》之 DOM(下) Element 類型 除了 Document 類型之外,Element 類型就要算是 Web 編程中最常用的類型了。Element 類型用于表現 XML 或 HTML 元素...
摘要:的變量作用域是基于其特有的作用域鏈的。需要注意的是,用創建的函數,其作用域指向全局作用域。所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體。 作用域 定義 在編程語言中,作用域控制著變量與參數的可見性及生命周期,它能減少名稱沖突,而且提供了自動內存管理 --javascript 語言精粹 我理解的是,一個變量、函數或者成員可以在代碼中訪問到的范圍。 js的變量作...
閱讀 2897·2021-11-24 09:39
閱讀 2465·2019-08-30 15:53
閱讀 3036·2019-08-30 13:47
閱讀 1316·2019-08-30 12:50
閱讀 1487·2019-08-29 16:31
閱讀 2651·2019-08-29 13:14
閱讀 1568·2019-08-29 10:55
閱讀 802·2019-08-26 13:32