从一行代码里面学点JavaScript
发布在每天学点javascript2015年8月7日view:15553BrettBat
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

enter image description here

从一行代码里面学点JavaScript

现如今,JavaScript无处不在,因此关于JavaScript的新知识也是层出不穷。JavaScript的特点在于,要学习它的语法入门简简单,但是要精通使用它的方式却是一件不容易的事。

来看看下面的这段代码,它来自于谷歌“名猿”Addy Osmani在几天前贴出的一段代码,它的作用是用来调试你的CSS层。全部代码只有三行,但是你绝对可以把它放在一行里面完成:

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

现在,在你的Chrome浏览器的控制台中输入这段代码,你会发现不同HTML层都被使用不同的颜色添加了一个高亮的边框。是不是非常酷?但是,简单来说,这段代码只是首先获取了所有的页面元素,然后使用一个不同的颜色为它们添加了一个1ps的边框。想法很简单,但是真要实现起来却不是那么容易的一件事。在下面的内容中,我们将一起一步一步学习如何理解上面的这段代码。

选择页面中所有的元素

我们需要做的第一件事情是获取页面中所有的元素,在上面的代码中,Addy使用了一个Chrome浏览器中特有的函数$$。你可以在你的Chrome浏览器控制台中输入$$('a'),然后你就能得到一个当前页面中所有锚元素的列表。

$$函数是许多现代浏览器命令行API中的一个部分,它等价于document.querySelectorAll,你可以将一个CSS选择器作为这个函数的参数,然后你就能够获得当前页面中所有匹配这个CSS选择器的元素列表。如果你在浏览器控制台以外的地方,你可以使用document.querySelectorAll('*')来代替$$('*')。更多关于$$函数的详细内容可以查看Chrome开发者工具的文档。

当然,除了使用$$函数之外,我们还有一种更简单的方法,document.all,虽然这并不是一种很规范的使用方法,但是它几乎在每一个浏览器中都能运行成功。

迭代所有的元素

经过第一步,我们已经获得了页面内所有的元素,现在我们想做的事情是遍历每一个元素,然后为它们添加一个彩色边边框。但是上面的代码究竟是怎么一回事呢?

[].forEach.call( $$('*'), function( element ) { /* 在这里修改颜色 */ });

首先,我们通过选择器获得的列表是一个NodeLists对象,它和JavaScript中的数组有点像,你可以使用方括号来获取其中的节点,你也可以检查它其中包含多少个元素,但是它并没有实现数组包含的所有方法,因此我们并不能使用$$('*').forEach()来进行迭代。在JavaScript中,有好几个类似于数组但是并不是数组的对象,除了前面的NodeLists,还有函数的参数集合arguments,在这里我们可以使用callapply函数将函数的方法运用到这些对象上。例如下面的例子:

function say(name) {
 console.log( this + ' ' + name );
}

say.call( 'hola', 'Mike' ); // 打印 'hola Mike'

// 你也可以将这种方法有用在arguments对象上 function example( arg1, arg2, arg3 ) { return Array.prototype.slice.call(arguments, 1); // Returns [arg2, arg3] }

在Addy的代码中,使用了[].forEach.call而不是Array.prototype.forEach.call,二者等价,但是前者可以节省几个字节。

为元素添加颜色

为了让元素都有一个漂亮的边框,我们在上面的代码中使用了CSS属性outlineoutline属性位于CSS盒模型之外,因此它并不影响元素的属性或者元素在布局中的位置,这对于我们来说非常有用。这个属性和修改border属性非常类似,因此下面的代码应该不会很难理解:

a.style.outline="1px solid #" + color

真正有趣的地方在于定义颜色部分:

~~(Math.random()*(1<<24))).toString(16)

天呐,上面的代码是什么意思?在JavaScript中,比特操作符并不是经常被使用,因此这里可能会让很多程序员感到很疑惑。

我们想达到的目的是活的一个十六进制格式的颜色例如白色对应的是FFFFFF,蓝色对应的是0000FF,或者随便一个颜色37f9ac。虽然我们人类喜欢十进制,但是我们的代码常常会需要十六进制的东西。

我们首先要学会如何使用toString函数将一个十进制的数组转换为一个十六进制整数。这个函数可以接受一个参数,如果参数缺省,默认为十进制,但是你完全可以使用别的数组:

(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"

因此,我们现在只需要一个位于0ffffff之间的十六进制数,由于:

parseInt("ffffff", 16) == 16777215

而这里的16777215实际上是2^24-1

如果你对二进制数学熟悉的话,你可能会知道1<<24 == 16777216

再进一步,你每在1后面添加一个0,你就相当于多做了一次2的乘方:

1 // 1 == 2^0
100 // 4 == 2^2
10000 // 16 == 2^4
1000000000000000000000000 // 16777216 == 2^24

因此,在这里我们可以知道Math.random()*(1<<24)表示一个位于016777216之间的数。

但是这里并没有结束,因为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

当然,我们还有一种更加简洁的方法,使用OR操作符:

~~a == 0|a == parseInt(a, 10)
~~b == 0|b == parseInt(b, 10)
~~c == 0|c == parseInt(c, 10)

最终,我们获得了一个位于016777216之间的随机整数,也就是我们想要的随机颜色。此时我们只需要使用toString(16)将它转化为十六进制数即可。

总结

现在,你已经完全理解了前面的这一行代码中的各个部分。作为一个程序员,我们应该在完成工作之后多问自己几遍为什么,还有没有更好更简洁的方法。当然,最应该做的事情当然是多阅读程序代码,也许你就能从某一行代码中学到很多新东西。

本文参考自Learning much javascript from one line of code,原文地址http://arqex.com/939/learning-much-javascript-one-line-code

评论
发表评论
2年前
赞了此文章!
3年前
赞了此文章!
3年前
赞了此文章!
3年前
赞了此文章!
4年前
赞了此文章!
4年前
赞了此文章!
4年前

位运算太神了!不过输出的颜色值有可能会出问题

4年前
赞了此文章!
4年前

@Kyrios 嗯,确实。接受批评指正:)

4年前

@张小俊128 你算的是11个元素全部出错的概率,“11个元素出错的概率” = 1 - “11个元素都不出错的概率” = 1 - (15/16)^11

4年前

@Kyrios 每个元素是发生错误都是独立事件,那么出错概率应该是十六分之一的十一次方吧。。。。。。

4年前

@张小俊128 概率就是十六分之一呀,学点数学吧老师。页面上有超过11个元素的话出错的概率就是50%了

4年前

@Kyrios 嗯,有一点问题,但是从实际使用来看,随机数出现小于0.01的情况还是比较小的:)

4年前

首先,实际上这段代码是有bug的

(~~(Math.random()*(1<<24))).toString(16)

如果Math.random返回数字小于十六分之一,比如0.01,那么这行表达式的结果是

(~~(0.01*(1<<24))).toString(16) = “28f5c”

结果只有5位,不是合法的颜色值。

可以用下面的式子获得定常的字符串:

Math.random().toString(16).substr(2,6)

然后,虽然query api返回的list是静态的,但线性访问依然有性能问题

4年前

mark 以后测试用!

4年前

确实不错。

4年前

学习啦!

4年前

没有那么多黑魔法的代码: (回复没法删除呀)

Array.prototype.forEach.call(document.querySelectorAll("*"),function(el){
  el.style.outline = "1px solid #" 
              + Math.floor(Math.random()* Math.pow(2,24)).toString(16)
})
4年前

没有那么多黑魔法的代码:

Array.prototype.forEach.call(document.querySelectorAll("*"), function(el){
  el.style.outline = "1px solid #" 
                  + Math.floor(Math.random()* Math.pow(2,24)).toString(16)
})
4年前

没有那么多黑魔法的代码

Array.prototype.forEach.call(document.querySelectorAll("*"), function(el){
  el.style.outline="1px solid #" + Math.floor(Math.random()* Math.pow(2,24)).toString(16)
})
4年前

很赞。实用,就是颜色值貌似不一定格式都对,虽然一般情况下都是正常。

4年前

位运算好赞!

4年前

(~~(Math.random()*(1<<24))).toString(16)并不能保证颜色值是有效的

4年前

css的颜色有效值好像是3或6位,(~~(Math.random()*(1<<24))).toString(16)并不能保证这点噻~~

4年前

(0|Math.random()*0xffffff).toString(16)

4年前

二进制运算那块写的很精彩!

WRITTEN BY
张小俊128
Intern in Baidu mobile search department。认真工作,努力钻研,期待未来更多可能。
TA的新浪微博
PUBLISHED IN
每天学点javascript

javascript进阶级教程,循序渐进掌握javascript

我的收藏