JavaScript 本身可以算是一門(mén)簡(jiǎn)單的語(yǔ)言,但我們也不斷用智慧和靈活的模式來(lái)改進(jìn)它敷硅。昨天我們將這些模式應(yīng)用到了 JavaScript 框架中,今天這些框架又驅(qū)動(dòng)了我們的 Web 應(yīng)用程序。很多新手開(kāi)發(fā)者被各種強(qiáng)大的 JavaScript 框架吸引進(jìn)來(lái)潭枣,但他們卻忽略了框架身后浩如星海的 JavaScript 實(shí)用技巧。本文將為你呈獻(xiàn)其中7個(gè)基礎(chǔ)知識(shí)點(diǎn)幻捏。
1. 在 String.prototype.replace 方法中使用 /g 和 /i 標(biāo)志位
令很多 JavaScript 初學(xué)者意外的是盆犁,字符串的 replace 方法并不會(huì) 替換所有匹配的子串——而僅僅替換第一次匹配。當(dāng)然 JavaScript 老手們都知道這里可以使用正則表達(dá)式篡九,并且需要加上一個(gè)全局標(biāo)志位(/g):
// Mistake
// 踩到坑了
var str = "David is an Arsenal fan, which means David is great";
str.replace("David", "Darren"); // "Darren is an Arsenal fan, which means David is great"
// Desired
// 符合預(yù)期
str.replace(/David/g, "Darren"); // "Darren is an Arsenal fan, which means Darren is great"
另一個(gè)基本的邏輯錯(cuò)誤就是在大小寫(xiě)不敏感的校驗(yàn)場(chǎng)合(字母可大寫(xiě)可小寫(xiě))沒(méi)有忽略大小寫(xiě)谐岁,此時(shí) /i 標(biāo)志位就很實(shí)用:
str.replace(/david/gi, "Darren"); // "Darren will always be an Arsenal fan, which means Darren will always be great"
(譯注:上面這段例程我沒(méi)有看懂用意榛臼,可能是注釋有誤吧……) 每個(gè) JavaScript 開(kāi)發(fā)者都曾踩過(guò)這兩個(gè)標(biāo)志位的坑——因此別忘了在適當(dāng)?shù)臅r(shí)候用上它們伊佃!
2. 類(lèi)數(shù)組對(duì)象和 Array.prototype.slice 方法
數(shù)組的 slice 方法通常用來(lái)從一個(gè)數(shù)組中抽取片斷。但很多開(kāi)發(fā)者不了解的是讽坏,這個(gè)方法還可以用來(lái)將“類(lèi)數(shù)組”元素(比如 arguments 參數(shù)列表锭魔、節(jié)點(diǎn)列表和屬性列表)轉(zhuǎn)換成真正的數(shù)組:(譯注:DOM 元素的屬性列表通過(guò) attributes 屬性獲取,比如 document.body.attributes路呜。)
var nodesArr = Array.prototype.slice.call(document.querySelectorAll("div"));
// "true" array of DIVs
// 得到一個(gè)由 div 元素組成的“真正的”數(shù)組
var argsArr = Array.prototype.slice.call(arguments);
// changes arguments to "true" array
// 把 arguments 轉(zhuǎn)換成一個(gè)“真正的”數(shù)組
你還可以使用一次簡(jiǎn)單的 slice 調(diào)用來(lái)克隆一個(gè)數(shù)組:
var clone = myArray.slice(0); // naive clone
// 淺克隆
∶耘酢(譯注:這里的參數(shù) 0 也可以省略织咧,我估計(jì) undefined 被 slice 方法自動(dòng)轉(zhuǎn)換為 0 了吧。)
Array.prototype.slice 絕對(duì)是 JavaScript 世界中的一玫珍寶漠秋,但 JavaScript 初學(xué)者們顯然還沒(méi)有意識(shí)到它的全部潛力笙蒙。
3. Array.prototype.sort 方法
數(shù)組的 sort 方法 遠(yuǎn)遠(yuǎn)沒(méi)有被充分利用,而且可能比開(kāi)發(fā)者們想像的更加強(qiáng)大庆锦。很多開(kāi)發(fā)者可能覺(jué)得 sort 方法可以用來(lái)做這種事情:
[1, 3, 9, 2].sort();
// 返回 [1, 2, 3, 9]
……這沒(méi)錯(cuò)捅位,但它還有更強(qiáng)大的用法,比如這樣:
[
{ name: "Robin Van PurseStrings", age: 30 },
{ name: "Theo Walcott", age: 24 },
{ name: "Bacary Sagna", age: 28 }
].sort(function(obj1, obj2) {
// Ascending: first age less than the previous
// 實(shí)現(xiàn)增序排列:前者的 age 小于后者
return obj1.age - obj2.age;
});
// Returns:
// [
// { name: "Theo Walcott", age: 24 },
// { name: "Bacary Sagna", age: 28 },
// { name: "Robin Van PurseStrings", age: 30 }
// ]
你不僅可以對(duì)簡(jiǎn)單類(lèi)型的數(shù)組項(xiàng)進(jìn)行排序搂抒,可以通過(guò)屬性來(lái)排序?qū)ο笸Р蟆H绻奶旆?wù)器端發(fā)來(lái)一段 JSON 數(shù)據(jù),而且其中的對(duì)象需要排序求晶,你可別忘了這一招焰雕!
4. 用 length 屬性來(lái)截?cái)鄶?shù)組
幾乎所有開(kāi)發(fā)者都踩過(guò) JavaScript 的這個(gè)坑——“傳對(duì)象只是傳引用”。開(kāi)發(fā)者們經(jīng)常會(huì)試圖 把一個(gè)數(shù)組清空芳杏,但實(shí)際上卻錯(cuò)誤地創(chuàng)建了一個(gè)新數(shù)組矩屁。
var myArray = yourArray = [1, 2, 3];
// :(
// 囧
myArray = []; // `yourArray` is still [1, 2, 3]
// `yourArray` 仍然是 [1, 2, 3]
// The right way, keeping reference
// 正確的方法是保持引用
myArray.length = 0; // `yourArray` and `myArray` both [ ]
// `yourArray` 和 `myArray`(以及其它所有對(duì)這個(gè)數(shù)組的引用)都變成 [ ] 了
坑里的人們終于明白,原來(lái)傳對(duì)象只是在傳引用爵赵。因此當(dāng)我把 myArray 重新賦值為 [] 時(shí)吝秕,確實(shí)會(huì)創(chuàng)建出一個(gè)新的空數(shù)組,但其它對(duì)老數(shù)組的引用仍然沒(méi)變空幻!大坑八盖汀!還是換用截?cái)嗟姆椒ò煞招倌辍?/p>
5. 使用 push 來(lái)合并數(shù)組
在上面的第 2 節(jié)里则剃,我展示了數(shù)組的 slice 和 apply 方法所能組合出的幾個(gè)小妙招耘柱,所以對(duì)于數(shù)組方法的其它技巧如捅,你應(yīng)該已經(jīng)做好心理準(zhǔn)備了吧。這次我們使用 push 方法來(lái)合并數(shù)組:
var mergeTo = [4,5,6];
var mergeFrom = [7,8,9];
Array.prototype.push.apply(mergeTo, mergeFrom);
mergeTo; // is: [4, 5, 6, 7, 8, 9]
這是一項(xiàng)不為人知的小技巧调煎,簡(jiǎn)單的原生方法就可以實(shí)現(xiàn)數(shù)組合并這樣的常見(jiàn)任務(wù)镜遣。(譯注:這個(gè)方法的巧妙之外不僅在于 push 方法可以接收多個(gè)參數(shù),還涉及到 apply 方法的第二個(gè)參數(shù)的用法士袄。)
6. 高效探測(cè)功能特性和對(duì)象屬性
很多時(shí)候開(kāi)發(fā)者們會(huì)像下面這樣來(lái)探測(cè)瀏覽器的某個(gè)特性:
if(navigator.geolocation) {
// Do some stuff
// 在這里干點(diǎn)事情
}
當(dāng)然這可以正常工作悲关,但它并不一定有很好的效率。因?yàn)檫@個(gè)對(duì)象探測(cè)方法會(huì)在瀏覽器中初始化資源娄柳。在過(guò)去寓辱,上面的代碼片斷可能會(huì)在某些瀏覽器下導(dǎo)致內(nèi)存泄露。更好赤拒、更快的方法是檢查對(duì)象是否包含某個(gè)鍵名:
if("geolocation" in navigator) {
// Do some stuff
// 在這里干點(diǎn)事情
}
鍵名檢查十分簡(jiǎn)單秫筏,而且可以避免內(nèi)存泄露诱鞠。另外請(qǐng)注意,如果這個(gè)屬性的值是假值这敬,那么前一種探測(cè)方式將會(huì)得到“否”的結(jié)果航夺,并不能真正探測(cè)出這個(gè)鍵名是否存在。
7. 事件對(duì)象的 preventDefault 和 stopPropagation 方法
很多時(shí)候崔涂,當(dāng)一個(gè)動(dòng)作元素(比如鏈接)被點(diǎn)擊時(shí)阳掐,會(huì)觸發(fā)某個(gè)功能。顯然我們并不希望點(diǎn)擊鏈接時(shí)瀏覽器順著這個(gè)鏈接跳轉(zhuǎn)冷蚂,于是我們會(huì)習(xí)慣性地使用 JavaScript 類(lèi)庫(kù)的 Event.stop 方法:
$("a.trigger").on("click", function(e) {
e.stop();
// Do more stuff
// 在這里干點(diǎn)事情
});
$员!(譯注:不知道哪個(gè)類(lèi)庫(kù)有這個(gè)方法,估計(jì)其作用相當(dāng)于 return false; 吧蝙茶。語(yǔ)法看起來(lái)像 jQuery涮俄,但 jQuery 并沒(méi)有這個(gè)方法,而且 jQuery 是支持 e.preventDefault 和 e.stopPropagation 方法的尸闸。)
這個(gè)懶方法有一個(gè)問(wèn)題彻亲,它不僅阻止了瀏覽器的默認(rèn)動(dòng)作,同時(shí)也阻止了事件繼續(xù)冒泡吮廉。這意味著元素上綁定的其它事件監(jiān)聽(tīng)器將不會(huì)被觸發(fā)苞尝,因?yàn)樗鼈兏揪筒恢烙惺录l(fā)生。此時(shí)不妨使用 preventDefault 吧宦芦!