从谷歌一行代码能学到哪些姿势

网上很流行的一行代码,据说是谷歌工程师写的,它的作用是给页面所有元素增加一个随机颜色的外边框

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

运行效果如下图:

3

这个代码虽然只有一行,但是包含的知识点不少,网上有很多解析。我也说下自己的理解,然后最后推荐在实务中使用TreeWalker对象进行遍历。

我的理解其中主要包含如下4个知识点:

11. [].forEach.call 22. $$("*") 33. a.style.outline 44. (~~(Math.random()*(1<<24))).toString(16)

1 [].forEach.call

1.1 [].forEach

forEach是数组遍历的一个方法,接收一个函数参数用来处理每一个遍历的元素,常规的使用姿势是:

1let arr = [3, 5, 8]; 2arr.forEach((item) => { 3 console.log(item); 4}) 5// 控制台输出: 6// 3 7// 5 8// 8

那么下面的写法:

1[].forEach

只是为了得到 forEach 这个方法,这个方法是定义都在Array.prototype上的方法,[] 表示空数组,可以访问到数组原型对象上的方法。

得到 forEach 这个方法后,就可以通过 call 发起调用。

1.2 call

call函数用来调用一个函数,和普通调用不同,call调用可以修改函数内this指向。

常规调用函数的姿势:

1let object1 = { 2 id: 1, 3 printId() { 4 console.log(this.id) 5 } 6} 7object1.printId(); 8// 控制台输出: 9// 1

因为是正常调用,方法内的this指向object1对象,所以上例输出1。

使用call调用printId方法,并传入另外一个对象object2:

1let object2 = { 2 id: 2 3} 4object1.printId.call(object2); 5// 控制台输出: 6// 2

这里使用call调用object1.printId函数,传入了object2对象,那么printId函数内的this就是指向object2这个对象,所以结果输出2。

1.3 综合分析

综合来看:

1[].forEach.call( $$("*"), function(a){} )

这行代码的意思就是遍历如下对象:

1$$("*")

然后用如下方法处理每个元素:

1function(a){}

其中,a就是遍历的的每一个元素。

那么

1$$("*")

指什么呢?我们接着往后看。

2 $$("*")

这个写法用来获取页面所有元素,相当于

1document.querySelectorAll('*')

只是

1$$("*")

只能在浏览器开发控制台内使用,这个是浏览器开发控制台提供出来的预定义API,至于为什么,大家可以参考底部的参考文章。

3 a.style.outline

设置元素边框,估计很多人都知道,但是设置外边框就比较少人了解了,外边框的效果和边框类似,唯一不同的点是外边框盒子模型的算式,仅仅做装饰使用。

1<style type="text/css"> 2 #swiper { 3 width: 100px; 4 height: 100px; 5 outline: 10px solid; 6 } 7</style> 8 9<div id="swiper"></div>

运行效果:

1

div元素实际的宽高还是100 * 100,如果把outline改成border,那么div元素的实际宽高就是120 * 120,因为要加上border的宽度。

外边框设置的最大作用就是:

可以设置元素边框效果,但是不影响页面布局。

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

这个代码从结果是得到一个16进制的颜色值,但是为什么能得到呢?

16进制的颜色值:81f262

4.1 Math.random()

这个容易理解,就是随机 [0, 1) 的小数。

4.2 1<<24

这个表示1左移24位,二进制表示如下所示:

1 0000 0000 0000 0000 0000 0000  

十进制就是表示:

2^24

那么

Math.random() * (1<<24)

就会得到如下范围的一个随机浮点数:

[0, 2^24) 

4.3 两次按位取反

因为Math.random()得到是一个小数,所以两次按位取反就是为了过滤掉小数部分,最后得到整数。

所以

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

就会得到如下范围的一个随机整数:

[0, 2^24) 

4.4 转成字符串toString(16)

最后就是把上面得到的数字转成16进制,我们知道toString()是用来把相关的对象转成字符串的,它可以接收一个进制参数,转成不同的进制,默认是转成10进制。

1对象.toString(2); // 转成2进制 2对象.toString(8); // 转成8进制 3对象.toString(10); // 转成10进制 4对象.toString(16); // 转成16进制

上面的得到的随机整数用二进制表示就是:

10000 0000 0000 0000 0000 0000 231111 1111 1111 1111 1111 1111

那么2进制转成16进制,是不是就是每4位转一个?

最终是不是就得到一个6个长度的16进制数了?

这个字符串加上#是不是就是16进制的颜色值了?

形如:

1#ac83ce 2#b74384 3等等...

实务应用

虽然上面的代码简短,并且知识含量也很高,但是在实务中如果要遍历元素,我并不建议使用这样的方式。

主要原因是两个:

11. $$("*") 只在开发控制台可以用,正常项目代码中不能用。 22. 选中所有元素再遍历,性能低。

如果实务中要遍历元素,建议是用 TreeWalker。querySelectorAll是一次性获取所有元素然后遍历,TreeWalker是迭代器的方式,性能上 TreeWalker 更优,另外 TreeWalker 还支持各种过滤。

参考如下示例:

1// 实例化 TreeWalker 对象 2let walker = document.createTreeWalker( 3 document.documentElement, 4 NodeFilter.SHOW_ELEMENT 5); 6// 遍历 7let node = walker.nextNode(); 8while (node !== null) { 9 node.style.outline = "1px solid #" + (~~(Math.random() * (1 << 24))).toString(16); 10 node = walker.nextNode(); 11}

虽然代码更多,当时性能更好,并且支持各种过滤等,功能也更加强大。

如果大家有学到新姿势,麻烦帮忙点个赞,谢谢。欢迎大家留言讨论。

参考资料

JavaScript中的$$(*)代表什么和$选择器的由来:http://ourjs.com/detail/54ab768a5695544119000007

querySelectorAll vs NodeIterator vs TreeWalker:https://stackoverflow.com/questions/64551229/queryselectorall-vs-nodeiterator-vs-treewalker-fastest-pure-js-flat-dom-iterat

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

一篇文章带你了解JavaScript日期

日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用