JS探秘——那些你理解存在偏差的问题
Javascript的连续赋值运算
var a = {n:1};
a.x = a = {n:2};
alert(a.x); // --> undefined 看 jQuery 源码 时发现的这种写法。 以上第二句 a.x = a = {n:2} 是一个连续赋值表达式。 这个连续赋值表达式在引擎内部究竟发生了什么?是如何解释的?
加一个变量 b,指向 a。
var a = {n:1};
var b = a; // 持有a,以回查
a.x = a = {n:2};
alert(a.x);// --> undefined
alert(b.x);// --> [object Object] 实际执行过程:从右到左,a 先被赋值为 {n:2},随后 a.x 被赋值 {n:2}。第一步 a = {n:2} 的 a 指向的是新的对象 {n:2}, 第二步
a.x = {n:2} 中的 a 是 {a:1}。如下所示
a.x = a = {n:2}
│ │
{n:1}{n:2}为什么 ++[[]][+[]]+[+[]] = 10?
++[[]][+[]]+[+[]],它相等于:
++[[]][+[]]
+
[+[]]
在 JavaScript 里,+ 会把一些字符转化成数字,如 +"" === 0、+[] === 0,因此,我们可以简化一下(++ 比 + 有更高的优先级):
++[[]][0]
+
[0]
因为 [[]][0] 的意思是:获取 [[]] 的第一个元素,这就得出了下面的结果:
- [[]][0] 返回内部数组 ([])。根据语言规范,我们说 [[]][0] === [] 是不正确的,但让我们把这个内部数组称作 A,以避免错误的写法。
- ++[[]][0] == A + 1, 因为 ++ 的意思是”加一”。
- ++[[]][0] === +(A + 1);换句话说,你得到的永远是个数值( +1 并不一定得到的是个数值,但 ++ 一定是)。
我们可以把这一堆代码简化的更清晰。让我们把 A 换回成 [] :
+([] + 1)
+
[0]
在 JavaScript 里,这也是正确的:[] + 1 === "1",因为 [] == "" (这相当于一个空的数组的内部元素连接),于是:
+([] + 1) === +("” + 1),并且
+("” + 1) === +("1"),并且
+("1") === 1 让我们再次简化一下:
1
+
[0]
同样,在 Javascript 里,这是正确的:[0] == "0",因为这是相当于一个有一个元素的数组的内部元素的连接。 各元素会使用,分隔。 当只有一个元素时,你可以推论出这个过程的结果就是它自身的第一个元素。
所以,最终我们得到 (数字 + 字符串 = 字符串):
1
+
"0"
=== "10" // 耶!
[1,2] + [3,4]=="1,23,4"
JavaScript 的 + 运算符有两个目的:
- 将两个数相加
- 将两个字符串连接
规范并没有定义 + 运算符在数组上的行为,所以javascript 首先 把数组转换成字符串,然后在字符串上进行 + 运算。
如果想连接两个数组,可以使用数组的 concat 方法:
[1, 2].concat([3, 4]) // [1, 2, 3, 4]
{}+{}等于多少?
结果为:NaN
这个问题的原因是,JavaScript 把第一个 {} 解释成了一个空的代码块(code block)并忽略了它。 NaN 其实是表达式 +{} 计算的结果 (+
加号以及第二个 {})。 你在这里看到的 + 加号并不是二元运算符「加法」,而是一个一元运算符,作用是将它后面的操作数转换成数字,和 Number() 函数完全一样。例如:
> +"3.65"
3.65
以下的表达式是它的等价形式:
+{}
Number({})
Number({}.toString()) // {}.valueOf() isn’t primitive
Number("[object Object]")
NaN为什么第一个 {} 会被解析成代码块(code block)呢? 因为整个输入被解析成了一个语句:如果左大括号出现在一条语句的开头,则这个左大括号会被解析成一个代码块的开始。
所以,你也可以通过强制把输入解析成一个表达式来修复这样的计算结果: (译注:我们期待它是个表达式,结果却被解析成了语句)
> ({} + {})
'[object Object][object Object]'{} === {},结果为false
每个对象都有自己唯一的标识符,因此通过字面量或构造函数创建的对象和任何其他对象都不相等,我们可以通过 === 进行比较。
{} === {} //false对象是通过引用来比较的,只有两个对象有相同的标识,才认为这个对象是相等的。
var obj = {};
obj === obj //true原始值没有内部标识,原始值是按值比较的: 比较两个原始值的依据是他们的内容,如果两个原始值的内容相同,这认为这两个原始值相同。
"abc" === "abc" //true
原始值和它们的包装类型
原始值类型 boolean, number 以及 string 都有自己对应的包装类型 Boolean, Number 和 String。 包装类型的实例都是对象值,两种类型之间的转换也很简单:
- 转换为包装类型:new String("abc")
- 转换为原始类型:new String("abc").valueOf()
原始值类型以及它们相应的包装器类型有很多不同点,例如:
typeof "abc" //'string'
typeof new String("abc") //'object'
"abc" instanceof String //false
new String("abc") instanceof String //true
"abc" === new String("abc") //false
包装类型的实例是一个对象,因此和 JavaScript 和对象一样,包装类型也无法进行值的比较(只能比较引用)。
var a = new String("abc");
var b = new String("abc");
a == b //false(虽然 a 和 b 有相同的内容,但是依然返回 false)
a == a //true
5.toFixed(3),报错SyntaxError: Unexpected token ILLEGAL
原始值没有自己的方法
包装对象类型很少被直接使用,但它们的原型对象定义了许多其对应的原始值也可以调用的方法。 例如,String.prototype 是包装类型 String 的原型对象。
它的所有方法都可以使用在字符串原始值上。 包装类型的方法 String.prototype.indexOf 在字符串原始值上也有,它们并不是两个拥有相同名称的方法,而的的确确就是同一个方法:
"abc".charAt === String.prototype.charAt
true
在数字的包装类型 Number 的原型对象有 toFixed 方法,即 Number.prototype.toFixed,但是当我们写如下代码时却发生错误:
5.toFixed(3)
SyntaxError: Unexpected token ILLEGAL
此错误是解析错误(SyntaxError),5 后面跟着一个点号(.),这个点被当作了小数点,而小数点后面应该是一个数,以下代码可以正常运行:
(5).toFixed(3)
"5.000"
5..toFixed(3)
"5.000"
JS探秘——那些你理解存在偏差的问题的更多相关文章
- js参数arguments的理解
原文地址:js参数arguments的理解 对于函数的参数而言,如下例子 function say(name, msg){ alert(name + 'say' + msg); } say('xiao ...
- js赋值运算的理解
简介 js引擎由于为了效率,很多时候的非直接量赋值都不是copy一份在赋值给新的变量,而是一个引用 ps:直接量:直接值数字字符串等 为什么使用len = doms.length; 里的len效率要比 ...
- js 模块化的一些理解和es6模块化学习
模块化 1 IIFE 2 commonjs 3 浏览器中js的模块化 4 简单理解模块加载器的原理 5 es6 之前在参加百度前端技术学院做的小题目的时候,自己写模块的时候 都是写成立即调用表达式( ...
- 我对 impress.js 源码的理解
源码看了两天,删掉了一些优化,和对 ipad 的支持,仅研究了其核心功能的实现,作以下记录. HTML 结构如下: <!doctype html> <html lang=" ...
- 理解Babel是如何编译JS代码的及理解抽象语法树(AST)
Babel是如何编译JS代码的及理解抽象语法树(AST) 1. Babel的作用是? 很多浏览器目前还不支持ES6的代码,但是我们可以通过Babel将ES6的代码转译成ES5代码,让所有的浏览器都 ...
- 谈谈我对 js原型链的理解
想要学习 “原型链” 必须要认识什么是 “原型” 和 “原型链” 先理解一下普通的继承和原型的区别,下面写一段js代码来帮助理解: var Animal = function(){ // 动物抽象类 ...
- 关于js执行机制的理解
js是单线程语言.指的是js的所以程序执行通过仅有的这一个主线程来执行. 但是还有辅助线程,包括定时器线程,ajax请求线程和事件线程. js的异步我理解的是: 主线程执行时候,从上到下依次执行,遇到 ...
- Node.js Event Loop 的理解 Timers,process.nextTick()
写这篇文章的目的是将自己对该文章的理解做一个记录,官方文档链接The Node.js Event Loop, Timers, and process.nextTick() 文章内容可能有错误理解的地方 ...
- js原型浅谈理解
之前在学习原型(prototype)的时候,一直对原型的理解不是很清晰,只是知道每个对象都有一个原型,然后在js中万物又皆对象.在这里谈一下自己对于js原型的简单理解吧. 原型可以实现属性和方法的共享 ...
随机推荐
- ubuntu14.10环境下使用apache2.2配置代理服务器
参考网址: 使用Apache架设代理服务器 ubuntu之apache正向代理及反向代理(ProxyPass\ProxyPassReverse) Apache配置正向代理与反向代理 我的配置步骤: 1 ...
- C语言文件操作函数大全(超详细)
C语言文件操作函数大全(超详细) 作者: 字体:[增加 减小] 类型:转载 本篇文章是对C语言中的文件操作函数进行了详细的总结分析,需要的朋友参考下 fopen(打开文件)相关函数 open,fc ...
- android:ellipsize实现跑马灯效果总结
最近无意间看到了涉及到跑马灯效果的代码,于是在网上查阅了很多资料,在这里对自己看的一些文章进行一下总结,顺便加上自己的一些体会. 让我们一步步逐渐向下. 首先我们要实现走马灯这样一个效果,通常来说都是 ...
- Python数据处理——绘制函数图形以及数据拟合
1.多项式拟合 对散点进行多项式拟合并打印出拟合函数以及拟合后的图形import matplotlib.pyplot as pltimport numpy as npx=np.arange(1,17, ...
- windows与虚拟机共享文件夹设置
1.在windows上建立共享文件夹2.virtualbox点击设置-共享文件夹-添加共享文件夹指定windows上的路径,指定名称code: 选择自动挂载.固定分配3.在centos上面执行如下命令 ...
- go实现定时功能两种方法
1:timer 学习自:https://studygolang.com/articles/2479 timer1 := time.NewTimer(time.Second * 2) //此处在等待ch ...
- JanusGraph的schema及数据建模
每个JanusGraph都有一个schema,该schema由edge labels, property keys和vertex labels组成.JanusGraph的schema可以显式或隐式创建 ...
- Hive 差集运算
差集定义:一般地,设A,B是两个集合,由所有属于A且不属于B的元素组成的集合,叫做集合A减集合B(或集合A与集合B之差). 类似地,对于集合A,B,我们把集合{x/x∈A,且x¢B}叫做A与B的差集, ...
- [ci]sonar sonar-runner安装并实现手动扫描项目
安装sonar: 下载地址:https://www.sonarqube.org/downloads/ wget https://sonarsource.bintray.com/Distribution ...
- 转:关于安卓多线程while(true)方法占用CPU高的原因及其解决方法
由于项目需要用到安卓多线程操作,结果开了四条线程,下载到平板一直很卡,CPU占用率暴涨.于是开始查找原因,发现是线程run()方法里的while(true)导致的, 下图是为解决时开启一条while( ...