在javascript無處不在的今天,我們每天都能很容易地學習到新的東西。一旦你掌握了這門語言的基本知識,就可以隨處找到蘊含著豐富知識的程式碼。Bookmarklets是一個完美的多種功能組合起來的工具,每當我發現一個有用的功能,我都會開心地去學習其原始碼,從中探索出怎麼實現這樣強大的功能。另外,如Google Analytics Code,或是Facebook Likebox的一些呼叫外部服務的小程式碼段,能教給我們的比一些書還多。
今天我想逐條跟大家分析Addy Osmani 幾天前分享的一個單行程式碼 。這行程式碼可以幫你debug你的各層CSS。為了方便觀看,我將其分為三行顯示:
[].forEach.call($ $(“”),function(a){
a.style.outline=”1px solid #”+(~~(Math.random()(1<<24))).toString(16)
})
將它放在你的瀏覽器控制臺中執行,頁面中各層的HTML就會被不同的顏色標記出來。很酷對不對?基本上,這行程式碼獲取了頁面中所有元素,然後給它們加上1px,顏色隨機的邊框。雖然它的原理很簡單,但是想要自己寫出這樣的程式碼,你得熟練掌握Web開發的方方面面。下麵讓我們一起學習它們。
選取一個頁面上所有的元素
首先需要做的是選取所有的元素,Addy用了只能在瀏覽器控制臺中使用的$ $。你可以在自己的瀏覽器的javascript控制臺中輸入$ $(‘a’),然後你會得到一個含有當前頁面所有錨元素的串列。
$ $函式是現代瀏覽器命令列的API的一部分,它等同於使用document.querySelectorAll 方法。你可以將一個CSS選擇器作為引數傳入document.querySelectorAll去選取當前頁面的元素。所以如果你想在瀏覽器的控制檯以外使用那個單行程式碼,你可以用document.querySelectorAll(‘*’)來替代$ $(‘*’)。點選這個stackoverflow問答可以進一步瞭解$ $。
非常好!對於我來說,能從這行程式碼中學習到$ $函式就已經很滿足了。然而在一個頁面上選取所有元素的方法還有很多。如果你有看過gist上面的評論,你會發現有人在討論這個話題。其中一個人就是Mathias Bynens(那裡有很多大神!),他告訴我們可以用document.all去實現這個功能,並且能在所有的瀏覽器上執行。
遍歷元素
於是我們得到了儲存所有的元素的NodeList,然後想給它們一個一個加上彩色的邊框。不過等等,我們的程式碼裡到底是怎麼寫的呢?
[].forEach.call( $ $(‘*’), function( element ) { /* And the modification code here */ });
[].forEach.call( $ $(‘*’), function( element ) { /* 對元素作出改變的程式碼 */ });
NodeList看起來像Array,你可以使用方括號去訪問它的節點,還可以訪問length屬性去瞭解它包含了多少元素,但是它並沒有實現Array的所有介面,因此$ $(‘*’).forEach會失效。在Javascript裡,有很多看起來像但不是陣列的物件,例如函式裡的arguments變數。我們有一個很好用的方法去處理這些物件:透過call和apply去實現像NodeList一樣的非array物件有機會去呼叫array的函式。我幾個月前談過這些函式,它們呼叫另外一個函式,並將第一個引數作為該函式裡面的this物件。
function say(name) {
console.log( this + ‘ ‘ + name );
}
say.call( ‘hola’, ‘Mike’ ); // Prints out ‘hola Mike’ in the console
say.call( ‘hola’, ‘Mike’ ); // 在console中輸出’hola Mike’
// Also you can use it on the arguments object
//你也可以在arguments物件上使用它
function example( arg1, arg2, arg3 ) {
return Array.prototype.slice.call(arguments, 1); // Returns [arg2, arg3]
}
單行程式碼用[].forEach.call來替代Array.prototype.forEach.call。透過Array物件[]去呼叫Array的函式這樣的方式(哈,又是一個給力的小技巧),節省了一些位元組。這相當於在$ $(‘*’).forEach中,把$ $(‘*’)當成一個Array來使用。
如果你再去看看評論區,你會發現有些人為了讓程式碼更短些而使用for(i=0;A=$ $(‘*’);)。這確實可行,但是它會洩漏全域性變數,所以如果你想要在控制檯以外的地方使用這種方法,你最好在一個乾凈的環境中使用。
for(var i=0,B=document.querySelectorAll(‘*’);A=B[i++];){ /* your code here */ }
如果你在瀏覽器的控制臺中使用就無所謂了,從你在裡面定義它們開始,i和A變數將會一直在裡面。
給元素上色
為了使這些元素都有漂亮的邊框,程式碼使用了CSS的outline屬性。如果你還不知道的話,顯示的邊框是在CSS區塊模型外的,它並不對元素本身在佈局中的大小和位置產生任何影響,因此該屬性非常適合用來實現我們的需求。 它的語法就像border的一樣,所以理解下麵的部分應該不難:
a.style.outline=”1px solid #” + color
有意思的是這裡定義顏色的方式:
~~(Math.random()*(1<<24))).toString(16)
被嚇到了嗎?當然,我不是一個位操作的專家,因此這是我最喜歡的部分,因為我從中學到了很多新東西。
我們想得到的是十六進製表示的顏色,如白色 FFFFFF, 或是藍色0000FF,亦或是…誰知道呢…37f9ac吧。像我一樣的普通人都習慣了使用十進位制數字,而我們心愛的程式碼對十六進位制非常地瞭解。
首先我們可以從中學到用toString方法把十進位制整數轉換成十六進位制整數。該方法以接收的引數作為其進位制基數,將一個數轉換為一個字串表示的數。如果沒有傳入引數,將會預設進行十進位制轉換,但其實你可以使用其他基數。
(30).toString(); // “30”
(30).toString(10); // “30”
(30).toString(16); // “1e” Hexadecimal
(30).toString(2); // “11110” Binary
(30).toString(36); // “u” 36 is the maximum base allowed
(30).toString(); // “30”
(30).toString(10); // “30”
(30).toString(16); // “1e” 十六進位制
(30).toString(2); // “11110” 二進位制
(30).toString(36); // “u” 36 是所允許的最大基數
反過來,你可以使用parseInt方法的第二個引數將十六進位制數的字串形式轉換為十進位制數。
parseInt(“30”); // “30”
parseInt(“30”, 10); // “30”
parseInt(“1e”, 16); // “30”
parseInt(“11110”, 2); // “30”
parseInt(“u”, 36); // “30”
我們需要一個介於0和十六進位制數 ffffff之間的隨機數,那麼就是parseInt(“ffffff”, 16) == 16777215。16777215正好是2^24 – 1。
你喜歡二進位制算術嗎?不喜歡的話,你只需要知道1<<24 == 16777216就好了(建議在控制檯裡試試)。
如果喜歡二進位制算術,你得知道每次你在1的右側加一個0,就相當於做了一次2^n操作,其中n為你加0的次數。
1 // 1 == 2^0
100 // 4 == 2^2
10000 // 16 == 2^4
1000000000000000000000000 // 16777216 == 2^24
左移運算x<
這還沒完,因為Math.random傳回的值是浮點數,而我們只需要整數部分。我們這裡使用了波浪符號(~)去實現。波浪符號用於對一個變數按位取反。如果你不明白我在說什麼,這裡有個很好的Javascript波浪符號講解。
但是這些程式碼重點不在於按位取反,而在於利用位運運算元會丟棄浮點數中的小數部分的特性,因此連續兩次按位取反是一個替代parseInt的便捷方法:
var a = 12.34, // ~~a = 12
b = -1231.8754, // ~~b = -1231
c = 3213.000001; // ~~c = 3213
~~a == parseInt(a, 10); // true
~~b == parseInt(b, 10); // true
~~c == parseInt(c, 10); // true
再提醒大家一遍,如果你去gist中看看評論區,你會發現大家在用更簡短的程式碼去獲取parseInt的結果。使用OR位運運算元你可以去掉我們隨機數中的小數部分。
~~a == 0|a == parseInt(a, 10)
~~b == 0|b == parseInt(b, 10)
~~c == 0|c == parseInt(c, 10)
Or運運算元的優先順序在最後,所以使用時可以省掉括號。這裡是Javascript運運算元的優先順序介紹,感興趣的話可以看看。
終於我們有了介於0和16777216之間的隨機數,即我們的隨機顏色。為了使用它,我們現在只需要用toString(16)將其轉換成字串形式的十六進位制數即可。
一些感想
當一名程式員並不容易。我們瘋狂地寫程式碼,有時卻沒有意識到我們需要多少知識去做我們做的事情。而消化掉所有我們在工作中用到的概念,需要很長的時間。
我想突出強調的是我們工作的複雜度,因為我知道程式員通常都會被低估(尤其在我的國家,西班牙)。在大部分公司裡我們扮演著重要角色,並且我們的工作非常有價值——能經常這樣說,是一件很好的事情。
如果你第一眼看到這單行程式碼,你就能理解它,你可以為你感到驕傲。
如果不能,但是你還是把文章看到這裡,不要擔心,你很快就有能力寫出這樣的程式碼,因為你是一個學習者!
如果你看到文章的第二行就在想太長不看,但你還是看到這裡了,你真是個奇葩,但我仍歡迎你在評論區寫下你的意見:)
原文出處:arqex.com
譯文出處:伯樂線上 – 鴨梨山大
連結:http://web.jobbole.com/82204/