歡迎光臨
每天分享高質量文章

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

這裡記錄一下以前學習各種書籍和文章裡邊出現的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、“

var a={valueOf:function(){console.log(“aaa”);}},b={valueOf:function(){console.log(“bbb”);}};

a


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)來創建一個完全空物件,這個物件沒有原型,這個方法是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

}

}

來自:FirstLovt

鏈接:http://www.cnblogs.com/dson/p/4415278.html



赞(0)

分享創造快樂