咱們今天先從概念入手,而後在經過一道例題,完完整整的瞭解代碼執行的過程,在這個過程當中會用到做用域和做用域鏈查找機制;咱們在根據概念的帶入詳細梳理;javascript
在某一個上下文中建立函數,除了開闢內存和賦值以外,還會給當前函數設置一個做用域屬性
[[scope]]
:java
- 當前函數
[[scope]]
= 當前函數建立時候所在的上下文 ;
簡單來講:當前函數的做用域取決於當前函數建立時候的上下文,它在哪一個上下文建立的,那它的做用域就是誰函數
EC(G)
中的全局變量對象VO(G)
中,存儲的變量EC(XXX)
中的變量對象AO(XXX)
中,存儲的變量;都有哪些是私有變量呢:ui
- 當前函數執行造成的上下文中:聲明過的變量或者函數,都會存儲到
AO(XXX)
中,- 函數定義的形參變量,也會存儲到當前上下文的
AO(XXX)
中;
scopeChain
的概念做用域鏈scopeChain
裏面包含了:spa
scope
做用域;
scopeChain
:<當前EC,函數[[scope]]>
;3d
以此來創建鏈式關係,以後咱們在查找變量的時候,就按照這個鏈式關係找(先找本身上下文的,本身沒有,按照做用域鏈向上級做用域找)code
做用域鏈是在函數執行的時候造成的;執行函數的具體步驟爲:cdn
- 建立私有上下文EC(有存放私有變量的變量對象AO)
- 進棧執行(時會把全局上下文壓縮到底部)
- 初始化做用域鏈
scopeChain
:<當前EC,函數[[scope]]>- 初始化
THIS
指向- 形參賦值(包含初始化
ARGUMENTS
)- 變量提高
- ......這裏咱們省略了一些
- 代碼執行
- 執行完可能會出棧(也可能不出棧)
當函數執行的時候除了按照咱們上面的,還有不少不少操做;對象
首先看它是不是私有的blog
- 1.若是是私有的,接下來的全部操做,都是操做本身的,和別人沒有關係;
- 2.若是不是私有的,則按照
scopeChain
做用域鏈進行查找,在哪一個上下文中找到,當前變量就是誰的- ......一直找到全局上下文爲止
- 若是找到
EC(G)
都找不到:
- 是獲取變量值就會報錯,
- 是設置值,至關於給
GO
加屬性
var a = b = 12 ;
和 var a = 12, b = 12 ;
的區別在此以前咱們先了解一個零散的小知識點:
var a = b = 12 ;
和var a = 12, b = 12 ;
的區別
var a = b = 12;
//=> 至關於:var a = 12; b = 12; 只有第一個帶VAR,第二個不帶VAR
var a=12, b=12;
//=> 至關於var a = 12; var b = 12; 連續建立多個變量,可使用逗號分隔;
複製代碼
好了,接下來開始咱們的正式內容;
console.log(a, b);
var a = 12,
b = 12;
function fn() {
console.log(a, b);
var a = b = 13;
console.log(a, b);
}
fn();
console.log(a, b);
複製代碼
此題的操做過程:
var a
;var b
;function fn(){...}
; (這裏聲明+定義fn )
AAAFFF000
),把函數看成字符串存進堆中;scope
做用域屬性
fn[[scope]] = EC(G)
;AAAFFF000
,看成值與 fn
變量關聯在一塊兒;console.log(a, b);
=> undefined
/ undefined
;var a = 12 ;
12
;var a
操做已經在變量提高階段完成,因此直接跳過);a
與12
關聯;var b = 12 ;
=> 同上面var a = 12
同樣;function fn(){...};
=> (變量提高階段已經完成,直接跳過);fn() ;
=> 讓fn
函數執行;
EC(fn1)
);AO
(咱們把它命名爲AO(fn1)
);EC(G)
被壓縮到ECStack
底部,同時EC(fn1)
進棧執行 ;scopeChain
:<當前EC,函數[[scope]]>
這裏的fn[[scope]]
咱們在函數建立時,已經知道是EC(G)
,因此最終的做用域鏈爲=> scopeChain
:<EC(fn1),EC(G)>
;var a
;console.log(a, b);
=> 此時就按照做用域鏈查找,
a
已經存在,只不過沒有賦值,因此a
是本身的私有屬性,直接操做本身的便可;=> undefined
b
不是本身的私有屬性,經過做用域鏈查找,找到EC(G)
中的b
,此後操做的都是EC(G)
中的b
; => 12
var a = 13 ;
=> 建立值13
,var a
跳過,a
與13
關聯;b = 13 ;
=> 經過做用域鏈找到全局的b
;給b
從新賦值13
;console.log(a, b);
=> 13
/ 13
;console.log(a, b);
=> 此時打印全局下的a
,b
;
a
=> 12 ;b
=> 13 ;console.log(a, b, c);//=> undefined undefined undefined
var a = 12,
b = 13,
c = 14;
function fn(a) {
console.log(a, b, c);//=> 10 , 13 , 14
a = 100;
c = 200;
console.log(a, b, c);//=> 100 , 13 ,200
}
b = fn(10);
console.log(a, b, c);//=> 100 undefined 200
複製代碼
var ary = [12, 23];
function fn(ary) {
console.log(ary);//=> [12,23]
ary[0] = 100;
ary = [100];
ary[0] = 0;
console.log(ary);//=> [0]
}
fn(ary);
console.log(ary);//=> [100,23]
複製代碼
var a=1;
var obj ={
"name":"tom"
}
function fn(){
var a2 = a;
obj2 = obj;
a2 =a;
obj2.name =」jack」;
}
fn();
console.log(a);//=> 1
console.log(obj);//=> {"name":"jack"}
複製代碼
var i = 20;
function fn() {
i -= 2;
var i = 10;
return function (n) {
console.log((++i) - n);
}
}
var f = fn();
f(1);//=>10
f(2);//=>10
fn()(3);//=>8
fn()(4);//=>7
f(5);//=>8
console.log(i);//=>20
複製代碼
//========有形參=======
let x = 5;
function fn(x) {
return function(y) {
console.log(y + (++x));
}
}
let f = fn(6);
f(7);//=>14
fn(8)(9);//=>18
f(10);//=>18
console.log(x);
//========無形參==========
let x = 5;
function fn() {
return function(y) {
console.log(y + (++x));
}
}
let f = fn(6);
f(7);//=>13
fn(8)(9);//=>16
f(10);//=>18
console.log(x);
複製代碼
x
的狀況