js詞法作用域與this實例詳解
前言
靜態(tài)作用域又叫做詞法作用域,采用詞法作用域的變量叫詞法變量。詞法變量有一個在編譯時靜態(tài)確定的作用域。詞法變量的作用域可以是一個函數(shù)或一段代碼,該變量在這段代碼區(qū)域內(nèi)可見(visibility);在這段區(qū)域以外該變量不可見(或無法訪問)。詞法作用域里,取變量的值時,會檢查函數(shù)定義時的文本環(huán)境,捕捉函數(shù)定義時對該變量的綁定。大多數(shù)現(xiàn)在程序設(shè)計語言都是采用靜態(tài)作用域規(guī)則,如 C/C++ 、 C# 、 Python 、 Java 、 JavaScript …… 相反,采用動態(tài)作用域的變量叫做動態(tài)變量。只要程序正在執(zhí)行定義了動態(tài)變量的代碼段,那么在這段時間內(nèi),該變量一直存在;代碼段執(zhí)行結(jié)束,該變量便消失。這意味著如果有個函數(shù) f,里面調(diào)用了函數(shù) g,那么在執(zhí)行 g 的時候,f 里的所有局部變量都會被 g 訪問到。而在靜態(tài)作用域的情況下,g 不能訪問 f 的變量。動態(tài)作用域里,取變量的值時,會由內(nèi)向外逐層檢查函數(shù)的調(diào)用鏈,并打印第一次遇到的那個綁定的值。顯然,最外層的綁定即是全局狀態(tài)下的那個值。采用動態(tài)作用域的語言有 Pascal 、 Emacs Lisp 、 Common Lisp (兼有靜態(tài)作用域)、 Perl (兼有靜態(tài)作用域)。C/C++ 是靜態(tài)作用域語言,但在宏中用到的名字,也是動態(tài)作用域。
實踐
思考這么一段代碼:
function fun1() { var a = 2 console.log(a) } function fun2() { var a = 3 console.log(a) fun1() } fun2()
答案會是多少呢?
3
2
當(dāng)然這個很好理解,js
是函數(shù)作用域,fun1
內(nèi)有 a
,當(dāng)然會打印 fun1 內(nèi)的 a
的值,并沒有特殊之處,可是這個代碼呢:
var a = 2 function fun1() { console.log(a) } function fun2() { var a = 3 console.log(a) fun1() } fun2()
答案和之前依舊一樣,是不是這樣就有點反直覺了? fun1 內(nèi)沒有 a 的情況下不是應(yīng)該讀取 fan2 的 a 嗎?為什么會讀取 全局作用域的 a 呢?說好的作用域是一層一層向上的呢?
當(dāng)然,作用域確實是向上查找,可是 js 是靜態(tài)作用域(詞法作用域),并不是動態(tài)作用域,所以他不會看函數(shù)的調(diào)用位置,而是定義位置,并且沿著定義位置向上查找。詞法作用域和動態(tài)作用域的區(qū)別如下:
- 詞法作用域是在 代碼解析(定義) 的時候確定的,關(guān)注的是函數(shù)在 何處定義 ,并從定義處向上查找作用域。
- 動態(tài)作用域是在 代碼運行 的時候確定的,關(guān)注的是代碼在 何處調(diào)用 ,并從調(diào)用棧向上查找作用域。
所以現(xiàn)在很好理解,為什么 fun1 內(nèi)沒有 a 他會先去讀取全局的 a,而不是 fun2 的 a 了吧?不信可以看這個代碼:
function fun1() { console.log(a) // a is not defined } function fun2() { var a = 3 console.log(a) fun1() } fun2()
當(dāng)然,js 有個特殊之處,就是 this
,思考這段代碼:
this.a = 2 function fun1() { console.log(this.a) // 1 } function fun2() { this.a = 1 console.log(this.a) // 1 fun1() } fun2()
是不是疑惑了,說好的從定義的地方向上查找呢,為什么會打印出執(zhí)行的作用域的值?
這里可以先說答案:因為 this
:
this.a = 2 // this 指向 window function fun1() { // 這里 this 還是指向 window console.log(this) // window console.log(this.a) // 1 } function fun2() { // this 依舊指向 window,不信可以打印看看那 console.log(this.a) // 2 // 這里修改了外邊的 this.a this.a = 1 // 打印修改后的值 console.log(this.a) // 1 fun1() } fun2()
所以明白了吧? 作用域依舊是在定義的地方向上查找,只不過是兩個函數(shù)都指向了同一個 this
而已。
這里插一嘴,雖然我認為 js 的
this
是一個設(shè)計的非常糟糕的東西(他完全不符合正常人的思維邏輯),我也非常非常久都不再使用this
,但是我認為這個東西還是必須得理解的,不然早晚會搞出大麻煩,你可以不用,但是你必須要懂。
Ok, 接著上面所說,為什么兩個函數(shù)指向了同一個 this
(window)?這里就要深入的了解一下 this
的指向問題:this
究竟指向哪里,是都指向 window
么?顯然不是,看一下代碼:
this.n = 1 function fun2() { console.log(this.n) // 2 } var a = { n: 2, fun1() { console.log(this) // {n: 2, fun1: function} console.log(this.n) // 2 a.fun2() }, fun2 } a.fun1()
這里的 fun1 的 this
明顯指向了 a 本身,并不是 this
,同樣 fun2 雖然定義在外部,但是也依然指向了 a ,是不是和之前想的不太一樣?fun2 定義在外邊,那么他的 this
應(yīng)該是 window
才對,打印的應(yīng)該是 1 才對啊,可能這個時候你就在想了,是不是 this 就是動態(tài)作用域呢?并不! This 依舊是靜態(tài)作用域,參考這個代碼:
this.n = 1 function fun2() { console.log(this.n) // 1 } var a = { n: 2, fun1() { fun2() }, fun2 } a.fun1()
發(fā)現(xiàn)區(qū)別了嗎?this
依舊是指向 window
,這就說明 this
只是在定義的時候強行綁定了執(zhí)行他的環(huán)境,所以我們通過 a.fun2 調(diào)用,this 就指向 a,通過直接調(diào)用 fun2(實際等于 window.fun2),指向的則是 window
。
當(dāng)然也有例外,比如箭頭函數(shù):
this.n = 1 const fun2 = () => { console.log(this.n) } var a = { n: 2, fun1() { // console.log(this) // {n: 2, fun1: function} // console.log(this.n) // 2 a.fun2() // fun2() }, fun2 } a.fun1()
箭頭函數(shù)中,不管你是 a.fun2 還是直接 fun2,指向的都是window
,因為箭頭函數(shù)的 this
固定指向他的父作用域,而根據(jù)靜態(tài)作用域的原則,他父作用域是定義時的作用域,也就是 window
,所以不管怎么調(diào)用,他都是 window
。通過以下這個例子更能看出來這一點,箭頭函數(shù)的 this
固定指向他定義的作用域:
var n = 1 var a = () => { console.log(this.n) } var b={ n: 2, fun2: { n: 3, fun1:a, fun() { a() // 1 console.log(this) // {n: 3, fun1:function, fun: function} console.log(this.n) // 3 this.fun1() // 1 } } } b.fun2.fun()
通過這個你就能發(fā)現(xiàn),箭頭函數(shù)的 this
并不指向調(diào)用他的對象,也不是指向調(diào)用他的對象的父作用域,而是指向他定義的位置的父作用域,不管你在哪里調(diào)用,都是同一個指向。
總結(jié)
總結(jié)一下,對于this
,你只需要記住這幾點:
- 正常情況下 this 指向調(diào)用他的上下文
- 箭頭函數(shù)的
this
指向他的父作用域的this
(靜態(tài)作用域、靜態(tài)作用域、靜態(tài)作用域) - new 會創(chuàng)建一個新的對象,
this
指向這個對象,詳情可以自行了解new
call
、bind
、apply
會改變this
的指向,詳情自行了解
a.xx() xx 內(nèi)的 this 就是 a a.b.xx() xx 內(nèi)的 this 就是 b
誰 .
xxx,.
之前的上下文就是他的 this。 而在非嚴格模式的全局環(huán)境中(嚴格模式會報錯),實際我們定義的變量都是掛載在 window
下,所以 this
指向的是 window
。
到此這篇關(guān)于js詞法作用域與this的文章就介紹到這了,更多相關(guān)js詞法作用域與this內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript將雙字節(jié)字符轉(zhuǎn)換成單字節(jié)字符并計算長度
這篇文章主要介紹Javascript將雙字節(jié)字符轉(zhuǎn)換成單字節(jié)字符并計算長度的方法,簡單實用,需要的朋友可以參考下。2016-06-06基于JavaScript實現(xiàn)鼠標向下滑動加載div的代碼
這篇文章主要介紹了基于JavaScript實現(xiàn)鼠標向下滑動加載div的代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-08-08不錯的用resizeTo和moveTo兩個函數(shù)實現(xiàn)窗口的“打乒乓球”效果
不錯的用resizeTo和moveTo兩個函數(shù)實現(xiàn)窗口的“打乒乓球”效果...2007-08-08對layui數(shù)據(jù)表格動態(tài)cols(字段)動態(tài)變化詳解
今天小編就為大家分享一篇對layui數(shù)據(jù)表格動態(tài)cols(字段)動態(tài)變化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-10-10