奇妙的一行JS程式碼之完整解析

Harry Xie
6 min readOct 25, 2019

--

有點久沒在 Medium 寫文章了,上一次在這個平台寫文竟然是五個月前了!!但自從完成今年的IT鐵人賽挑戰之後,就似乎找回了寫文章的習慣,之前都是做成自己看得懂的筆記,現在想挑一些想跟大家分享的內容寫成文章,因此今後會不定期在此平台繼續發文,還請大家多多關注給我鼓掌,感恩。

這段程式是我在網路上無意間看到的,如果你把這段程式碼貼到開發人員工具的 console 會看到像下圖一樣出現一堆框線XDD


[].forEach.call($$("*"),function(a){a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)})

那我們就來看懂這段程式到底在做啥,不過簡單的來說就是抓取頁面所有元素並且給予它們隨機顏色的邊框。

我們先看到 [].forEach.call($$("*"), function(a){…}) 的部分:

1. $$('*') 意義等同於 document.querySelectorAll('*'),可以參考以下連結:
https://developers.google.com/web/tools/chrome-devtools/console/utilities#queryselectorall

2. [] 等同於 Array.prototype。為陣列 Array 的原型,它定義了陣列的一系列方法,從附圖可以知道 Array.prototype 包括了許多我們在操作陣列會用到的方法。

順便一提,這邊要有原型和繼承的概念才比較了解在做啥

3. 了解以上兩點後,原本的程式就可以看做:
Array.prototype.forEach.call(document.querySelectorAll('*'), function(a){…})

那麼為何不使用 document.querySelectorAll('*').forEach(function() {});
去選擇所有元素呢,這樣不是比較淺顯易懂嗎?那是因為 document.querySelectorAll() 會回傳一組 NodeList,而 NodeList 在比較舊的瀏覽器可能不支援搭配 forEach 這樣的寫法(目前比較新的瀏覽器已經可以這樣寫了),因此用陣列搭配 forEachcall 來選取所有頁面元素。

補充:
關於 NodeList: 它包括了DOM元素節點,文字和屬性,以<div id="title">Hello world</div> 為例,DOM元素節點就是<div>,文字為Hello world,屬性為id="title"
mozilla 上 NodeList 的介紹:
https://developer.mozilla.org/en-US/docs/Web/API/NodeList

forEach() + call() 的使用參考: https://stackoverflow.com/questions/16053357/what-does-foreach-call-do-in-javascript

Array.prototype.forEach.call(document.querySelectorAll('*'), function(a){...})// 等同於:
const arr = document.querySelectorAll('*');
arr.forEach(function(a){...});

接著我們來看 function 裡面的這段內容:
function(a){a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)}

1. 在此就是將剛剛透過 document.querySelectorAll('*') 取到的 NodeList 一個個傳入這個函式,並為它們加上寬度 1px 的框線,#+(~~(Math.random()*(1<<24))).toString(16) 產生的就是16進位的色碼。

2. 在 (1<<24)的部分,就是將1變成1000…總共24位數,而且為二進位制的型態,轉為十進位此數字將為16,777,216
可以參考以下連結:
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%E4%BD%8D%E5%85%83%E4%BD%8D%E7%A7%BB%E9%81%8B%E7%AE%97%E5%AD%90

備註: 這邊要有進位制概念才看得懂在做啥

3. 而 ~~ 這個符號,效用幾乎相等於 Math.floor() (在正整數的情況下兩者結果都會相等,負數則不會)~ 它就是位元運算子的其中一個,而 ~~ 就是做兩次 NOT 運算
~(4.5) // -5
~(-4.5) // 3
~~(4.5) // 4
~~(-4.5) // -4


參考資料:
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#(%E4%BD%8D%E5%85%83_NOT)

4. 最後是 toString(),這相當好理解就是將前面的一串東西轉為字串,()裡面的參數則是代表數字的進位制。

因此 (~~(Math.random()*(1<<24))).toString(16) 這段我們可以理解就是取 0~16,777,216 的亂數並去掉小數點,再轉為16進位的數字再變成字串,最後得到16進位的隨機色碼,例如 #FFFFFF 白色,也因此我們才會在輸入整段程式碼的網頁看到一堆五顏六色的邊框XDD。

最後的小心得就是短短的一段程式碼裡面竟然包含了不少觀念,自己查到是 Google 的工程師 Addy Osmani 寫的,也附上這段程式來源的 Github Gist 給大家看看:
https://gist.github.com/addyosmani/fd3999ea7fce242756b1
有機會也可以逛逛這位大神的 Github,感受一下所謂的大神是什麼樣子哈哈哈。

--

--

Harry Xie

專注於網頁技術的鑽研,認為「有熱情持續自我學習、提升技能」、「重視團隊流暢溝通與開發前的規劃」、「擁有獨立解決問題的能力」是成為優秀工程師的重要能力。更多關於我 : https://linktr.ee/harry.xie