DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> JavaScript基礎知識 >> 結合代碼圖文講解JavaScript中的作用域與作用域鏈
結合代碼圖文講解JavaScript中的作用域與作用域鏈
編輯:JavaScript基礎知識     

先上三段說明作用域的代碼

//==========例1==========
 
var scope='global';
function fn(){
  alert(scope);
  var scope='local';
  alert(scope);
}
fn();    //輸出結果?
alert(scope);//輸出結果?
 
//===========例2==========
 
var scope='global';
function fn(){
  alert(scope);
  scope='local';
  alert(scope);
}
fn();    //輸出結果?
alert(scope);//輸出結果?
 
//===========例3=========
 
var scope='global';
function fn(scope){
  alert(scope);
  scope='local';
  alert(scope);
}
fn();    //輸出結果?
alert(scope);//輸出結果?

這三段代碼只有小許差異,但結果缺截然不同,例1分別輸出[undefined , local , global],例2分別輸出[global , local , local],例3結果輸出[undefined , local , global],如果不能答對說明你對javascript的作用域特性還未理解透徹。

什麼是作用域?

也許有人會問:變量a的作用域是什麼?一會兒又問:函數a的作用域是什麼?變量和函數的作用域分別是啥玩意?

我們先來看看“作用域”是什麼意思,“作用域”拆開來就是“作用”和“域”

  • 作用:訪問、操作、調用……
  • 域:區域、范圍、空間……

作用域就是變量和函數的可訪問范圍,或者說變量或函數起作用的區域。

1.javascript函數的作用域:

函數內的區域,就是這個函數的作用域,變量和函數在這個區域都可以訪問操作。最外層函數外的區域叫全局作用域,函數內的區域叫局部作用域。

2.javascript變量的作用域:

在源代碼中變量所在的區域,就是這個變量的作用域,變量在這個區域內可以被訪問操作。在全局作用域上定義的變量叫全局變量,在函數內定義的變量叫局部變量。

簡單地理解,JS源代碼被函數{ }劃分成一塊一塊的區域,這些區域換個身份就是某函數或某變量的作用域,變量的作用域和函數的作用域在源代碼中有可能指的是同一塊區域。

作用域鏈
作用域鏈(Scope Chain)是javascript內部中一種變量、函數查找機制,它決定了變量和函數的作用范圍,即作用域,理解作用域鏈的作用原理,上一篇文章的三個例子也就能理解了,從而知其然也知其所以然。

作用域鏈是ECMAScript-262說明文檔中的概念,javascript引擎是按ECMAScript-262說明文檔去實現的,了解javascript引擎的工作原理有利於我們理解javascript的特性,但絕大多數js程序員不會去了解非常底層的技術,所以閱讀ECMAScript-262說明文檔,我們可以有一個直觀的方式去模擬javascript引擎的工作原理。

本文將通過1999年的ECMAScript-262-3th第三版來說明作用域鏈的形成原理,將會介紹執行環境,變量對象和活動對象,arguments對象,作用域鏈等幾個概念。2009年發布了ECMAScript-262-5th第五版,不同的是取消了變量對象和活動對象等概念,引入了詞法環境(Lexical Environments)、環境記錄(EnviromentRecord)等新的概念,所以兩個版本的概念不要混淆了。

1.執行環境(Execution Contexts)

執行環境(Execution Contexts)也被翻譯為執行上下文,當解析器進入ECMAScript的可執行代碼,解析器就進入一個執行環境,活動的執行環境組成一個邏輯上的棧,在這個邏輯棧頂部的執行環境是當前運行的執行環境。

注:ECMAScript中有三種可執行代碼,Global、Function和Eval,全局環境即是Global可執行代碼,函數即是Function可執行代碼。邏輯棧是一種特殊的數據存儲格式,特點是‘先進後出,後進先出',添加數據會先壓入邏輯棧頂部,刪除數據必須先從頂部開始刪除。

201675145052363.jpg (398×180)

變量對象(Variable Object)、活動對象(Activation Object)和Arguments對象(Arguments Object)

每個執行環境都有一個與之關聯的變量對象,當解析器進入執行環境時,就會創建一個變量對象,變量對象保存著在當前執行環境中聲明的變量和函數的引用。

變量對象是一個抽象的概念,在不同的執行環境中,變量對象有不同的身份,在解析器進入任何執行環境之前,就已經創建了一個Global對象,當解析器進入全局執行環境時,Global對象就充當變量對象,當解析器進入一個函數時,就會創建一個活動對象充當變量對象。

2.解析器處理代碼時的兩個階段

我們都知道javascript解析器是一段一段解析處理代碼的,為毛?這就要涉及解析器處理代碼時的兩個階段,解析代碼和執行代碼。

當解析器進入執行環境時,變量對象就會添加執行環境中聲明的變量和函數作為它的屬性,這就意味著變量和函數在聲明之前已經可用,變量值為undefined,這就是變量和函數聲明提升(Hoisting)的原因,與此同時作用域鏈和this確定,此過程為解析階段,俗稱預解析。接著解析器開始執行代碼,為變量添加相應值的引用,得到執行結果,此過程為執行階段。

舉兩個好吃的栗子:

var a=123;
var b="abc";
function c(){
  alert('11');
}

上述全局環境中的代碼解析執行後,會將Global對象作為變量對象,保存以下數據。

201675145144397.jpg (476×190)

function testFn(a){
  var b="123";
  function c(){
    alert("abc");
  }
}
 
testFn(10);

當解析器進入函數執行環境時,則會創建一個活動對象作為變量對象,活動對象還會創建一個Arguments對象,arguments對象是一個參數集合,用來保存參數,這就是我們寫函數時可以使用arguments[0]等來使用參數的原因。

201675145319913.jpg (476×204)

3.作用域鏈(Scope Chain)

每個執行環境都有一個與之關聯的作用域鏈,當解析器進入執行環境時被定義,作用域鏈是一個對象列表,用來檢索各個變量對象中的變量和函數,這樣可以保證執行環境有權訪問哪些變量和函數,舉個栗子。

var a='123';
function testFn(b){
  var c='abc';
 
  function testFn2(){
    var d='efg';
    alert(a);
  }
 
  testFn2();
}
 
testFn(10);

testFn2內未聲明變量a,為什麼testFn2能調用全局變量a?整個過程是怎麼發生的呢?請看下圖。

201675145344904.jpg (608×272)

當解析器進入全局執行環境時,調用變量和函數時只在Global對象中查找。

當解析器進入testFn函數執行環境時,函數內部屬性[[scope]]中首先填入Global對象,然後將testFn活動對象添加到Global對象之前,形成一個作用域鏈。

201675145416219.jpg (631×370)

當解析器進入testFn2函數執行環境時,函數內部屬性[[scope]]首先填入父級的作用域鏈,然後再將當前的testFn2活動對象添加到作用域鏈的前端,形成一個新的作用域鏈。

testFn2調用變量a時,首先在當前的testFn2活動對象中查找,如果沒有找到就順著作用域鏈向上,在testFn活動對象中查找變量a,如果沒有找到再順著作用域鏈向上查找,直到在最後Global對象中找到為止,否則報錯。所以函數內部可以調用外部環境的變量,外部環境不能調用函數內部的變量,這就是作用域特性的原理。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved