那些容易被忽視的JavaScript細(xì)節(jié)總結(jié)
《JavaScript 權(quán)威指南》這本書從第四版開始,一直到第六版,每個(gè)版本我都逐字逐句讀過幾遍,然而每一遍下來的感受卻完全不一樣。上上周的周一,再次翻開了這本犀牛書,這一次我是帶著批判精神和研究精神過來的,所以看的時(shí)候也寫下了一些感受和筆記,都是些容易被忽略的點(diǎn),部分內(nèi)容犀牛書上不一定有提到。
之前都發(fā)在 微博 上,稍微整理了一番,放在這里,方便閱讀。
語句/表達(dá)式
換個(gè)角度理解語句(statemaents)和表達(dá)式(expressions):表達(dá)式不會(huì)改變程序的運(yùn)行狀態(tài),而語句會(huì)。還有一種叫做表達(dá)式語句,可以理解為表達(dá)式和語句的交集,如({a:1})
、"use strict;"
等,我覺得沒必要死扣,意義不大。
字符集
ES3 要求 JS 必須實(shí)現(xiàn) Unicode 2.1 及后續(xù)版本,而 ES5 只要求支持 Unicode 3 及后續(xù)版本。Unicode 字符 2005 年超過了十萬字符,至今仍在不斷增修,***版本是 8.0。
分號(hào)
如果你寫 JS 代碼不喜歡帶分號(hào),而又搞不清什么時(shí)候必須加分號(hào),可以這么做:在以 “(“、”[“ 、”/“、”+”、”-“ 開頭的語句前面都加上一個(gè)分號(hào),如 ;(a + b).toString()
。
進(jìn)制
ES5 嚴(yán)格模式中禁止使用八進(jìn)制。目前各種引擎對(duì) JS 的實(shí)現(xiàn)是存在差異的,部分支持八進(jìn)制,部分不支持。八進(jìn)制被禁止的原因:String 和 Number 之間經(jīng)常被相互轉(zhuǎn)換,而以0
開頭的八進(jìn)制數(shù)據(jù)特別容易讓人迷惑,也容易讓機(jī)器迷惑,比如 09
是該被轉(zhuǎn)換成 9 還是直接報(bào)錯(cuò)?十六進(jìn)制不存在這個(gè)問題,如 0x98。更多信息參閱 這里。
精度
JS 采用 IEEE-754 浮點(diǎn)數(shù)表示法,這是一種二進(jìn)制表示法,由于精度原因 JS 不能表示所有的實(shí)數(shù)。它能展示的浮點(diǎn)數(shù)個(gè)數(shù)是有限的,比如它不能準(zhǔn)確地表示三分之一的數(shù)值字面量。這也導(dǎo)致了它在浮點(diǎn)數(shù)的計(jì)算上存在誤差,如 0.3-0.2 != 0.2-0.1
,因?yàn)樵谟?jì)算的過程中,存在數(shù)據(jù)的溢出,丟失了精度。
null/undefined
系統(tǒng)級(jí)、出乎意料的或者類似錯(cuò)誤的值的空缺使用 undefined,而程序級(jí)、正常的或意料之中的值的空缺使用 null。平時(shí)編程給變量賦值時(shí),不要使用 undefined 而應(yīng)該用 null。值得注意的是 ES3 中的 undefined 是可以被重新賦值的,ES5 修復(fù)了這個(gè) bug。通常我們使用 void 0 來還原/代替 undefined 的值。
eval
eval 是個(gè)不好把握的東西,它在 ES3 中更像是 Function,而在 ES5 中更像是一個(gè)運(yùn)算符(嚴(yán)格模式下不允許設(shè)置別名,否則報(bào)錯(cuò),且將其作為保留字)。實(shí)際上 ES3 中也不允許給 eval 設(shè)置別名,然而很多實(shí)現(xiàn)卻依然允許,并將其作為全局代碼來執(zhí)行,瀏覽器尤其是 IE 對(duì)它實(shí)現(xiàn)相當(dāng)混亂,沒有什么規(guī)律可循,不過 IE 中提供了一個(gè) execScript 函數(shù),類似全局的 eval,這個(gè)函數(shù)每次執(zhí)行都會(huì)返回 null。
需要使用 eval 的場景并不多,盡量少用,一般需求使用 new Function 就能滿足。
引用
刪除屬性存在的坑:a = {n: {x: 2}}, b = a.n; delete a.n;
這段代碼執(zhí)行之后,b.x 依然等于 2,原因是 {x:2} 這個(gè)對(duì)象被 a 和 b 同時(shí)引用,delete 指令只刪除了 a 對(duì)它的引用,b 上的引用依然存在。這種問題有可能造成內(nèi)存泄漏。
Object 擴(kuò)展
Object 的 freeze 方法過于嚴(yán)格;defineGetter/lookupGetter 和對(duì)應(yīng)的 Setter 是很好用的屬性。
toLocalString
如圖,你可能還不知道 JavaScript 的 toLocaleString 還可以這么玩。
this語義
this 上下文只存在兩種語義,一種是被當(dāng)作方法調(diào)用,this 指向調(diào)用它的對(duì)象;一種是作為函數(shù)調(diào)用,指向 Global 對(duì)象(嚴(yán)格模式下為 undefined)。它沒有作用域的限制,如下圖所示,a 由于是作為函數(shù)被調(diào)用,所以它指向的是 window,故而返回 false。
類型
JavaScript 可以被調(diào)用執(zhí)行的均為 Function 類型,但是也存在可調(diào)用的 Object,如低版本 IE 中的一些宿主對(duì)象:document.getElementById、alert 等,在很多瀏覽器中 typeof RegExp 同樣是 Object。這絕對(duì)是一個(gè)不標(biāo)準(zhǔn)的實(shí)現(xiàn),在瀏覽器摒棄/修正這些錯(cuò)誤類型之前應(yīng)該盡量少依賴它們。
IE8 getter/setter
Object.defineProperty 雖然是 ES5 的東西,早在 IE8 就已經(jīng)支持了,但支持得并不完善,比如 writable、enumerable、configurable 這些配置項(xiàng)設(shè)置就無效,IE8 下主要支持 getter/setter。
JSON.stringify
JSON.stringify 接受三個(gè)參數(shù),很多人都知道第三個(gè)參數(shù)可以設(shè)置空白字符來美化輸出,但是你可能不知道第二個(gè)參數(shù)的作用,它為 {Array|Function} 類型,如果為 Array 則用于過濾 key,如果為 Function 則可以對(duì) value 做處理,如圖所示。
Symbol
ES6 中添加了一種新的數(shù)據(jù)類型,Symbol,它是一種原始數(shù)據(jù)類型(圖一),具備對(duì)象的特性(圖二),并可以指向同一個(gè)引用(圖三),能夠作為對(duì)象的 key 但不可枚舉(圖四),內(nèi)置的 Symbol 會(huì)影響程序的執(zhí)行(圖五),Symbol.iterator 是個(gè)舉足輕重的符號(hào),能夠讓元素具備迭代屬性(圖六),花樣很多。
附圖見:http://weibo.com/1812166904/DqMwR8O6z
偽數(shù)組添加 Symbol.iterator 的幾個(gè)辦法:鴨式辨型的 iterator 函數(shù)、yield 函數(shù)和直接使用 Array 的遍歷符號(hào)。
附圖見:http://weibo.com/1812166904/DqMBYebPw
Set/WeakSet
Set/WeakSet 這種數(shù)據(jù)結(jié)構(gòu),不能說沒用,但確實(shí)也沒啥大用,前者就是個(gè)不允許出現(xiàn)重復(fù)成員的數(shù)組,順便還帶了點(diǎn) ES6 的特性,后者雖說可以一定程度上防止內(nèi)存泄漏,但是也容易出錯(cuò),比如某個(gè)引用已經(jīng)被垃圾回收了,再去使用它可能就返回 null。它們都是 ES6 的配套產(chǎn)物。而 Map/WeakMap 倒是兩個(gè)非常不錯(cuò)的設(shè)計(jì),常規(guī)的 Object 結(jié)構(gòu)都為 String-Val 鍵值對(duì),而它擴(kuò)展為 AllType-Val,任意類型都可以作為它的 Key,無論是服務(wù)端編程還是客戶端編程,這個(gè)屬性都帶來了極大的便利性。
正則
理解正則零寬的含義:正則中所謂的零寬斷言,類似于錨點(diǎn)字符,它們匹配指定的位置而不會(huì)匹配內(nèi)容,如 ^ 匹配開頭,$ 匹配結(jié)尾,\b 匹配單詞邊界;(?=p) 匹配「接下來的字符與 p 匹配」的位置,(?!p) 匹配「接下來的字符不與 p 匹配」的位置。\b 字符匹配單詞邊界,實(shí)際上就是匹配 \w 與 \W 之間的位置(\w 匹配 [a-zA-Z0-9])。很少會(huì)有人用到 \B,它匹配的是非單詞邊界位置,簡單理解就是 \w & \w 之間位置或者 \W & \W 之間位置。
持續(xù)學(xué)習(xí)和分享…
內(nèi)容都是片段化的分享,比較多,也比較雜,就沒有全部列舉出來,感興趣的同學(xué)可以 follow 我的 微博,我的想法和筆記都會(huì)在上面同步。
感受
在這之前犀牛書已經(jīng)翻閱了差不多六七遍,很多內(nèi)容都已經(jīng)深深地刻在了腦海里,但時(shí)間久了也會(huì)忘記些,時(shí)而鞏固復(fù)習(xí)下,畢竟是前端最基礎(chǔ)部分。
帶著問題去看書,收獲是完全不一樣的。犀牛書不難啃,難的是你對(duì)這些知識(shí)點(diǎn)的理解深度。