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