你可能不知道的一些JavaScript 奇技淫巧
這里記錄一下以前學(xué)習(xí)各種書籍和文章里邊出現(xiàn)的JS的小技巧,分享給大家,也供自己查閱,同時(shí)感謝那些發(fā)現(xiàn)創(chuàng)造和分享這些技巧的前輩和大牛們。
1、遍歷一個(gè)obj的屬性到數(shù)組
- var a=[];
- for(a[a.length] in obj);
- return a;
乍一看可能比較蒙,不過仔細(xì)分析還是不難理解的。常見用法是for(var key in obj),這里key初始也是undefined的,a[a.length]整體也是undefined,所以二者其實(shí)是等價(jià)的。在for循環(huán)中,obj 的屬性會(huì)依次賦值給key,同樣,也依次賦值給a[a.length],這里length一直在變,就巧妙地挨個(gè)賦值給數(shù)組的每一個(gè)元素了。
2、重復(fù)字符串(如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);//之所以要?jiǎng)?chuàng)建帶length屬性的對(duì)象,是因?yàn)檎{(diào)用數(shù)組原型方法時(shí),必須是一個(gè)類數(shù)組對(duì)象,而類數(shù)組對(duì)象的條件就是length為非負(fù)整數(shù)
- }
不新建數(shù)組,而是用擁有l(wèi)ength屬性的對(duì)象替代,然后調(diào)用數(shù)組的join方法,性能提升很大
再改進(jìn):
- var repeat=(function(){
- join=Array.prototype.join,obj={};
- return function(target,n){
- obj.length=n+1;
- return join.call(obj,target); } })();
利用閉包將對(duì)象和join方法緩存起來,不用每次都新建對(duì)象和尋找方法
3、for循環(huán)中,當(dāng)?shù)诙?xiàng)為false時(shí)會(huì)終止循環(huán),這里并不一定存在比較,可以直接賦值,如果賦值為undefined之類的值時(shí),轉(zhuǎn)成bool值也為假,因此也會(huì)終止,比如遍歷數(shù)組可以寫成:
- for(var i=arr.length,element;element=arr[—-i];){…}
這里,第二項(xiàng)一定是arr[—-i]而非arr[i--],如果是后者的話,上來就是undefined,就不會(huì)執(zhí)行循環(huán)體,或者for(var i=0,element;element=arr[i++];){…}
4、NaN是JS中唯一不等于自己的值,因此可以用來判斷一個(gè)變量是否真的為NaN:a!==a
5、“<”,”+”等運(yùn)算符會(huì)強(qiáng)制符號(hào)兩邊的表達(dá)式執(zhí)行valueOf然后比較,所以如果兩邊是函數(shù)或者對(duì)象,而又重寫了該對(duì)象的valueOf方法,就會(huì)自動(dòng)執(zhí)行兩邊的方法。如:
- var a={valueOf:function(){console.log(“aaa”);}},b={valueOf:function(){console.log(“bbb”);}};
- a<b;//會(huì)輸出:aaa;bbb;false;
6、JS具備自動(dòng)插入分號(hào)的能力,但是自動(dòng)插入分號(hào)并不是***的,其有三條規(guī)則:
1)只在”}”標(biāo)記之前、一個(gè)或多個(gè)換行之后以及程序輸入的結(jié)尾被插入;
2)分號(hào)只在隨后的輸入標(biāo)記不能被解析時(shí)插入;
這一點(diǎn)很重要,比如:
- a=b
- (f());
是不會(huì)在a=b之后自動(dòng)插入分號(hào)的,因?yàn)閍=b(f())是可以被解析的,因此像”(“,”[“,”+”,”-“,”/“開頭的時(shí)候,需要特別注意上一行可能不會(huì)自動(dòng)插入。
還有一些情況,盡管不會(huì)出現(xiàn)解析錯(cuò)誤,JS仍然會(huì)強(qiáng)制插入分號(hào),這就是所謂的JS語法限制產(chǎn)生式。它不允許在兩個(gè)字符間出現(xiàn)換行,最危險(xiǎn)的就是return語句,如
return
{};
會(huì)被強(qiáng)制插入而成為
return;
{};
類似的還有:throw語句、帶有顯示標(biāo)簽的break活著continue語句、后置自增或自減運(yùn)算符
3)分號(hào)不會(huì)作為分隔符在for循環(huán)空語句的頭部被自動(dòng)插入
因此,***的辦法是在自己的js文件的最開始防御性地插入”;”,這樣在合并js文件的時(shí)候就不會(huì)出問題了。
7、閉包。理解閉包需學(xué)會(huì)三個(gè)基本事實(shí):
(1)JS允許你引用在當(dāng)前函數(shù)意外定義的變量
(2)即使外部函數(shù)已經(jīng)返回,當(dāng)前函數(shù)仍然可以引用在外部函數(shù)所定義的變量。這是因?yàn)镴S的函數(shù)值包含里比調(diào)用它們時(shí)執(zhí)行所需要的代碼更多的信息
(3)閉包可以更新外部變量的值。這是因?yàn)殚]包存儲(chǔ)的是外部變量的引用而非值副本。如:
- 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
這一點(diǎn)很重要,比如在函數(shù)的for循環(huán)體內(nèi)返回閉包或者有閉包取for循環(huán)的計(jì)數(shù)器值,那么這個(gè)閉包取到的永遠(yuǎn)是for循環(huán)結(jié)束時(shí)i的最終值,因?yàn)殚]包存儲(chǔ)的是它的引用而非當(dāng)時(shí)的值副本。
8、JS沒有塊級(jí)作用域,因此通常情況下函數(shù)內(nèi)部的所有變量都是綁定到函數(shù)作用域的,也就是說相當(dāng)于都在函數(shù)一開始就聲明了的,一個(gè)例外就是try/catch中的變量是塊級(jí)的,只屬于try/catch塊。
9、眾所周知,在函數(shù)內(nèi)部聲明函數(shù)是可以的,但是在在函數(shù)內(nèi)的局部塊里聲明,可能會(huì)出現(xiàn)問題:
- 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”]
將函數(shù)聲明到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);//?
結(jié)果會(huì)如何呢?理論上講,JS沒有塊級(jí)作用域,因此f()的作用域是整個(gè)test函數(shù),因此合理猜測(cè)應(yīng)該是與上一次輸出相同,全部 為”local”,可是并不是所有的JS執(zhí)行環(huán)境都如此行事,有的會(huì)根據(jù)是否執(zhí)行包含f的代碼塊來有條件地綁定函數(shù)f(綁定即意味著將該變量綁定到其最近 的作用域,而賦值是發(fā)生在代碼實(shí)際執(zhí)行到賦值那一步的時(shí)候進(jìn)行的)。
因此***的辦法是如果要聲明嵌套函數(shù),都在其富函數(shù)的最外層聲明,要么就不要聲明函數(shù),而是使用var聲明和函數(shù)表達(dá)式來實(shí)現(xiàn):
- 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創(chuàng)建字典的時(shí)候,如果是利用對(duì)象的方式(因?yàn)镴S對(duì)象的核心是一個(gè)字符串屬性名稱和屬性值的映射表),會(huì)遇到一個(gè)問題就是原型污染,因 為獲取字典屬性值的時(shí)候用hasOwnProperty還好,如果用for in遍歷的話,不僅會(huì)遍歷對(duì)象本身,包括它的原型,因此如果在其他地方污染了Object的原型,那么for in就會(huì)產(chǎn)生非預(yù)期的結(jié)果,這時(shí)可能會(huì)用hasOwnProperty來先檢測(cè)該對(duì)象本身是否含有屬性來避免原型污染,然而更極端的情況是連 hasOwnProperty這個(gè)原型方法都有可能被污染。避免原型污染的方法是在創(chuàng)建字典對(duì)象的時(shí)候用Object.create(null)來創(chuàng)建一 個(gè)完全空對(duì)象,這個(gè)對(duì)象沒有原型,這個(gè)方法是ES5的,在沒有這個(gè)方法可用的時(shí)候,***是創(chuàng)建字典類,然后在字典類里用數(shù)組來存儲(chǔ)有序集合,自己維護(hù)這個(gè) 集合。
11、JS中的類數(shù)組對(duì)象可以享用數(shù)組的大部分原型方法如map等,類數(shù)組對(duì)象是指滿足兩個(gè)條件的對(duì)象:一是具備合理范圍值內(nèi)的length屬性, 二是length屬性大于該對(duì)象的***索引,索引是一個(gè)合理范圍的證書,它的字符串表示的是對(duì)象的一個(gè)key;但是數(shù)組的一個(gè)原型方法contact是不 能被類數(shù)組對(duì)象調(diào)用的,因此需要先用[].slice.call把類數(shù)組對(duì)象轉(zhuǎn)換為真正的數(shù)組比如[].slice.call(arguments)。
12、并不是所有時(shí)候都需要繼承,繼承也不是***的,有時(shí)候會(huì)創(chuàng)造比他能解決的更多的問題,特別是當(dāng)層次關(guān)系沒那么明顯的時(shí)候,這時(shí)候應(yīng)該多用結(jié)構(gòu) 類型(又叫鴨子類型,如果它看起來像鴨子、游泳像鴨子并且叫聲像鴨子,那么它就是鴨子),用結(jié)構(gòu)類型設(shè)計(jì)靈活的對(duì)象接口的時(shí)候,不需要?jiǎng)?chuàng)建類工廠來返回類 的實(shí)例,而是直接返回對(duì)象,對(duì)象具備預(yù)期的方法和屬性,比如:
原文鏈接:http://www.cnblogs.com/dson/p/4415278.html
【編輯推薦】
- JavaScript全文搜索之相關(guān)度評(píng)分
- 編程語言流行榜:JavaScript 坐頭把交椅,Swift 躥升最快
- JavaScript中常見的字符串操作函數(shù)及用法
- JavaScript開發(fā)者:Win10斯巴達(dá)瀏覽器很贊
- JavaScript將在企業(yè)環(huán)境中引發(fā)巨大變革
【責(zé)任編輯:wangxueyan TEL:(010)68476606】