做用域和做用域鏈查找機制|內附思惟導圖

咱們今天先從概念入手,而後在經過一道例題,完完整整的瞭解代碼執行的過程,在這個過程當中會用到做用域和做用域鏈查找機制;咱們在根據概念的帶入詳細梳理;javascript

思惟導圖

1、做用域的概念

在某一個上下文中建立函數,除了開闢內存和賦值以外,還會給當前函數設置一個做用域屬性[[scope]]java

  • 當前函數[[scope]] = 當前函數建立時候所在的上下文 ;

簡單來講:當前函數的做用域取決於當前函數建立時候的上下文,它在哪一個上下文建立的,那它的做用域就是誰函數

2、全局變量 VS 私有變量

  • 全局變量:在全局上下文EC(G)中的全局變量對象VO(G)中,存儲的變量
  • 私有變量:在函數執行造成的私有上下文EC(XXX)中的變量對象AO(XXX)中,存儲的變量;

都有哪些是私有變量呢:ui

  • 當前函數執行造成的上下文中:聲明過的變量或者函數,都會存儲到AO(XXX)中,
  • 函數定義的形參變量,也會存儲到當前上下文的AO(XXX)中;

3、做用域鏈查找機制

一、做用域鏈scopeChain的概念

做用域鏈scopeChain裏面包含了:spa

  • 當前造成的上下文,
  • 以及當前函數所對應的scope做用域;

scopeChain:<當前EC,函數[[scope]]>;3d

以此來創建鏈式關係,以後咱們在查找變量的時候,就按照這個鏈式關係找(先找本身上下文的,本身沒有,按照做用域鏈向上級做用域找)code

二、做用域鏈的造成

做用域鏈是在函數執行的時候造成的;執行函數的具體步驟爲:cdn

  • 建立私有上下文EC(有存放私有變量的變量對象AO)
  • 進棧執行(時會把全局上下文壓縮到底部)
  • 初始化做用域鏈 scopeChain:<當前EC,函數[[scope]]>
  • 初始化THIS指向
  • 形參賦值(包含初始化ARGUMENTS
  • 變量提高
  • ......這裏咱們省略了一些
  • 代碼執行
  • 執行完可能會出棧(也可能不出棧)

當函數執行的時候除了按照咱們上面的,還有不少不少操做;對象

三、做用域鏈的查找機制

  • 在當前上下文中,代碼執行的過程當中遇到一個變量時:

首先看它是不是私有的blog

  • 1.若是是私有的,接下來的全部操做,都是操做本身的,和別人沒有關係;
  • 2.若是不是私有的,則按照scopeChain做用域鏈進行查找,在哪一個上下文中找到,當前變量就是誰的
  • ......一直找到全局上下文爲止
  • 若是找到EC(G)都找不到:
    • 是獲取變量值就會報錯,
    • 是設置值,至關於給GO加屬性

4、以題爲例:詳細解析

一、一個小知識點: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);
複製代碼

此題的操做過程:

  • 第一步:造成執行環境棧ECStack
  • 第二步:造成一個全局上下文EC(G)
  • 第三步:造成全局變量對象VO(G)
  • 第四步:進棧執行
  • ......:這裏咱們省略一些咱們暫時用不到的內容
  • 第五步:變量提高;
    • var a ;
    • var b ;
    • function fn(){...} ; (這裏聲明+定義fn )
      • 開闢堆內存,生成一個空間地址(這裏咱們假設堆的地址爲AAAFFF000),把函數看成字符串存進堆中;
      • 與此同時,在建立函數的時候還會給函數加一個scope做用域屬性
        • 做用域屬性是指:當前函數是在哪一個上下文中建立的,那它的做用域就是哪一個上下文
        • 因此本題的fn[[scope]] = EC(G) ;
      • 把堆的地址AAAFFF000,看成值與 fn 變量關聯在一塊兒;
  • 第六步:代碼執行;
    • 1.console.log(a, b); => undefined / undefined;
    • 2.var a = 12 ;
      • 首先建立值12;
      • (此時var a 操做已經在變量提高階段完成,因此直接跳過);
      • 把變量a12關聯;
    • 3.var b = 12 ; => 同上面var a = 12同樣;
    • 4.function fn(){...}; => (變量提高階段已經完成,直接跳過);
    • 5.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 ; => 建立值13var a跳過,a13關聯;
        • b = 13 ; => 經過做用域鏈找到全局的b;給b從新賦值13;
        • console.log(a, b); => 13 / 13;
        • 此時函數體中代碼以所有執行完。
      • 第四步:出棧銷燬,同時全局上下文恢復到原來位置,繼續執行。
    • 6.console.log(a, b); => 此時打印全局下的a,b;
      • a => 12 ;
      • b => 13 ;
  • 執行完成

三、例題圖解析

5、補充幾道練習題

1.寫出下面代碼輸出的結果(本題咱們畫圖解析)

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
複製代碼

2.寫出下面代碼輸出的結果(本題咱們畫圖解析)

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]
複製代碼

3.寫出下面代碼輸出的結果(本題咱們畫圖解析)

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"}
複製代碼

4.寫出下面代碼輸出的結果(本題咱們畫圖解析)

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
複製代碼

5.寫出下面代碼輸出的結果(本題咱們畫圖解析)

//========有形參=======
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的狀況
  • 無形參的狀況

相關文章
相關標籤/搜索