JS核心之做用域

什麼是做用域

做用域是運行代碼中某些特定部分的變量,函數和對象的可訪問性及生命週期javascript

javascript中的做用域

在javascript中有兩種類型的做用域
1.全局做用域
2.局部做用域
在函數內部定義的在局部做用域內,而在函數外定義的變量在全局做用域內,調用時的每一個函數建立一個新的做用域java

全局做用域

當你開始在文檔中編寫代碼時,你已經在全局做用域內,整個js環境中只有一個全局做用域,若是在函數外部定義了一個變量,他就在全局做用域內。函數

// 全局做用域
var name = "哈士奇"

全局做用域的變量能夠在任何其餘做用域內訪問或更改this

// 全局做用域
var name = "哈士奇";

console.log(name); // 哈士奇

//局部做用域
function logName(){
    name = "泰迪";
    console.log(name);
}

logName();

局部做用域

在函數內定義的變量在局部做用域內,對於每一個函數的調用,它們都有不一樣的做用域,這意味着具備相同名稱變量能夠用於不一樣的函數,只是由於這些變量被綁定到它們各自的函數。每一個函數具備不一樣的做用域,而且在其餘做用域中是不可訪問的。spa

function foo(){
    var name = "哈士奇";
    console.log(name);
}

function bar(fn){
    var name = "泰迪";
    fn();
}

bar(foo);    // 哈士奇

不管函數在哪裏被調用,也不管他如何被調用,他的做用域都只由函數聲明時的位置決定的線程

查找

做用域查找會在找到第一個匹配的標識符時中止。在多層的嵌套做用域中能夠能夠定義同名的變量,這叫作遮蔽效應(內部的變量遮蔽了外部的變量)code

function foo(){
    var name = "哈士奇"
    function bar(){
        var name = "泰迪"
        console.log(name)
    }
    bar(); //泰迪
    console.log(name); // 哈士奇
}

foo();
欺騙詞法

javascript中的eval函數能夠接受一個字符串爲參數,並將其中的內容好像在書寫時就存在於程序中這個位置的代碼。對象

function foo(str, a){
    eval(str);
    console.log(a, b); //欺騙
}

var b = 2;

foo("var b = 3;", 1); //1, 3

在執行eval()以後的代碼時,引擎並不知道或在乎前面的代碼是以動態形式插入進來,並對做用域的環境進行修改的,引擎只會如往常對詞法做用域進行查找。生命週期

塊做用域

像塊語句ifswitch條件或whilefor循環不建立一個新的做用域,在塊語句內定義的變量將保留在它們已經存在的做用域內事件

if(true){
    var name = "哈士奇"
}

console.log(name);    // 哈士奇

在ES6中新增的constlet關鍵字能夠在塊語句內聲明局部做用域。

if(true){
    let name = "哈士奇";
}

console.log(name); // not defined

執行上下文堆棧

由於javascript是單線程語言,一次就只能執行一個函數,其餘的函數會在執行上下文中排隊,
即會造成執行上下文堆棧。

ec-stack.png

在javascript引擎開始執行代碼時,它進入全局執行上下文,它是堆棧的底部和第一個元素。而後,全局代碼提供一些初始化,建立所需的對象和函數。在執行全局上下文期間,其代碼可能激活一些其餘(已建立的)函數,這些函數將進入其執行上下文,將新函數推送到堆棧上,依此類推。初始化完成後,運行時系統正在等待一些事件(例如用戶的鼠標點擊),這將激活一些功能,並將進入新的執行上下文。

ec-stack-changes.png

執行上下文

每一個函數都會建立本身的執行上下文,但只能有一個全局上下文。執行上下文能夠被簡單的表示一個對象,它擁有下面的屬性
execution-context.png

變量對象(Variable object)

變量對象是一個數據容器與執行上下文相關聯。這是一個存儲上下文中定義的變量和函數聲明的特殊對象(請注意函數表達式)不包括在變量對象。

var name = "哈士奇"
function foo(){}
(function bar(){});

console.log(this.name = "哈士奇"); //true
console.log(bar); //bar is not defined;

全局上下文的變量對象將具備如下屬性

variable-object.png

激活對象

當調用函數時,將建立一個稱爲激活對象的特殊對象,他填充了形式參數和arguments對象,而後激活對象被用做函數上下文的變量對象。

function foo(x, y){
    var z = 30;
    function bar(){}
    (function baz(){});
}

foo(10, 20);

foo函數的上下文裏有這些屬性,而且函數表達式bar沒有被包含在變量對象中。

activation-object.png

做用域鏈

在執行上下文建立的時候,在變量對象以後建立做用域鏈,做用域鏈自己就包含變量對象,當函數被調用的時候,做用域鏈包含的是激活對象。
當被要求解析變量時,JavaScript始終從代碼嵌套的最內層開始,並保持對父做用域的鏈接,直到找到變量或其餘所需的資源。做用域鏈能夠簡單地定義爲包含其本身的執行上下文的變量對象的對象,以及父做用域的全部執行上下文。

var x = 10;
 
(function foo() {
  var y = 20;
  (function bar() {
    var z = 30;
    console.log(x + y + z);
  })();
})();

咱們能夠用__parant__這個屬性來假定做用域鏈的鏈接,它指的是鏈中的下一個對象。
scope-chain.png

相關文章
相關標籤/搜索