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原型的简单理解吧. 原型可以实现属性和方法的共享 ...
随机推荐
- centos 7 安装gcc g++
yum install gcc gcc-c++ over. ps:如果系统报yum命令未找到,退出重新登陆试试root.
- docker实战——Docker本地私有镜像仓库Harbor搭建及配置
Harbor介绍 Docker容器应用的开发和运行离不开可靠的镜像管理,虽然docker官方提供了公共的镜像仓库(Docker Hub),但是从安全和效率等方面考虑,部署我们私有环境内的Registr ...
- git学习——打标签
打标签 就像某人发布某个软件版本(v1.0)后,一般会给一个标签. Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated).轻量级标签就像是个不会变化的分支, ...
- 【玩转cocos2d-x之三十九】Cocos2d-x 3.0截屏功能集成
3.0的截屏和2.x的截屏基本上同样.都是利用RenderTexture来处理,在渲染之前调用call函数,然后调用Cocos的场景visit函数对其进行渲染,渲染结束后调用end函数就可以.仅仅是3 ...
- redislive
安装Redis Live监控服务 分类: redis 2014-06-25 20:24 436人阅读 评论(0) 收藏 举报 redis redis live为监控redis服务的软件,带有监控web ...
- Spring Cloud简单入门教程
原文地址:http://www.cnblogs.com/skyblog/p/5127690.html 按照官方的话说:Spring Cloud 为开发者提供了在分布式系统(如配置管理.服务发现.断路器 ...
- CentOS 7中mariadb编译安装教程systemctl启动
mariadb做为mysql的替代品 现在centos的新版本yum包已换成mariadb 跟上篇一样只是启动方式改为systemd 安装一些库 yum install gcc gcc-c++ wge ...
- Atitit.编程语言新特性 通过类库框架模式增强 提升草案 v3 q27
Atitit.编程语言新特性 通过类库框架模式增强 提升草案 v3 q27 1. 修改历史2 2. 适用语言::几乎所有编程语言.语言提升的三个渠道::语法,类库,框架,ide2 2.1. 单根继承 ...
- eclipse代码凝视之模板xml
曾经在eclipse中配置凝视的时候,都是各自在自己电脑上配置凝视,每一个同事风格不一样,并且配置繁琐.假设新来的同事忘了提醒,可能就没有配置凝视了.所以我就把类.方法的凝视写成xml文件.然后将配置 ...
- string::find_last_of
今天在代码中用到string的这个方法,一不小心就用错了. 这是http://www.cplusplus.com/关于这个方法的解释. Find character in string from th ...