你可能不知道的一些JavaScript 奇巧淫技

來源:互聯網
上載者:User

標籤:

  這裡記錄一下以前學習各種書籍和文章裡邊出現的JS的小技巧,分享給大家,也供自己查閱,同時感謝那些發現創造和分享這些技巧的前輩和大牛們。

1、遍曆一個obj的屬性到數組

  var a=[];  for(a[a.length] in obj);  return a;

  乍一看可能比較蒙,不過仔細分析還是不難理解的。常見用法是for(var key in obj),這裡key初始也是undefined的,a[a.length]整體也是undefined,所以二者其實是等價的。在for迴圈中,obj的屬性會依次賦值給key,同樣,也依次賦值給a[a.length],這裡length一直在變,就巧妙地挨個賦值給數組的每一個元素了。

2、重複字串(如abc=>abcabc)

     function repeat(target,n){          return (new Array(n+1).join(target));     }

  改良版本:

     function repeat(target,n){          return Array.prototype.join.call(          {length:n+1},target);//之所以要建立帶length屬性的對象,是因為調用數組原型方法時,必須是一個類數組對象,而類數組對象的條件就是length為非負整數     }

  不建立數組,而是用擁有length屬性的對象替代,然後調用數組的join方法,效能提升很大  

  再改進:

     var repeat=(function(){          var join=Array.prototype.join,obj={};          return function(target,n){               obj.length=n+1;               return join.call(obj,target);          }     })();

  利用閉包將對象和join方法緩衝起來,不用每次都建立對象和尋找方法

3、for迴圈中,當第二項為false時會終止迴圈,這裡並不一定存在比較,可以直接賦值,如果賦值為undefined之類的值時,轉成bool值也為假,因此也會終止,比如遍曆數組可以寫成:

    for(var i=arr.length,element;element=arr[—-i];){…}

  這裡,第二項一定是arr[—-i]而非arr[i--],如果是後者的話,上來就是undefined,就不會執行迴圈體,或者for(var i=0,element;element=arr[i++];){…}

4、NaN是JS中唯一不等於自己的值,因此可以用來判斷一個變數是否真的為NaN:a!==a

5、“<”,”+”等運算子會強制符號兩邊的運算式執行valueOf然後比較,所以如果兩邊是函數或者對象,而又重寫了該對象的valueOf方法,就會自動執行兩邊的方法。如:

    var a={valueOf:function(){console.log(“aaa”);}},b={valueOf:function(){console.log(“bbb”);}};    a<b;//會輸出:aaa;bbb;false;

6、JS具備自動插入分號的能力,但是自動插入分號並不是萬能的,其有三條規則:

  1)只在”}”標記之前、一個或多個換行之後以及程式輸入的結尾被插入;

  2)分號只在隨後的輸入標記不能被解析時插入;       這一點很重要,比如:       a=b       (f());       是不會在a=b之後自動插入分號的,因為a=b(f())是可以被解析的,因此像”(“,”[“,”+”,”-“,”/“開頭的時候,需要特別注意上一行可能不會自動插入。    還有一些情況,儘管不會出現解析錯誤,JS仍然會強制插入分號,這就是所謂的JS文法限制產生式。它不允許在兩個字元間出現換行,最危險的就是return語句,如    return     {};    會被強制插入而成為    return;    {};    類似的還有:throw語句、帶有顯示標籤的break活著continue語句、後置自增或自減運算子  3)分號不會作為分隔字元在for迴圈空語句的頭部被自動插入    因此,最好的辦法是在自己的js檔案的最開始防禦性地插入”;”,這樣在合并js檔案的時候就不會出問題了。7、閉包。理解閉包需學會三個基本事實:(1)JS允許你引用在當前函數意外定義的變數(2)即使外部函數已經返回,當前函數仍然可以引用在外部函數所定義的變數。這是因為JS的函數值包含裡比調用它們時執行所需要的代碼更多的資訊(3)閉包可以更新外部變數的值。這是因為閉包儲存的是外部變數的引用而非值副本。如:
function box(){     var val=undefined;     return {          set:function(x){val=x;},          get:function(){return val;}     };}var b=box();b.get();//“undefined”b.set(5);b.get();//5

  這一點很重要,比如在函數的for迴圈體內返回閉包或者有閉包取for迴圈的計數器值,那麼這個閉包取到的永遠是for迴圈結束時i的最終值,因為閉包儲存的是它的引用而非當時的值副本。

8、JS沒有塊級範圍,因此通常情況下函數內部的所有變數都是綁定到函數範圍的,也就是說相當雩都在函數一開始就聲明了的,一個例外就是try/catch中的變數是塊級的,只屬於try/catch塊。

9、眾所周知,在函數內部聲明函數是可以的,但是在在函數內的局部塊裡聲明,可能會出現問題:

function f(){return “global”;}function test(x){     function f(){return “local”}     var result=[];     if(x){          result.push(f());     }     result.push(f());     return result;}test(true);//[“local”,”local”]test(false);//[“local”]

將函式宣告到if塊中:

function f(){return “global”;}function test(x){     var result=[];     if(x){          function f(){return “local”}          result.push(f());     }     result.push(f());     return result;} test(true);//?test(false);//?
結果會如何呢?理論上講,JS沒有塊級範圍,因此f()的範圍是整個test函數,因此合理猜測應該是與上一次輸出相同,全部為”local”,可是並不是所有的JS執行環境都如此行事,有的會根據是否執行包含f的代碼塊來有條件地綁定函數f(綁定即意味著將該變數綁定到其最近的範圍,而賦值是發生在代碼實際執行到賦值那一步的時候進行的)。因此最好的辦法是如果要聲明嵌套函數,都在其富函數的最外層聲明,要麼就不要聲明函數,而是使用var聲明和函數運算式來實現:
function f(){return “global”;}function test(x){     var result=[];     if(x){          var g=function(){return “local”}          result.push(g());     }     result.push(f());     return result;}  

10、用js建立字典的時候,如果是利用對象的方式(因為JS對象的核心是一個字串屬性名稱和屬性值的映射表),會遇到一個問題就是原型汙染,因為擷取字典屬性值的時候用hasOwnProperty還好,如果用for in遍曆的話,不僅會遍曆對象本身,包括它的原型,因此如果在其他地方汙染了Object的原型,那麼for in就會產生非預期的結果,這時可能會用hasOwnProperty來先檢測該對象本身是否含有屬性來避免原型汙染,然而更極端的情況是連hasOwnProperty這個原型方法都有可能被汙染。避免原型汙染的方法是在建立字典對象的時候用Object.create(null)來建立一個完全Null 物件,這個對象沒有原型,這個方法是ES5的,在沒有這個方法可用的時候,最好是建立字典類,然後在字典類裡用數組來儲存有序集合,自己維護這個集合。

11、JS中的類數組對象可以享用數組的大部分原型方法如map等,類數組對象是指滿足兩個條件的對象:一是具備合理範圍值內的length屬性,二是length屬性大於該對象的最大索引,索引是一個合理範圍的認證,它的字串表示的是對象的一個key;但是數組的一個原型方法contact是不能被類數組對象調用的,因此需要先用[].slice.call把類數組對象轉換為真正的數組比如[].slice.call(arguments)。

12、並不是所有時候都需要繼承,繼承也不是完美的,有時候會創造比他能解決的更多的問題,特別是當層次關係沒那麼明顯的時候,這時候應該多用結構類型(又叫鴨子類型,如果它看起來像鴨子、遊泳像鴨子並且叫聲像鴨子,那麼它就是鴨子),用結構類型設計靈活的對象介面的時候,不需要建立類工廠來返回類的執行個體,而是直接返回對象,對象具備預期的方法和屬性,比如:

SomeObj.someWidget=function(opts){     return {          a:blabla,          b:function(){...},          c:blabla     }}

 

你可能不知道的一些JavaScript 奇巧淫技

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.