作用域鏈:
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
逆皮,此時a
與n
都是存在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)
相當于把 `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)載