本文首发于个人我的网站blog.skyline.ink,欢迎各位大大访问. 做者水平有限,文章仅供参考,不对的地方但愿各位及时指正,共同进步,不胜感激javascript
对于前端程序员来讲,JavaScript可谓灰常强,但此强非彼强。根据维基百科的阐释,在计算机编程中,通俗地将语言分为强类型和弱类型,虽然没有精确的定义,可是强类型有很是严格的规则,包括变量定义时必须指定类型,使用时必须是指望的类型,不然报错或拒绝编译。维基百科上的类型强弱html
JavaScript并不须要在定义时指定变量类型,同时使用时,不是指望的值会自动转换,弱类型相对于强类型来讲类型检查更不严格,故而引出了今天的问题,自动转换的一些机制前端
数据类型 | 转化成true | 转化成false |
---|---|---|
String | 非空字符 | ""(空字符) |
Number | 非零 | 0与NaN |
Object | 非Null对象 | null |
undefined | 无 | undefined |
(注:调用Boolean()方法获得结果相同)vue
对于多数状况来讲,对象隐式转换成字符串或数字,其实调用了一个叫作ToPrimitive(obj,preferredType)的内部方法来干这件事情,看了网上不少资料,都是balabala一大堆,规范里面文字也很多,其实调用这个方法转换的时候,除了date对象走转换数字流程(即preferredType值是number),其余走的都是转字符流程(即preferredType值是string),大概流程以下:java
对象 | 调用toString() |
---|---|
普通对象 | "[object Object]" |
数组arr | arr.join() |
函数类 | 定义函数的代码 |
日期类 | 可读日期 |
正则对象 | 正则对象字面量的字符 |
值 | 转化成字符串 | 转化成数字 | 转化成布尔值 |
---|---|---|---|
undefined | "undefined" | NaN | false |
null | "null" | 0 | false |
NaN | "NaN" | NaN | false |
[](空数组) | "" | 0 | true |
""(空字符串) | "" | 0 | false |
2 + "3"; // "23"
1 + 2 + "3"; // "33"
true + 2 + "3"; // "33"
1 + "2" + 3; // "123"
"2" + true; //"2true"
"2" + undefined; //"2undefined"
"2" + NaN //"2NaN"
'23' + {'a': 1} //"23[object Object]"
'23' + [1,3,{}, null, undefined, '', '2'] // "231,3,[object Object],,,,2"
[1,3,{}, null, undefined, '', '2'].toString() //"1,3,[object Object],,,,2"
23 + "1,3,[object Object],,,,2" //"231,3,[object Object],,,,2"
复制代码
1 + [] //"1"
1 + [1] //"11"
1 + {a:'a'} //"1[object Object]"
null + null //0
true + {a:'a'} //"true[object Object]"
复制代码
typeof NaN //"number"
null + undefined //NaN
1 + undefined //NaN
复制代码
1 - '5' //-4
1 - [2, 2] //NaN
1 - {a:1} //NaN
1- undefined //NaN
1 - [] //1
1 - [2, 2] //NaN
1 - null //1
复制代码
+ '3' // 数字3
- '-3' // 数字3
复制代码
var x = NaN;
x === NaN; // false
undefined == "undefined" // false
null == "null" // false
null == 0 // false
null == false // false
undefined == 0 // false
undefined == false // false
复制代码
如下内容纯属拓展,不感兴趣的童鞋可忽略node
[] + {} //"[object Object]"
{} + [] // 0
[] + {} === {} + [] // true
{} + [] === [] + {} // true
{a: 1} // {a: 1}
{a: 1}; // 1
{'a': 1} // {a: 1}
{'a': 1}; // SyntaxError
{} + 0 + {}; // "0[object Object]"
{} + 0 + {} // "[object Object]0[object Object]"
复制代码
[] + {} //"[object Object]"
{} + [] // 0
[] + {} === {} + [] // true
{} + [] === [] + {} // false
{a: 1} // 1
{a: 1}; // 1
{'a': 1} // SyntaxError
{'a': 1}; // SyntaxError
{} + 0 + {}; // "0[object Object]"
{} + 0 + {} // "0[object Object]"
复制代码
MDN:label {a: 1}相关的几个输出里,先忽略分号,{}分别被当作block和object literal,当被当作代码块时,入下所示:webpack
//{'a': 1}
{
"a": 1; // 语法错误
}
// {a: 1}
{
a:
1;
} // ^-- Automatic Semicolon Insertion
复制代码
分号是否书写在前端领域来讲,这个问题如同vi、emacs编辑器之争,最好计算机语言之争通常。在知乎上看了JavaScript 语句后应该加分号么?后,大体总结出来就是:git
说回ASI,程序员
官方规范中的ASIweb
官文太抽象,网上大神的翻译大多数更加抽象,根据网上各类资料来看,总结用口水话说就是代码块最后一条语句自动插入,换行时候大多数在语句末尾自动插入,除了:
wikipedia: Abstract_syntax_tree
In computer science, an abstract syntax tree (AST), or just syntax tree, is a tree representation of the abstract syntactic structure of source code written in a programming language. Each node of the tree denotes a construct occurring in the source code. The syntax is "abstract" in not representing every detail appearing in the real syntax. For instance, grouping parentheses are implicit in the tree structure, and a syntactic construct like an if-condition-then expression may be denoted by means of a single node with three branches.
其实就是将源代码分析成所对应的树状结构,便于以后的语法分析,代码检查等。如今的不少热门工具如webpack、vue、UglifyJS、Lint等都会用到这个技术,各个浏览器引擎也会使用自家定义的一套语法书生成树规范,生成相应的语法树。
其实因为浏览器厂商众多,每一个与解析状况不一致,日常代码中基本不会遇到{}+这种问题,咱们也没有精力研究各厂商预解析源码,从Chrome和Firefox来看,总结出来有下面几点:
大概在chrome版本49以前,Chrome控制台上面的输出结果基本和Firefox一致,以后在chrome上有人提出bug,Issue 499864,大概意思就是说我在控制台输入{a: 4, b: 5}
你给我报个错干吗,我就是想要一个对象而已。Chrome确实该近几年大火,没过多久就修复了,修复的方式也特别666,就是凡是语句以{开头,以}结尾,我解析的时候就包裹一层括号在外面。git记录,里面的关键代码以下:
+ if (/^\s*\{/.test(text) && /\}\s*$/.test(text))
+ text = '(' + text + ')';
复制代码
也就是说{} + 0 + {}实际上是({} + 0 + {}), {a: 1}实际上是({a: 1}),也就是说在Chrome中,凡是语句以{开头,
以{} + 0 + {}为例来看
此时,Chrome将第一个{}解析成对象
此时,firefox将第一个{}解析成代码块
分析以后不可贵出如上的结果
《JavaScript高级程序设计》