淺談JS作用域鏈(scope chain)

作用域鏈:

JS權(quán)威指南指出”JavaScript中的函數(shù)運行在它們被定義的作用域里,而不是它們被執(zhí)行的作用域里.” 
ECMA262中所述 任何執(zhí)行上下文時刻的作用域, 都是由作用域鏈(scope chain)來實現(xiàn).在一個函數(shù)被定義的時候, 會將它定義時刻的scope chain鏈接到這個函數(shù)對象的[[scope]]屬性.
在一個函數(shù)對象被調(diào)用的時候,會創(chuàng)建一個活動對象(也就是一個對象), 然后對于每一個函數(shù)的形參,都命名為該活動對象的命名屬性, 然后將這個活動對象做為此時的作用域鏈(scope chain)最前端, 并將這個函數(shù)對象的[[scope]]加入到scope chain中.

不必刻意去記概念。當然上面的話,我是記不住偶妖,看著也有點繞,下面是我自己這幾天學(xué)習(xí)的體會:


每當執(zhí)行一個函數(shù)就進入一個新的的作用域,使用一個變量或是賦

值际长。首先從自己的當前作用域內(nèi)部找變量,找到就輸出兴泥,找不到就是

往當前函數(shù)所在上層的作用域找工育,(上層的作用域就是當前函數(shù)聲明的作用域)

基礎(chǔ)知識:

console.log(a) //undefined (變量提升,在函數(shù)內(nèi)部同樣適用)
var a = 1

fn() // "2"      //函數(shù)聲明前置
function fn(){
    console.log('2')
}     

相關(guān)JavaScript知識點請參考阮一峰JavaScript教程

實例剖析:


??①
var x = 10
bar() 
function foo() {
  console.log(x)
}
function bar(){
  var x = 30
  foo()
}
/*
var x

x = 10

function foo() {
  console.log(x)
}

function bar() {
  var x = 30
  foo()
}
*/

10

分析

變量 x 提升到頭部,bar()函數(shù)聲明前置搓彻,函數(shù)foo()的變量x的作用域不是在bar()內(nèi)部如绸,從函數(shù)foo()本身也沒有找到,往其上層作用域找到為var x = 10全局變量找到,bar()輸出的結(jié)果是10

??②
var a = 1
function fn1(){
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //輸出多少

/*
function fn1(){
  var a
 // 函數(shù)fn1()內(nèi)部的變量a, 提升到函數(shù)頂部
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  a = 2
  return fn3
}
*/

2

結(jié)果分析:

可簡單的看出輸出的結(jié)果是fn2()內(nèi)的cobsole(2)的打印值,fn2()的變量對應(yīng)的作用域旭贬,首先從自身既函數(shù)fn2()內(nèi)部作用域找變量怔接。
內(nèi)部無法找到就往上層找,上層所對應(yīng)的作用域即是fn1()內(nèi)部,內(nèi)部變量 var a = 2**『函數(shù)內(nèi)部變量提升』稀轨,所以fn(2)的變量對應(yīng)的作用域在函數(shù)fn1()內(nèi)部扼脐,console.log(a)對應(yīng)的a的值是2
fn = fn2() = fn(3)
所以輸出結(jié)果既是函數(shù)fn2()的值2

??③
var a = 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
function fn2(){
  console.log(a)
}
var fn = fn1()
fn() //輸出多少

1

結(jié)果分析:

還是上例同樣的套路,fn2()中a對應(yīng)的作用域奋刽,從自己本身找不到瓦侮,所以往上層找,就是全局變量 a (a = 1),

function fn2(){
  console.log(a)
}

a的值為1
fn2()的結(jié)果為1
fn = fn1() = f3(), 函數(shù)fn1()佣谐,fn3的作用域都不是fn2()的變量a所對應(yīng)的作用域肚吏。

??④
var a = 1
function fn1(){
  function fn3(){
    function fn2(){
      console.log(a)
    }
    fn2()
    var a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //輸出多少

undefined

結(jié)果分析:

return fn3 , fn1() = fn3() = fn2();

fn2()對應(yīng)的作用域為它的上級作用域fn3()

 function fn3(){
    function fn2(){
      console.log(a)
    }
    fn2()
    var a = 4
  }

fn2()的的輸出結(jié)果是undefined,這是由于在

 function fn3(){
       // ...
      // ...
   var a = 4
 }

fn3()中變量a 提升到函數(shù)內(nèi)首部,但是函數(shù)賦值是在調(diào)用fn2()之后,
所以console.log(a)中的a,并沒有被賦值,既輸出結(jié)果是undefined狭魂。
如果把 (var a = 4)放在調(diào)用fn2()之前則輸出的結(jié)果是4

??⑤
var a = 1
var c = { name: 'curry', age: 2 }
function f1(n) {
  ++n
}
function f2(obj) {
  ++obj.age
}
f1(a) 
f2(c) 
f1(c.age) 
console.log(a) 
console.log(c)  

// 1
// { name: 'curry', age:3 }

結(jié)果分析:
var a = 1
function f1(n){
  ++n
}
f1(a)
console.log(a) 

等同

var a = 1
function f1(){
  var n = arguments[0]
 // 假設(shè) n 為 函數(shù) f1 的第一個參數(shù)
  ++n
}
f1(a)
console.log(a) 

arguments[0]傳遞進來罚攀,arguments[0] = 1党觅,++n,大括號內(nèi)的變量增加1斋泄,但是沒有返回值杯瞻,所以和目前的a并沒什么關(guān)系(并不能改變a的值),
函數(shù)f1(a),對應(yīng)的結(jié)果是++a是己,但是它并沒有輸出又兵,所以console.log(a)
中的a還是 a = 1(全局變量),既輸出結(jié)果1
即使 上面的f1(n)返回了值,console.log(a)的值依然是1

var a = 1
function f1(n){
  return ++n
}
f1(a)
console.log(a)
// 2
// 打印的結(jié)果是 1

解析:

function f1(n){
  return ++n
}
// 等價于
function f1() {
  var n = arguments[0]
  return ++n
}

上面的??中 函數(shù)f1(n)中的返回值卒废,可以從根據(jù)數(shù)據(jù)在內(nèi)存中儲存的方式來探究沛厨。首先全局變量a是一個簡單類型,var n = arguments[0]其中arguments[0]就是變量a摔认,把 變量 a的值賦值給n逆皮,此時an都是存在stack里面,且相互獨立参袱;當return ++n的時候电谣,就是給n的值加1,而此時a的值并不會跟著變化抹蚀,還是原來的值剿牺。
如果是下面的這種情況打印出來的值也是2

var a = 1
function f1() {
  return ++a
 // 如果是上面的 ++a 沒有return,后面的console.log(a)打印的值也是 2 
 // ++a
}
f1()
console.log(a) // 打印出來的a的值是2

var c = { name: 'curry', age: 2 }
function f2(obj){
   // var obj = c 
  // 把c賦值給obj环壤,把c的地址和指針賦值給obj了

  // c = 0x0001
  c與obj其實就是兩個名字不同但是***值與地址***相同的值

  ++obj.age
}
f2(c) 
f1(c.age) 
console.log(c)     
image.png
相當于把 `obj = c`晒来,
把c賦值給obj,把c的地址和指針賦值給obj了郑现,
c與obj其實就是兩個名字不同但是***值與地址***相同的值

f2(obj)雖然也不會輸出結(jié)果湃崩,但是但是因為obj的地址改變了,則c的地址也改變了
所以console.log(c) 的結(jié)果是++obj.age { name: 'curry', age:3 }


總結(jié):

  • 1.函數(shù)在執(zhí)行的過程中接箫,先從自己內(nèi)部找變量
  • 2.如果找不到攒读,再從創(chuàng)建當前函數(shù)所在的作用域去找, 以此往上
  • 3.注意找的是變量的當前的狀態(tài)

版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主許可不得轉(zhuǎn)載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辛友,一起剝皮案震驚了整個濱河市薄扁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌废累,老刑警劉巖邓梅,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異九默,居然都是意外死亡震放,警方通過查閱死者的電腦和手機宾毒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門驼修,熙熙樓的掌柜王于貴愁眉苦臉地迎上來殿遂,“玉大人,你說我怎么就攤上這事乙各∧福” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵耳峦,是天一觀的道長恩静。 經(jīng)常有香客問我,道長蹲坷,這世上最難降的妖魔是什么驶乾? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮循签,結(jié)果婚禮上级乐,老公的妹妹穿的比我還像新娘。我一直安慰自己县匠,他們只是感情好风科,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乞旦,像睡著了一般贼穆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兰粉,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天故痊,我揣著相機與錄音,去河邊找鬼亲桦。 笑死崖蜜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的客峭。 我是一名探鬼主播豫领,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舔琅!你這毒婦竟也來了等恐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤备蚓,失蹤者是張志新(化名)和其女友劉穎课蔬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體郊尝,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡二跋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了流昏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扎即。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡吞获,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谚鄙,到底是詐尸還是另有隱情各拷,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布闷营,位于F島的核電站烤黍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏傻盟。R本人自食惡果不足惜速蕊,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娘赴。 院中可真熱鬧互例,春花似錦、人聲如沸筝闹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽关顷。三九已至糊秆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間议双,已是汗流浹背痘番。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留平痰,地道東北人汞舱。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像宗雇,于是被迫代替她去往敵國和親昂芜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

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