《你不知道的JavaScript(上卷)》讀書總結之做用域

這本書我讀的很慢,一部分緣由是本身水平有限要慢慢理解,還有一個緣由是畢竟是翻譯過來的書,因此有些地方讀起來不會很順,可是我仍是奉行本身的原則:讀下來總會有收穫。因此,也準備寫一個系列的關於讀書總結的博客,包括做用域、閉包、this、對象和原型幾篇。這些博客並非原抄書中內容,而是更多加上了本身的感悟,因此,有不對之處,歡迎噴。閉包

在解釋某種原理的時候,不少時候都是經過想象(假設)某個模型來解釋,好比DOM(document object model文檔對象模型),對DOM操做其實就是對DO的操做,但是仍是加了一個M,大概就是這個意思,還有對變量的訪問權限的解釋引用了做用域,解釋做用域引用了做用域鏈,其實都是引用一種模型,經濟學書中解釋某些經濟學原理的時候,也習慣引用模型,好比假設一個封閉區域,即不考慮對外國貿易。函數

理解JavaScript做用域先要記住如下幾條原理,咱們一條一條來解釋。this

1.函數的一個做用就是造成做用域,在函數外部沒法訪問在函數內部聲明的變量,相反,函數內部能夠訪問到在函數外部聲明的變量,狀況以下:翻譯

var a=100;
     function fn1(){
         var b=100;
         console.log(a)
     }
     fn1();
     console.log(b);

結果爲code

  • 100 (fn1函數內部能夠訪問在外部定義的a)
  • ReferenceError: b is not defined (報錯,b未被定義,由於fn1函數外部沒法訪問其內部定義的變量)

2.而且要記住JS代碼是從上至下一條一條執行的,在變量聲明的時候無需判斷做用域,只有執行的時候才須要判斷變量的做用域:對象

function fn1(){
         console.log(a);
      }
     var a=100;
     fn1();
function fn2(){
         console.log(b);
      };
      fn2();
      var b=100;

這兩段代碼的運行結果不同,前面的結果爲100;後面的爲undefined,由於var b=100在fn2函數執行的後面,JS代碼是從上至下一條一條執行的,爲何會是undefined而不是報錯,由於變量提高,後面第3點有說明。當你讀fn1函數內部代碼的時候,看到了變量a,不能認爲fn1函數前面沒有對a進行定義和賦值,就認爲console會報錯,這是fn1並無被執行,不該該對做用域進行判斷,而應該繼續日後讀,當讀到fn1()代碼時,才應該對函數內部的變量a進行做用域的判斷。換句話說,做用域的範圍就是,變量的聲明、賦值只做用於其對應的聲明、賦值的後面的區域。ip

3.JS代碼被執行前,編譯器先對其進行編譯,全部用var聲明的變量和函數會被提高到各自做用域的頂部,變量賦值不提高,而用 function fn(){ } 形式聲明的函數會被總體提高:作用域

fn1();
     console.log(a); //undefined
     function fn1(){
         console.log(b); //undefined
         var b=200;
     }
     var a=100;

輸出結果爲兩個undefined,若是一個變量定義(var)了可是未被賦值,則其值默認爲undefined,這段代碼等價於下面這段代碼:文檔

function fn1(){
         var b;
         console.log(b);
         b=200;
     }
     var a;
     fn1();
     console.log(a);
     a=100;

再看下面這段代碼:原型

fn1();
     fn2();
     var fn2= function (){
        console.log("fn2函數被執行了");
     }
     function fn1(){
        console.log("fn1函數被執行了");
     }

結果爲

  • fn1函數被執行了
  • Uncaught TypeError: fn2 is not a function

fn1和fn2都會被提高,可是fn1會被總體提高,上面這段代碼等價於:

var fn2;
     function fn1(){
        console.log("fn1函數被執行了");
     }
     fn1();
     fn2();
     fn2= function (){
        console.log("fn2函數被執行了");
     }

4.這點比較特殊,能夠單獨考慮,即函數內部沒有用var聲明的變量能夠直接視爲全局變量。

function fn1(){
         return  function (){
             a=100;
             console.log(a); //100
          }
      }
     fn1()();
     console.log(window.a); //100

這段代碼並不會報錯,而是輸出兩個100,用window.a的方法能夠檢測a是否爲全局變量,這裏能夠輸出100,說明第4點原理是對的,不然輸出undefined。

5.說說ES6,ES6中多了let和const兩種方式定義變量。前面說到只有函數纔會造成做用域,判斷和循環不會造成做用域,以下面代碼,在if(){ }後面是能夠訪問到if語句裏的變量a的,雖然if語句像函數同樣有大括號{ }。

function fn1(){
         if(true){
             var a=100;
         }
         console.log(a); //100
      }
     fn1();

可是在ES6中,只要在大括號{ }內部利用let和const聲明的變量,那麼這個變量的做用域就侷限於這個{ }內部:

{
        let a=100;
    }
    console.log(a)

結果爲ReferenceError: a is not defined(引用錯誤,a未被定義)。 而且用let和const聲明的變量都不會有像var同樣有提高:

console.log(a);
    let a=100;
    //報錯 Uncaught ReferenceError: a is not defined

const的做用是定義一個常量,其值固定,對其任何修改都將致使報錯:

if(true){
        const a=100;
        a=100;
    }

結果爲報錯。

相關文章
相關標籤/搜索