你不知道的JavaScript上卷筆記

你不知道的JavaScript上卷筆記

前言

You don't know JavaScript是github上一個系列文章
 
初看到這一標題的時候,感受怎麼老外也搞標題黨,用這種衝突性比較強的題目吸引眼球,以至最初真沒去看內容。直到出了中文版《你不知道的JavaScript》,一看評價你們都說好,買來一讀,內容果真很好,不少地方,讓我這個半路轉行JavaScript的人豁然開朗。中文版如今出了上卷,中卷應該很快會推出,下卷就要等久一點了javascript

做用域和閉包

做用域是什麼

  1.現代JavaScript已經再也不是解釋執行的,而是編譯執行的。可是與傳統的編譯語言不一樣,它不是提早編譯,編譯結果不能進行移植。編譯過程當中,一樣會通過分詞/詞法分析,解析/語法分析,代碼生成三個階段。html

  2.以var a = 2;語句爲例,對這一程序語句對處理,須要通過引擎,編譯器,做用域三者的配合。其中,引擎從頭至尾負責整個javascript程序的編譯和執行過程;編譯器負責語法分析和代碼生成;做用域負責收集並維護由全部聲明的標識符組成的系列查詢,並實施一套規則,肯定當前執行的代碼對這些標識符的訪問權限。java

  3.對於var a = 2;編譯器首先查找做用域中是否已經有該名稱的變量,而後引擎中執行編譯器生成的代碼時,會首先查找做用域。若是找到就執行賦值操做,不然就拋出異常git

  4.引擎對變量的查找有兩種:LHS查詢RHS查詢。當變量出現中賦值操做左側時是LHS查詢,出現中右側是RHS查詢es6

詞法做用域

  1.詞法做用域就是定義在詞法階段的做用域。詞法做用域是由你在寫代碼時將變量和塊做用域寫在哪裏決定的,詞法處理器分析代碼時會保持做用域不變github

  1. function foo(a){ 
        var b = a * 2; 
        function bar(c){ 
            console.log(a,b,c); 
        } 
        bar(b * 3); 
    } 
    foo(2);     
    這個例子有三級嵌套的做用域
  2. 做用域查找會在找到第一個匹配的標識符時中止
  3. eval和with能夠欺騙詞法做用域,不推薦使用

函數做用域和塊做用域

  1. JavaScript具備基於函數的做用域,屬於這個函數的變量均可以在整個函數的範圍內使用及複用
  2. (function fun(){})()
    函數表達式和函數聲明的區別是看function關鍵字出如今聲明中的位置。若是function是聲明中的第一個詞,那麼就是一個函數聲明,不然就是一個函數表達式
  3. with,try/catch具備塊做用域,方便好用的實現塊級做用域的是es6帶來的let關鍵字

提高

  1.以下示例  web

a=2; 
var a; 
console.log(a); 

  上述代碼輸出的不是undefined,而是2。由於上述代碼須要通過編譯器的編譯,編譯過程當中首先會進行變量聲明,而後再由引擎進行變量賦值,因此,上述變量聲明雖然寫在第二行,可是聲明過程是首先執行的ajax

  1. console.log(a); 
    var a = 2; 
    上述代碼不是拋ReferenceError異常, 而是輸出undefined
  2. 只有聲明會被提高,賦值及其餘運行邏輯會留在原地 
  3. foo(); 
    function foo(){ 
        console.log(a);//undefined 
        var a = 2; 
    }
    foo();//不是ReferenceError,是TypeError var foo = function bar(){}; foo被提高並分配給所在做用域,因此foo()不會致使ReferenceError,可是foo沒有被賦值,對undefined進行函數調用,因此拋出TypeError

做用域閉包

  1. 將內部函數傳遞到所在詞法做用域之外,它都會持有對原始定義做用域的飲用,不管中何處執行這個函數都會使用閉包
  2. 本質上,不管什麼時候何地,若是將函數看成第一級的值類型並處處傳遞,就會看到閉包在這些函數中的應用。在定時器,事件監聽器,ajax請求,web workers或者其餘任何異步任務中,只要使用了回調函數,實際上就是在使用閉包
  3. 模塊的封裝利用了閉包,將內部變量隱藏,並返還一個公共api的對象,這一返回的對象對模塊的私有變量造成閉包訪問。

動態做用域

  1.詞法做用域是一套引擎如何尋找變量以及會在何處找到變量的規則。詞法做用域最重要的特徵是它的定義過程發生中代碼的書寫階段segmentfault

  2.動態做用域讓做用域做爲一個在運行時就被動態肯定的形式,而不是在寫代碼時進行靜態肯定的形式。設計模式

function foo(){ 
    console.log(a);//2 
} 
function bar(){ 
    var a = 3; 
    foo(); 
} 
var a = 2; 
bar(); 

詞法做用域讓foo()中的a經過RHS引用到了全局做用域中的a,因此輸出2;動態做用域不關心函數和做用域如何聲明以及在何處聲明,只關心從何處調用。換言之,做用域鏈是基於調用棧的,而不是代碼中的做用域嵌套。若是以動態做用域來看,上面代碼中執行時會輸出3

  3.JavaScript不具有動態做用域,可是this機制中某種程度上很像動態做用域,this關注函數如何調用。

this詞法

  1. es6經過箭頭函數,將this同詞法做用域聯繫起來了。
  2. 以前若是會遇到this丟失,常見方法時使用本地變量替換this引用 
    var obj = { 
        msg : 'awesome', 
        cool:function(){ 
            setTimeout(function timer(){ 
                console.log(this.msg); 
                },100);     
            } 
        }; 
    var msg = 'not awesome'; 
    obj.cool(); //not awesome 
    
    var obj = { 
        msg : 'awesome',
         cool:function(){ 
        var self = this; setTimeout(function timer(
            console.log(self.msg); 
            },100); 
        }  
    }; 
    var msg = 'not awesome'; 
    obj.cool(); //awesome 
    
    var obj = { 
        msg : 'awesome', 
        cool:function(){ setTimeout(() => { 
            console.log(this.msg); 
            },100); 
        } 
    }; 
    var msg = 'not awesome'; 
    obj.cool(); //awesome 
    
    var obj = { 
        msg : 'awesome', 
        cool:function(){ 
            setTimeout(function timer() { 
                console.log(this.msg); }.bind(this),100); 
                } 
        }; 
    var msg = 'not awesome'; 
    obj.cool(); //awesome            

this和對象原型

this全面解析

  1. 默認綁定。
    function foo(){ 
        console.log(this.a); 
    } 
    var a = 2; foo();//2
    上述示例中,函數調用應用了默認綁定,this指向全局對象
  2. 隱式綁定。調用位置是否有上下文對象,或者說是否被某個對象擁有或者包含。
    function foo(){ 
        console.log(this.a); 
    } 
    var obj={ 
        a:2,
         foo:foo 
    }; 
    obj.foo();//2 
    foo()被調用時,落腳點指向obj對象。當函數引用有上下文對象時,隱式對象將函數調用中的this綁定到上下文對象。
  3. 顯示綁定。使用apply,call或者bind顯示綁定
  4. new綁定。
    • JavaScript中的構造函數只是一些使用new操做符時被調用的函數。它們並不會屬於某個類,也不會實例化一個類。實際上,它們甚至不能說是一種特殊的函數類型,它們只是被new操做符調用的普通函數。
    • 使用new來調用函數,或者說發生構造函數調用,會自動執行下面操做:a)建立一個全新的對象 b)這個新對象會被執行[[原型]]鏈接 c)這個新對象會綁定到函數調用的this d)若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象
  5. 優先級。new綁定 > 顯示綁定 > 隱式綁定 > 默認綁定

對象

  1. 屬性描述符 
    var obj = {}; 
    Object.defineProperty(
        obj,
        "a",
        { 
            value:2, 
            writable:true, 
            configurable:true, 
            enumerable:true 
        }
    );
    obj.a;//2    
    • writable決定是否能夠修改屬性的值,writable爲false時,對obj.a的修改會靜默失敗。
    • configurable表示屬性是否能夠配置,若是爲false,再對obj調用defineProperty來修改設置,會拋出TypeError
    • enumerable表示屬性是否會出如今對象枚舉屬性中,若是爲false,就不會出如今for...in循環中
  2. 對象常量。結合使用writable:false和configurable:false能夠建立一個真正的常量屬性(不可修改,不可重定義,不可刪除)
  3. 禁止擴展。若是想禁止對一個對象添加新屬性而且保留已有屬性,可使用Object.preventExtensions()方法
  4. 密封。Object.seal()會建立一個密封的對象,這個方法實際上會在一個現有對象上調用Object.preventExtensions()方法,而且把全部屬性標記爲configurable:false
  5. 凍結。Object.freeze()會建立一個凍結對象,這個方法會在現有對象上調用Object.seal()方法,並把全部數據訪問的屬性標記爲writable:false
  6. [[get]],[[put]]。
    • var obj = { 
          this._a_ = 2; 
          get a(){ return this._a_; }, 
          set a(val){ this._a_ = val * 2; } 
      }; 
      Object.defineProperty( 
          obj, 
          "b", 
          { 
              get:function(){return this._a_ * 2}, 
              enumerable:true 
          } 
      )

原型

本節爲以爲書中寫的有點繞,不清晰。推薦閱讀

  1. constructor, prototype, proto 詳解
  2. JavaScript中proto與prototype的關係
  3. How does proto differ from constructor.prototype?
  4. 理解JavaScript面向對象的思路

行爲委託

  1. [[prototype]]機制是對象中一個內部連接引用另外一個對象 若是中第一個對象上沒找到須要的屬性或者方法引用,引擎就會繼續在[[prototyoe]]關聯的對象上進行查找。同理,若是後者中也沒找到須要的引用,就會繼續查找它的[[prototype]],以此類推,這一系列對象的連接稱爲原型鏈。換言之,這個機制就是對象之間的關聯關係。
  2. [[prototype]]是一種不一樣於類的設計模式,它是一種委託行爲的設計模式。
相關文章
相關標籤/搜索