再说这行代码之前,咱们先来预习一下知识.
我们都知道计算机操作系统分为32位或者64位.那么这个32位或64位指的是什么意思呢?其实,要想解释它并不难,其实这就是计算机处理数据的机制,32位表示计算机的CPU(*处理
器)一次性能够处理32位的数据,计算机数据都是按位来存储的,单位就是8位(bit)=1字节(b).然后依次向上推,1kb (千字节)= 1024b(字节),1mb(兆字节)=1024kb(千字节),1GB(千兆字节) =
1024mb(兆字节).
说了这么多,与今天讲的有什么关系吗?那当然是有关系啦.
我们先来看一串代码,如下图所示:
然后再在浏览器上运行查看效果,如下图所示:
是不是很神奇?就特么一行常用的符号就能够输出"sb"这两个字母,简直太牛叉了.接下来,我就来分析其中原理.
首先在分析之前,我们还需要知道一点知识,那就是既然计算机是按位处理数据的,那么浏览器处理js代码中的数据自然也不例外.所以js当中就有一个基本的概念,那就是位操作符.
什么是位操作符?简单点来说,就是在最基本的层次上,按内存中数值的位来操作数值.通过计算机的位操作原理,我们就能明白位操作符的含义.但是有一点需要区别,那就是位操作符不能
直接操作64位的数值,而是先将64位的数值转换成32位的数值来操作.
而转换的数值有且只有两个,那就是0和1.0表示的是正数,1表示的是负数.由0和1组成的一长串的如0000 0000 0000 0000 0000 0000 0000 0000 (4 X 8 = 32)的数字就是所谓的机器码.
机器码就是按照二进制格式来存储数据的.所谓二进制,也就是只有0和1两个数字来存储.
而前31位都表示的是整数,第32位则表示一个数值的符号.也就是+或-号,你可以把它理解成数学上的正负数的符号.
正数可以直接二进制存储,负数则需要二进制补码.也就是说咱们先取负数的绝对值,也就是将负数转换为正数来取二进制码,然后再取反,这里的取反意思就是因为二进制只有0和1两种
数字,所以不言而喻,取反就是0变成1,1变成0.
然后在加上一个1,当二进制最后一位是1时,加一就需要进位前面的一个0变成1,然后这个最后一位1就变成0.
我们通常都是十进制数,如10,那么如何转化为二进制数呢,通过除以2,然后取余数,最后把余数倒排.,要一直除到商等于0时才不用继续除下去,然后取它们的余数,颠倒顺序排列.
比如10/2 商5,余数是0,商5不等于0,继续除下去,得商2,取余数为1,商还不等于0,继续除下去,得商为1,余数是0,再除一次,得商0,余数为1,此时商也是0,也就不能再除,然后把得到的余数排
列就是0101,然后倒排就是1010,没有用到的位用0补上,或者不补,这里为了便于理解,我们不上前面的28个0 得0000 0000 0000 0000 0000 0000 0000 1010.所以得出10的二进制数就
是1010.那么-10的二进制数就应该是先将10的二进制数求出得1010,然后取反0变1,1变0,得0101.因为最后一位是1,按照上面的说法,然后加一个1,得0110.
说了这么多,与今天的代码有什么意义吗?那是当然.在了解了位操作符之后,我们还要了解一个知识,那就是js运算符的优先级,如下图:
首先需要耐心看完以上符号对应的含义,至少把上面那串代码用到的对应的符号的含义搞清楚.然后我们就可以根据运算符的优先级规则,把上面那一串代码给分解成多个子表达式.
重点解释一下~表示对先对数字求负,然后再减1.!返回一个布尔值.然后就是转换规则问题,对于一个非原始数据类型的,会先将其转换为原始数据类型,原始数据类型.实际上就是
ToPrimitive()这个方法来转换.它有两个参数,第一个参数表示数据类型,第二个参数是数值或者字符串.如果第一个参数是原始数据,那么就返回原始值,即本身值.否则就是返回object对
象.至于什么是原始数据类型,实际上就是基本数据类型(字符串,布尔值,数值...相信后面不用我多说了吧).而第二个参数可返回任何原始值,如果是对象也返回它本身.
所以,接下来,我们把以上的长串代码给拆分.
先来看最左边的(!(~+[])+{}),我们知道()表示一个函数的调用或者是一个表达式的分组,所以我再拆分得到(~+[]),好了先看[]这个符号,其实就是一个空数组,空数组会被转换为0,为什么呢?
首先它调用了对象底层valueOf()方法返回数组本身,实际上此时就得到的是一个空值,空值在遇到+这样的运算符的时候就会先调用Number()函数,当然实际上最底层还有一个
toNumber()函数转换成一个可转换的数值型数据,然后再通过Number()函数转换成数值0.随后就变成了~ + 0,然后就是先取负,变-0,再减1变成-1,然后就变成了!-1,就会返回一个false的
布尔值(可通过查看!逻辑非的转换返回相关知识得知这里为何会返回false).
接下来又是一个表达式(false + {}),这个比较关键,首先来看{}这是一个空对象,很明显就会调用ToPrimitive()这个方法,首先数据类型是对象,所以返回object,其次调用这个方法时,发现第
二个参数也还是对象,所以也就返回object,所以(false + {})也就成了false[object,object].
好接下来继续,由于第二个有点长,我们知道一对中括号包起来表示一个数组的下标.
那么[--[~+""][+[]]*[~+[]]+~~!+[]]这么一长串,我们就可以这样,先看[~ + ""],首先""空串其实跟null一样都被转换成了数值0,然后~+0则就是-1,--运算符就是先自减1再参与运算所以就是-1-1
变成了-2.
再看[+[]]实际上通过以上的规则不难得出就是[0]所以也就变成了[-2][0],这个表示啥意思,就是一个数组只有-2一个元素,而现在要取下标为0也就是第一个元素即-2,所以也就返回了-2,接
下来看[~+[]]不言而喻可以得出是[-1],[-2]*[-1]这很明显会当成-2*(-1)自然也就得出了2.
接下来看~~!+[]这里实际上都是从右往左计算的,因为运算符的优先级问题,通过上面的图可以看出,所以这里就是先转换!+[]返回true,然后再转换为1,然后~~1,根据~是先对数字求反再减
1的规律得到这里实际上就是1,然后这么一长串符号就变成了[2+1]即[3],然后就变成了
false[object object][3],通过这么艰难的分析就得出了s这个字母,然后再来看这部分({}+[])[[~!+[]]*~+[]],先运算表达式里面,很明显对象+一个对象会得出什么,自然是[object,object],这里的
+很明显被当成了拼接的作用,不要忘了它不只是做加法运算,还做拼接符号呢.
然后后面依次得出结论[-2 * -1]得出[2],然后就是[object,object][2],取出b,所以sb这两个字由此而来.
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]一行js代码的原理分析的更多相关文章
-
vue项目 一行js代码搞定点击图片放大缩小
一行js代码搞定xue项目需要点击图片放大缩小,其实主要用的是用到了vue:class的动态切换,内容比较简单.一开始我把维护的需求想得太复杂了,和测试小姐姐聊了一下才反应过来. 两个月不到跟了四个项 ...
-
如何编写高质量的js代码--底层原理
转自: 如何编写高质量的 JS 函数(1) -- 敲山震虎篇 本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm ...
-
js封装的三级联动菜单(使用时只需要一行js代码)
前言 在实际的项目开发中,我们经常需要三级联动,比如省市区的选择,商品的三级分类的选择等等. 而网上却找不到一个代码完整.功能强大.使用简单的三级联动菜单,大都只是简单的讲了一下实现思路. 下面就给大 ...
-
一行js代码识别Selenium+Webdriver及其应对方案
有不少朋友在开发爬虫的过程中喜欢使用Selenium + Chromedriver,以为这样就能做到不被网站的反爬虫机制发现. 先不说淘宝这种基于用户行为的反爬虫策略,仅仅是一个普通的小网站,使用一行 ...
-
AngularJS:一行JS代码实现控件验证效果
如上图所示,我们需要实现如下这些验证功能: 控件都是必输控件 都需要控制最大长度 第一次打开页面,控件不能显示为错误状态 输入内容再清空后,必输控件需要显示为错误状态 只有所有输入合法后,发布按钮才能 ...
-
一行JS代码,解决DedeCMS TAG标签错误输入符号问题
在维护内容的时候, Tag标签输入经常要来回切换输入法, 只能通过','号分隔. 中文用户, 输入法出来的经常是全角的, 经常弄错, 增加了检查的工作量, 现在只要一句JS代码, 就自动替换所有 ...
-
一行js代码实现时间戳转时间格式
javascript时间戳转换,支持自定义格式,可以显示年,月,周,日,时,分,秒多种形式的日期和时间. 推荐一个JavaScript常用函数库 jutils jutils - JavaScript常 ...
-
将一行很长的js代码格式化输出方便查看
之前的一行js代码,有2万多字符,打开这个网址,粘贴到左边空白框,点下面格式化: 参考下面文章: 数千行的js代码变成了一行,如何复原,该换行的换行,该对齐的对齐_开发工具_小邯韩的博客-CSDN博客 ...
-
拼团商品列表页 分析 js代码行位置对执行的影响和window.onload的原理 setTimeout传参
w TypeError : Cannot set property 'innerHTML' of nullTypeError : Cannot set property 'value' of null ...
随机推荐
-
c#上利用NPlot实现动态曲线图需要的dll文件
这儿暂时只提供我之间根据网上的方法编译出来的dll文件,大家如果需要直接在vs项目上导入就行了,然后在工具箱里就会自动添加一项,大家添加上去就知道了. 下载地址:http://pan.baidu.co ...
-
Sublime Text 3 常用插件以及安装方法
安装Sublime Text 3插件的方法: 一.直接安装 安装Sublime text 2插件很方便,可以直接下载安装包解压缩到Packages目录(菜单->preferences->p ...
-
Code First Migrations更新数据库结构的具体步骤
一.打开程序包管理器控制台 当你的实体模型与数据库架构不一致时,引发以下错误:The model backingthe 'SchoolContext' context has changed sinc ...
-
Linux下通过shell脚本创建账户
当我们在linux平台上开发一些项目时,或者有一些项目是需要部署到linux系统上时,有时候会涉及到linux上的特定的账户,例如有一些项目需要运行在某些特定的账户下,或者有时候需要在全新的环境上搭建 ...
-
韦东山yy公开课笔记(2)--汇编,段,栈,重定位/链接地址,位置无关吗
1. 要不要学习汇编 可以只懂一点,工作中基本不用,一旦用就是出了大问题 ldr : load 读内存 ldr r0, [r1] : r1里存放的是地址值, 去这个地址读取4字节的内容,存入r0 s ...
-
Splash界面布局与代码实现(一)
xml界面布局代码: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns ...
-
logrotate 清理tomcat日志
rsyslog tomcat 服务器: 192.168.32.215 input(type="imfile" File="/usr/local/apache-tomcat ...
-
mysql存储过程 详细注释
原文:https://my.oschina.net/u/3582142/blog/1581929
-
高阶函数(Higher-order function)
变量可以指向函数 以Python内置的求绝对值的函数abs()为例,调用该函数用以下代码: >>> abs(-15) 15 但是,如果只写abs呢? >>> abs ...
-
WIFI 802.11 a/b/g/n/ac
802.11 a/b/g/n/ac FHSS: Frequency-hopping spread spectrum (FHSS) is a method of transmitting radio s ...