摘要:中作用域的問題可以說是老生常談,個(gè)人認(rèn)為的作用域中存在著兩種作用域,一種是詞法作用域,一種是動(dòng)態(tài)作用域。但是自動(dòng)有了箭頭函數(shù)后,箭頭函數(shù)中的并不是動(dòng)態(tài)作用域,而是屬于詞法作用域,再其定義時(shí)就已經(jīng)確定好了,相當(dāng)于。
js中作用域的問題可以說是老生常談,個(gè)人認(rèn)為js的作用域中存在著兩種作用域,一種是詞法作用域,一種是動(dòng)態(tài)作用域。
詞法作用域詞法作用域就是定義在詞法階段的作用域,也就是說由我們寫代碼時(shí)將變量寫在哪里所決定的,當(dāng)然在js中大部分是這種情況。
var a = 20; function foo () { console.log(a); } foo(); // 20 function bar () { var a = 30; foo(); // 20 } bar();
這個(gè)例子就是一個(gè)很好的印證,可以發(fā)現(xiàn)的是,無論foo在哪里調(diào)用,其a的值永遠(yuǎn)是全局作用域中的a的值,這就是詞法作用域,定義函數(shù)時(shí),作用域是在全局,那么foo上層作用域就是全局,改變其調(diào)用位置是不能改變其作用域鏈的,其作用域鏈?zhǔn)窃诙x時(shí)就決定好的。
利用詞法作用域,我們可以引申出閉包的概念,將我們上面的代碼改寫:
var a = 20; function bar () { var a = 30; return function foo () { console.log(a); } } bar()(); // 30
foo函數(shù)在bar函數(shù)內(nèi)定義,所以foo函數(shù)的作用域鏈上層對應(yīng)的是bar的作用域,利用閉包將foo暴露出去,由于foo函數(shù)仍然保持著對bar作用域的引用,所以bar內(nèi)部的作用域依然存在,沒有被回收,這也是閉包能夠產(chǎn)生私有變量等效果的原因。
改變作用域對沒錯(cuò),詞法作用域可以被改變,通過eval或者with就可以將作用域改變,但是這并不被提倡。
eval改變作用域:
var a = 20; function bar () { eval("var a = 30;"); console.log(a); } bar(); // 30
eval內(nèi)的代碼可以看做,本身就寫在了那一行,但是在嚴(yán)格模式下,eval有著自己的作用域,所以嚴(yán)格模式下上面的代碼會(huì)報(bào)出一個(gè)ReferenceError。
with改變作用域:
function foo (obj) { with (obj) { b = 2; a = 2; } } var obj = {a: 1}; foo(obj); console.log(obj, b); // { a: 2 } 2
with可以形成一個(gè)新的作用域,其詞法標(biāo)識符就是這個(gè)對象的屬性,所以可以看到的是a = 2,本質(zhì)上改變的是obj的屬性,而b = 2由于這個(gè)作用域下沒有找到該變量,所以會(huì)沿著作用域鏈向上查找,由于在foo的作用域內(nèi)也沒有找到,一直到全局作用域都沒有找到,所以會(huì)給全局添加一個(gè)屬性,這就將b變?yōu)榱巳肿兞?,所以我們在全局作用域中可以訪問到b。但是我們在with中用var定義變量時(shí),會(huì)將該變量定義到其外層作用域中。
var obj = {a: 1}; with (obj) { var b = 2; var a = 2; } console.log(obj, b, a); // { a: 2 } 2 undefined
所以我們可以看到的with塊內(nèi)的變量也有著提前聲明,但是其提前聲明的位置是其上層作用域中。這種行為實(shí)在是十分讓人理解,將對象放入一個(gè)新的作用域,但是同時(shí)可以給其上層作用域定義變量。當(dāng)然在嚴(yán)格模式中,完全不用擔(dān)心,因?yàn)?b>with在嚴(yán)格模式中被禁用。
動(dòng)態(tài)作用域動(dòng)態(tài)作用域取決于其調(diào)用方式以及在哪里調(diào)用,這個(gè)聽著有點(diǎn)像this啊,沒錯(cuò),在js中唯一的動(dòng)態(tài)作用域就是this,當(dāng)然也可以叫其延遲綁定。在談起this之前,首先要知道的是,執(zhí)行上下文是什么?
每一種代碼的執(zhí)行都依賴于自身的上下文,函數(shù)的每一次調(diào)用都會(huì)進(jìn)入函數(shù)執(zhí)行中的一個(gè)上下文,并且在函數(shù)每次調(diào)用時(shí)都會(huì)產(chǎn)生一個(gè)變量對象(虛擬出來的,真實(shí)代碼中是訪問不出來的,會(huì)被視作undefined),函數(shù)中的每一個(gè)變量都可以視為這個(gè)變量對象的一個(gè)屬性,在進(jìn)入上下文時(shí),首先會(huì)對函數(shù)的形參進(jìn)行操作,將形參添加到變量對象中,然后會(huì)對函數(shù)聲明進(jìn)行操作,假若變量對象中存在同名屬性時(shí),同名屬性將被覆蓋,最后對變量聲明進(jìn)行操作,假若變量對象中存在同名屬性時(shí),該變量則會(huì)被忽略聲明,下面的代碼就可以驗(yàn)證我們的這個(gè)過程:
function foo (fn) { function fn () {} var fn; console.log(fn); // [Function: fn] } foo();
我們的this和執(zhí)行上下文沒有關(guān)系,但是和我們的變量對象有著很大的關(guān)系。
在非嚴(yán)格模式下,this指向null或者undefined時(shí)會(huì)指向全局對象。
以這個(gè)準(zhǔn)則來看我們《JavaScript語言精粹》中提到的函數(shù)的四種調(diào)用方式與this取值的關(guān)系:
1.方法調(diào)用模式
var obj = { a: 2, foo: function () { console.log(this.a); } }; obj.foo();
這種情況下,this指向的就是該對象。
2.函數(shù)調(diào)用模式
var a = 2; function foo () { console.log(this.a); } foo(); // 瀏覽器環(huán)境中:2 // 嚴(yán)格模式下 var a = 2; function foo () { "use strict"; console.log(this.a); // TypeError } foo();
這種情況下this應(yīng)該指向的那個(gè)我們上文所提到的那個(gè)虛擬出來的變量對象,由于其本來并不存在,所以this指向的undefined,在非嚴(yán)格模式下指向的是全局window,但是在嚴(yán)格模式下,禁止了這種隱式的轉(zhuǎn)換。函數(shù)調(diào)用模式下,this其實(shí)可以理解為指向我們虛擬出來的那個(gè)變量對象。
3.構(gòu)造器調(diào)用模式
也就是該函數(shù)被當(dāng)做構(gòu)造函數(shù)來調(diào)用,這是首先會(huì)在函數(shù)內(nèi)部創(chuàng)建一個(gè)空對象,然后在將this指向這個(gè)空對象,最后將這個(gè)對象返回出去。
4.call與apply調(diào)用
這時(shí)this指向的是其第一個(gè)參數(shù)。
但是自動(dòng)有了箭頭函數(shù)后,箭頭函數(shù)中的this并不是動(dòng)態(tài)作用域,而是屬于詞法作用域,再其定義時(shí)就已經(jīng)確定好了,相當(dāng)于function () {}.bind(this)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/79871.html
摘要:也毫不例外,但在中作用域的特性與其他高級語言稍有不同,這是很多學(xué)習(xí)者久久難以理清的一個(gè)核心知識點(diǎn)。主要使用的是函數(shù)作用域。 關(guān)于作用域:About Scope 作用域是程序設(shè)計(jì)里的基礎(chǔ)特性,是作用域使得程序運(yùn)行時(shí)可以使用變量存儲(chǔ)值、記錄和改變程序的狀態(tài)。JavaScript 也毫不例外,但在 JavaScript 中作用域的特性與其他高級語言稍有不同,這是很多學(xué)習(xí)者久久難以理清的一個(gè)核...
摘要:作用域分類作用域共有兩種主要的工作模型。換句話說,作用域鏈?zhǔn)腔谡{(diào)用棧的,而不是代碼中的作用域嵌套。詞法作用域詞法作用域中,又可分為全局作用域,函數(shù)作用域和塊級作用域。 一篇鞏固基礎(chǔ)的文章,也可能是一系列的文章,梳理知識的遺漏點(diǎn),同時(shí)也探究很多理所當(dāng)然的事情背后的原理。 為什么探究基礎(chǔ)?因?yàn)槟悴蝗ッ嬖嚹憔筒恢阑A(chǔ)有多重要,或者是說當(dāng)你的工作經(jīng)歷沒有亮點(diǎn)的時(shí)候,基礎(chǔ)就是檢驗(yàn)?zāi)愫脡牡囊豁?xiàng)...
摘要:圖片中的作用域鏈,是全局執(zhí)行環(huán)境中的作用域鏈。然后此活動(dòng)對象被推入作用域鏈的最前端。在最后調(diào)用的時(shí)候,創(chuàng)建先構(gòu)建作用域鏈,再創(chuàng)建執(zhí)行環(huán)境,再創(chuàng)建執(zhí)行環(huán)境的時(shí)候發(fā)現(xiàn)了一個(gè)變量標(biāo)識符。 從圖書館翻過各種JS的書之后,對作用域/執(zhí)行環(huán)境/閉包這些概念有了一個(gè)比較清晰的認(rèn)識。 栗子說明一切 第一個(gè)栗子 來看一個(gè)來自ECMA-262的栗子: var x = 10; (function foo(...
摘要:在中的應(yīng)用采用詞法作用域,也就是靜態(tài)作用域。那什么又是詞法作用域或者靜態(tài)作用域呢請繼續(xù)往下看靜態(tài)作用域與動(dòng)態(tài)作用域因?yàn)椴捎玫氖窃~法作用域函數(shù)的作用域在函數(shù)定義的時(shí)候就決定了。 開篇 當(dāng)我們在開始學(xué)習(xí)任何一門語言的時(shí)候,都會(huì)接觸到變量的概念,變量的出現(xiàn)其實(shí)是為了解決一個(gè)問題,為的是存儲(chǔ)某些值,進(jìn)而,存儲(chǔ)某些值的目的是為了在之后對這個(gè)值進(jìn)行訪問或者修改,正是這種存儲(chǔ)和訪問變量的能力將狀態(tài)給...
摘要:作用域作用域是指程序源代碼中定義變量的區(qū)域。采用詞法作用域,也就是靜態(tài)作用域。而與詞法作用域相對的是動(dòng)態(tài)作用域,函數(shù)的作用域是在函數(shù)調(diào)用的時(shí)候才決定的。前面我們已經(jīng)說了,采用的是靜態(tài)作用域,所以這個(gè)例子的結(jié)果是。 JavaScript深入系列的第二篇,JavaScript采用詞法作用域,什么語言采用了動(dòng)態(tài)作用域?兩者的區(qū)別又是什么?還有一個(gè)略難的思考題,快來看看吧。 作用域 作用域是指...
閱讀 3999·2021-09-09 09:33
閱讀 1906·2021-09-06 15:14
閱讀 1986·2019-08-30 15:44
閱讀 3164·2019-08-29 18:36
閱讀 3829·2019-08-29 16:22
閱讀 2150·2019-08-29 16:21
閱讀 2623·2019-08-29 15:42
閱讀 1707·2019-08-29 11:00