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