【12月5日】關于JS中的作用域和作用域鏈

作用域

定義:作用域是指程序源代碼中定義變量的區(qū)域。
作用:作用域規(guī)定了如何查找變量,也就是確定當前執(zhí)行代碼對變量的訪問權(quán)限。
在javaScript中的應用 :JavaScript采用詞法作用域(lexical scoping),也就是靜態(tài)作用域。
那什么又是 詞法作用域或者靜態(tài)作用域呢卦尊?

請繼續(xù)往下看

靜態(tài)作用域與動態(tài)作用域

因為javaScript采用的是詞法作用域(也就是靜態(tài)作用域),函數(shù)的作用域在函數(shù)定義的時候就決定了。

而詞法作用域相對的是動態(tài)作用域,函數(shù)的作用域是在函數(shù)調(diào)用的時候才決定的舌厨。
讓我們看一個例子來理解詞法作用域和動態(tài)作用域之間的區(qū)別:

var value = 1;

function foo() {
  console.log(value);
}

function bar() {
  var value = 2;
  foo();
}

bar();

// 結(jié)果是 ???
上面的代碼中:

1.我們首先定義了一個value岂却,并賦值為1;
2.聲明一個函數(shù)foo裙椭,函數(shù)的功能是打印 value 這個變量的值;
3.聲明一個函數(shù)bar躏哩,函數(shù)內(nèi)部重新創(chuàng)建了一個變量 value 這個變量賦值為2;
在函數(shù)內(nèi)部執(zhí)行了 foo() 這個函數(shù)揉燃;
4.執(zhí)行 bar() 這個函數(shù)
假設javaScript采用靜態(tài)作用域震庭,讓我們分析下執(zhí)行過程:

執(zhí)行foo函數(shù),首先從 foo 函數(shù)內(nèi)部查找是否有變量 value ,如果沒有
就根據(jù)書寫的位置,查找上面一層的代碼你雌,我們發(fā)現(xiàn)value等于1器联,所以結(jié)果會打印 1二汛。

假設javaScript采用動態(tài)作用域,讓我們分析下執(zhí)行過程:

執(zhí)行foo函數(shù)拨拓,依然是從 foo 函數(shù)內(nèi)部查找是否有局部變量 value肴颊。如果沒有,
就從調(diào)用函數(shù)的作用域渣磷,也就是 bar 函數(shù)內(nèi)部查找 value 變量婿着,所以結(jié)果會打印 2。

上面在區(qū)分靜態(tài)作用于和動態(tài)作用域的時候,我們已經(jīng)說了如果是靜態(tài)作用域醋界,那么函數(shù)在書寫定義的時候已經(jīng)確定了竟宋,而動態(tài)作用域是函數(shù)執(zhí)行過程中才確定的。

JavaScript采用的是靜態(tài)作用域形纺,所以這個例子的結(jié)果是 1丘侠。
我們在控制臺中輸入執(zhí)行上面的函數(shù),檢驗一下執(zhí)行結(jié)果果然是 1逐样。

動態(tài)作用域

那什么語言是采用的動態(tài)的作用域呢? 其實bash 就是動態(tài)作用域蜗字,
我們可以新建一個 scope.bash 文件將下列代碼放進去,執(zhí)行一下這個腳本文件:

#!/bin/bash

value=1
function foo () {
    echo $value;
}
function bar () {
  local value=2;
  foo;
}
bar

上面代碼運行的結(jié)果輸出2很好解釋脂新,雖然在代碼最上層定義了 value并賦值為1挪捕,但是在調(diào)用foo函數(shù)的時候,在查找 foo 內(nèi)部沒有 value 變量后,會在foo 函數(shù)執(zhí)行的環(huán)境中繼續(xù)查找争便,也就是在bar 函數(shù)中查找级零,很幸運我們找到了。~

看一道題

var scope = "global scope";
 function checkscope(){ 
    var scope = "local scope"; 
    return scope;
 } 
console.log(checkscope());//local scope

原因是:函數(shù)內(nèi)的變量優(yōu)先級高于全局變量滞乙,如果在函數(shù)內(nèi)聲明的局部變量和全局變量重名奏纪,那么在函數(shù)局部范圍內(nèi),局部變量的賦值和計算將頂替全局變量原有的值酷宵,但是在函數(shù)之外繼續(xù)引用全局變量亥贸,全局變量原來的值不變躬窜。
很容易理解
再直接打印一下scope,可以看到是沒有改變的

var scope = "global scope";
 function checkscope(){ 
    var scope = "local scope"; 
    return scope;
 } 
console.log(checkscope());
console.log(scope);//global scope

這個時候把var去掉,全局scope被覆蓋了

var scope = "global scope";
 function checkscope(){ 
    scope = "local scope"; //去掉了var聲明
    return scope;
 } 
console.log(checkscope());
console.log(scope);//從global scope變成了local scope

再來看看當函數(shù)內(nèi)嵌套多層的情況:

var scope = ' global scopr'; // 全局變量 現(xiàn)在值時global scope
function checkscope(){ // 第一層函數(shù)
     var scope = 'local scope'; //創(chuàng)建了一個函數(shù)內(nèi)的全局變量
     function nested(){
         var scope = 'nested scope'; //創(chuàng)建了一個函數(shù)內(nèi)的全局變量
         return scope; //這里放回 socope 的結(jié)果浇垦,是netsed scope 
     }
     return nested(); //返回netstd的值 也是 netsed scope

}
console.log(checkscope())// 所以這里的值是 nested scope 
var scope = "global scope";
function checkscope(){
  var scope = "local scope";
  function f(){
    return scope;
  }
  return f();
}
checkscope();

在checkscope 函數(shù)中 又定義一個函數(shù) f ,這個函數(shù) 只做了一件事:返回scope 這個變量;
最后返回并執(zhí)行 f 這個函數(shù)荣挨;
調(diào)用checkscope
按照javaScript中靜態(tài)作用域理解男韧,在執(zhí)行 checkscope 這個函數(shù)的時候在函數(shù)內(nèi)部執(zhí)行的是f 這個函數(shù),首先在 f 這個函數(shù)內(nèi)部查找 scope 這個變量發(fā)現(xiàn)沒有默垄,繼續(xù)在定義函數(shù)f的上面一層查找此虑,發(fā)現(xiàn)在checkscope 這個函數(shù)作用域內(nèi) 找到了scope的值 直接返回,至于 checkscope外面定義的scope沒有理睬口锭。

再舉一個栗子

// 例2:
var scope = "global scope";
function checkscope(){
  var scope = "local scope";
  function f(){
    return scope;
  }
  return f;
}
checkscope()();

單純的返回 f 這個函數(shù)朦前;
調(diào)用checkscope
按照我們上面解釋的javaScript中靜態(tài)作用域理解介杆,在執(zhí)行 checkscope 這個函數(shù)的時候在函數(shù)內(nèi)返回了函數(shù)f實際是在最外面調(diào)用的f但是由于javaScript是采用的詞法作用域,因此函數(shù)的作用域基于函數(shù)創(chuàng)建的位置韭寸。

而引用《JavaScript權(quán)威指南》的回答就是:

JavaScript 函數(shù)的執(zhí)行用到了作用域鏈春哨,這個作用域鏈是在函數(shù)定義的時候創(chuàng)建的。嵌套的函數(shù) f() 定義在這個作用域鏈里恩伺,其中的變量 scope 一定是局部變量赴背,不管何時何地執(zhí)行函數(shù) f(),這種綁定在執(zhí)行 f() 時依然有效晶渠。

但是在這里真正想讓大家思考的是:

雖然兩段代碼執(zhí)行的結(jié)果一樣凰荚,但是兩段代碼究竟有哪些不同呢?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褒脯,一起剝皮案震驚了整個濱河市便瑟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憨颠,老刑警劉巖胳徽,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異爽彤,居然都是意外死亡养盗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門适篙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來往核,“玉大人,你說我怎么就攤上這事嚷节∧羧澹” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵硫痰,是天一觀的道長衩婚。 經(jīng)常有香客問我,道長效斑,這世上最難降的妖魔是什么非春? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮缓屠,結(jié)果婚禮上奇昙,老公的妹妹穿的比我還像新娘。我一直安慰自己敌完,他們只是感情好储耐,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滨溉,像睡著了一般什湘。 火紅的嫁衣襯著肌膚如雪长赞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天闽撤,我揣著相機與錄音涧卵,去河邊找鬼。 笑死腹尖,一個胖子當著我的面吹牛柳恐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播热幔,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼乐设,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了绎巨?” 一聲冷哼從身側(cè)響起近尚,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎场勤,沒想到半個月后戈锻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡和媳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年格遭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片留瞳。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡拒迅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出她倘,到底是詐尸還是另有隱情璧微,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布硬梁,位于F島的核電站前硫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荧止。R本人自食惡果不足惜屹电,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罩息。 院中可真熱鬧嗤详,春花似錦个扰、人聲如沸瓷炮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娘香。三九已至苍狰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烘绽,已是汗流浹背淋昭。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留安接,地道東北人翔忽。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像盏檐,于是被迫代替她去往敵國和親歇式。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容