記得還在懵懂學習JavaScript基礎之時,坊間便有傳言“with語句是低效率語句,若非必要,請不要使用該語句”,同時, ECMAScript 5 的strict mode下是禁止使用with語句的,所以一直以來我對with語句一直沒啥好感。
今天在知乎有個話題大概說的是“你覺得什麼東西相當有B格”之類的,然後就有人貼了這段代碼:
with(document)with(body)with(insertBefore(createElement("script"), firstChild))setAttribute("exparams","category=&userid=68497352&aplus&yunid=", id = "tb-beacon-aplus", src = (location > "https" ? "//s": "//a") + ".tbcdn.cn/s/aplus_v2.js")
代碼拆解:
with(document)with(body)with(insertBefore(createElement("script"), firstChild))setAttribute("exparams","category=&userid=68497352&aplus&yunid=",id = "tb-beacon-aplus",src = (location > "https" ? "//s": "//a") + ".tbcdn.cn/s/aplus_v2.js")
再拆開
var script = document.createElement("script");document.body.insertBefore(script, document.body.firstChild);script.setAttribute("exparams","category=&userid=68497352&aplus&yunid=",script.id = "tb-beacon-aplus",script.src = (location > "https" ? "//s": "//a") + ".tbcdn.cn/s/aplus_v2.js");
因為在 JavaScript 裡,可以給函數多傳一些無用參數的。
因此,最後這句話完全可以理解為:
script.id = "tb-beacon-aplus";
script.src = (location > "https" ? "//s": "//a") + ".tbcdn.cn/s/aplus_v2.js";
script.setAttribute("exparams","category=&userid=68497352&aplus&yunid=");
如果賦值的不是標準屬性,就不會寫出到標籤的 attribute 裡了,所以分開賦值能讓載入的外部指令碼讀取到這裡的附加參數。
據說是淘寶首頁的,好奇心使然,果斷跑去淘寶看了下,有圖有真相哪:
淘寶這種大項目一般是十分講究效率的,居然會使用傳說中的低效率代碼?
我們試著用正常代碼來達到上面的功能:
var s = document.createElement("script");s.setAttribute("exparams","category=&userid=68497352&aplus&yunid=");s.setAttribute("src",(location>"https"?"//s":"//a")+".tbcdn.cn/s/aplus_v2.js");s.id="tb-beacon-aplus";document.body.insertBefore(s,document.body.firstChild)
這是我能寫出的最簡單的代碼了,當然,您可以嘗試像淘寶那代碼一樣setAttribute,不過結果會讓你很受傷!!!經過我的測試,他那樣帶3個以上的參數設定節點屬性僅在with語句下有效,而且第三個及以後參數所設定的屬性只能是HTML標準屬性。原因我也不知道,有大牛願意指教嗎?
代碼壓縮後,淘寶代碼224位元組,我寫的代碼264位元組。
我得出的結論是:大的網站惜位元組如金,特別是像淘寶這種每天流量巨大的網站,為了節省流量(別看只有幾個位元組,乘以個大的訪問量後結果還是挺驚人的)而稍微犧牲下使用者代碼啟動並執行效率是完全值得的。況且,在瀏覽器代碼執行效率日新月異的今天,with語句效率真有那麼低嗎?
秉承一顆探索的心(此刻內心略為激動。。),做了如下代碼測試,
html代碼:
<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <title>test</title></head><body> <div id="one" data="test data"></div></body></html>
with語句擷取div#data值
var now = +new Date;for(var i=0;i<1000000;i++){ with(document)with(getElementById("one"))getAttribute("data")}console.log(new Date-now)
一般代碼擷取div#one的data值
var now = +new Date;for(var i=0;i<1000000;i++){ document.getElementById("one").getAttribute("data")}console.log(new Date-now)
擷取屬性值的代碼均迴圈運行100W次,輸出已耗用時間,測試瀏覽器為Chrome 35與IE 11:
申明:誰特麼說我是IE黑,我和誰急!!!
| Chrome 35 |
數值單位:ms |
|
第1次 |
第2次 |
第3次 |
第4次 |
第5次 |
第6次 |
第7次 |
第8次 |
第9次 |
第10次 |
平均值 |
10W次差值 |
單次差值 |
| 一般代碼 |
1362 |
1358 |
1379 |
1377 |
1372 |
1411 |
1371 |
1341 |
1356 |
1339 |
1366.6 |
888.7 |
0.89μs |
| IE 11 |
單位:ms |
|
第1次 |
第2次 |
第3次 |
第4次 |
第5次 |
第6次 |
第7次 |
第8次 |
第9次 |
第10次 |
平均值 |
10W次差值 |
單次差值 |
| 正常情況 |
2352 |
2332 |
2321 |
2347 |
2342 |
2339 |
2365 |
2373 |
2353 |
2343 |
2346.7 |
861.7 |
0.86μs |
由於其它軟體啟動並執行影響及兩種代碼啟動並執行先後順序,結果可能不是十分嚴謹,但個人認為還是不會影響我們得出最終的結論:with語句在不是嵌套十分複雜的情況下,相比於一般代碼對執行效率的影響其實微乎其微。
我想ECMAScript 5 的strict mode下禁用with語句的主要原因應該是with語句讓個對象與方法與屬性的關係變得更模糊, 不利於JavaScript向物件導向編程靠攏吧。
雲棲社區小編補充:雖然有B格但不論是易於閱讀還是考慮效能的目的,很多網站還是使用普通的方式載入。