JS006. 详解自执行函数原理与数据类型的快速转换 (声明语句、表达式、运算符剖析)
今天的主角:
Operator | Description |
一元正值符 " + "(MDN) | 一元运算符, 如果操作数在之前不是number,试图将其转换为number。 |
圆括号运算符 " () "(MDN) |
圆括号运算符由一对圆括号组成,包裹表达式和子表达式用来覆盖常规的运算符优先级(MDN),达到低优先级的表达式比高优先级的表达式更早运算。 |
一元正值符 - " + "
? 为什么图例 - 3中 +'1.22' + 1.5 = 2.71999999… ? 查看我的另一篇文章:深入探讨带浮点数运算丢失精度问题(二进制的浮点数存储方式)
首先区分如 let x = '1.20' 是作为变量声明被浏览器解析,包括 function / let / var / const / class 等等这些都是作为 声明语句 被解析。
浏览器预解析到声明语句,则会将其存入堆栈区并为其分配存储空间。
而 运算符 - 一元正值符(+) 的作用是 声明语句 --> 表达式语句(“ 使某件事发生 ” 的计算带有副作用的表达式)。
它为什么带有副作用:
除了基本数据类型外,任何数据都会被正值运算符 “ + ” 转换成Number类型数据,包括Object或Array的引用如
let me = {
name: '97z4moon',
age: '24.00'
} console.log(typeof +me.age, ': ', +me.age)
// number: 24
console.log(typeof +me.name, ': ', +me.name)
// number: NaN
若数据无法被转换成Number类型,则仍然返回number类型的NaN。除此之外,JS特别的 “ Date对象 ” 也是能够被转换成Number类型的数据:
window.onload = function(){
console.log(new Date())
// Wed Apr 07 2021 17:17:12 GMT+0800 (中国标准时间) console.log(new Date().getTime())
// 1617787032426
console.log(+new Date())
// 1617787032426
}
new Date( ) 在正值符作用下等同于 new Date( ).getTime( ) ,发现这一点的我甚至怀疑了 getTime( ) 的存在价值。
小总结
- 声明语句(MDN):将一条数据、一个函数、一块语句等等声明成一个变量,在堆栈区为其分配内存空间,需要特别注意的是 声明提升(hoisting)。
- 表达式语句(MDN):“ 使某件事发生 ” 的计算带有副作用的语句,需要特别注意的是 运算符的副作用 。
- 运算符(MDN):对单 / 多个数据进行特定规则的运算,有些运算符会包含一些附加作用,如尝试对参与运算数据进行类型转换(Number)的一元正值符 “ + ” ,它们往往会对数据造成意料之外的效果,需要特别注意的是 运算符的优先级 。
圆括号运算符 - " ( ) "
分组运算符 - 使低优先级比高优先级更早运算。
1 + 2 * 2 // 5
( 1 + 2 ) * 2 // 6
但我们知道" () " 在JS中还充当其他重要的作用,它们脱离了运算符的概念:
参数传递
funA(12)不是为了优先执行“ 12 ”,它起着另一种至关重要的作用。除此之外它还有其他的作用:
函数调用
funA()
限定作用
条件语句 (Condition)
if (i < 10) { // Some code. }
循环语句 (Loop)
for (var i = 0; i < 10; i++) { // Some code. }
强制表达式运算
我们都知道 ( 1 + 2 ) * 2 能够执行,那么 ( 1 + 2 ) 呢,( 2 ) 呢?
最常用的立即执行函数 Immediately-Invoked Function Expression (IIFE)由此诞生:
图例 -1 和 图例 -2 写法相似,结果相同,但它们绝不相同,浏览器内部发生的事情绝对刺激!
了解立即执行函数的原理,请阅读我的另一篇文章:JS016. 详解匿名函数_立即执行函数_递归函数(自执行函数)
运算符 - 优先级 & 结合性 & 分组短路
运算符有优先级,就如css的权值一样有不同的执行顺序,而一个表达式除了优先级,还有结合性。
我们大多数情况遇到的都是左结合(左到右),如:
2 + 3 * 4 // 2 + 12
1 * 2 ** 3 // 1 * 8
而有些个例是作为右结合被解析的,如赋值运算符:
a = b = 5
// 等价于
a = ( b = 5 )
另一个例子是,只有幂运算符是右结合的,而其他算术运算符都是左结合的。有趣的是,无论结合性和优先级如何,求值顺序总是从左到右:
混合求除法和幂:
2 ** 3 / 3 ** 2 //0.8888888888888888...
//等价于
( 2 ** 3 ) / ( 3 ** 2 )
还有一个很常用且很有趣的意外情况:分组与短路
分组( Group )在下表中拥有最高优先级,但并不意味着浏览器总是对分组符号 (...) 内的表达式优先求值,比如涉及到短路时。
短路是条件求值的术语。例如,在表达式 a && ( b + c ) 中,如果 a 为虚值(falsy),那么即使 ( b + c ) 在圆括号中,也不会被求值。我们可以说逻辑或运算符(" OR ")是 “短路的”。
除了逻辑或运算符外,其他短路运算符还包括逻辑与(" AND ")、空值合并、可选链和条件(三元)运算符。
a || (b * c); // 首先对 `a` 求值,如果 `a` 为真值则直接返回 `a` (变量a储存的值)
a && (b < c); // 首先对 `a` 求值,如果 `a` 为虚值则直接返回 `a` (false)
a ?? (b || c); // 首先对 `a` 求值,如果 `a` 不是 `null` 或 `undefined` 则直接返回 `a`
a?.b.c; // 首先对 `a` 求值,如果 `a` 是 `null` 或 `undefined` 则直接返回 `undefined`
3 > 2 && 2 > 1
// 返回 true 3 > 2 > 1
// 返回 false,因为 3 > 2 是 true,然后 true 会在比较运算符中
// 被隐式转换为 1,因此 true > 1 会变为 1 > 1,结果是 false
// 加括号可以更清楚:(3 > 2) > 1
汇总表
优先级 | 运算符类型 | 结合性 | 运算符 |
---|---|---|---|
21 | 分组 | n/a(不相关) | ( … ) |
20 | 成员访问 | 从左到右 | … . … |
需计算的成员访问 | 从左到右 | … [ … ] |
|
new (带参数列表) |
n/a | new … ( … ) |
|
函数调用 | 从左到右 | … ( … ) |
|
可选链(Optional chaining) | 从左到右 | ?. |
|
19 | new (无参数列表) |
从右到左 | new … |
18 | 后置递增 | n/a | … ++ |
后置递减 | … -- |
||
17 | 逻辑非 (!) | 从右到左 | ! … |
按位非 (~) | ~ … |
||
一元加法 (+) | + … |
||
一元减法 (-) | - … |
||
前置递增 | ++ … |
||
前置递减 | -- … |
||
typeof |
typeof … |
||
void |
void … |
||
delete |
delete … |
||
await |
await … |
||
16 | 幂 (**) | 从右到左 | … ** … |
15 | 乘法 (*) | 从左到右 | … * … |
除法 (/) | … / … |
||
取余 (%) | … % … |
||
14 | 加法 (+) | 从左到右 | … + … |
减法 (-) | … - … |
||
13 | 按位左移 (<<) | 从左到右 | … << … |
按位右移 (>>) | … >> … |
||
无符号右移 (>>>) | … >>> … |
||
12 | 小于 (<) | 从左到右 | … < … |
小于等于 (<=) | … <= … |
||
大于 (>) | … > … |
||
大于等于 (>=) | … >= … |
||
in |
… in … |
||
instanceof |
… instanceof … |
||
11 | 相等 (==) | 从左到右 | … == … |
不相等 (!=) | … != … |
||
一致/严格相等 (===) | … === … |
||
不一致/严格不相等 (!==) | … !== … |
||
10 | 按位与 (&) | 从左到右 | … & … |
9 | 按位异或 (^) | 从左到右 | … ^ … |
8 | 按位或 (|) | 从左到右 | … | … |
7 | 逻辑与 (&&) | 从左到右 | … && … |
6 | 逻辑或 (||) | 从左到右 | … || … |
5 | 空值合并 (??) | 从左到右 | … ?? … |
4 | 条件(三元)运算符 | 从右到左 | … ? … : … |
3 | 赋值 | 从右到左 | … = … |
… += … |
|||
… -= … |
|||
… **= … |
|||
… *= … |
|||
… /= … |
|||
… %= … |
|||
… <<= … |
|||
… >>= … |
|||
… >>>= … |
|||
… &= … |
|||
… ^= … |
|||
… |= … |
|||
… &&= … |
|||
… ||= … |
|||
… ??= … |
|||
2 | yield |
从右到左 | yield … |
yield* |
yield* … |
||
1 | 逗号 / 序列 | 从左到右 | … , … |
- END -
最新文章
- Android Studio accelerator key(shortcut)&; Basic knowledge
- linux命令每日一练习 wc more
- js数组的队栈操作
- 原博客地址http://blog.chinaunix.net/uid/20656672.html弃用
- C语言字符串与数字相互转换
- hdfs创建级联文件夹
- 数据结构-AVL树
- [!!!!!]Inno Setup教程-常见问题解答
- CSS强制英文换行
- 安全接口 interface --显示实现接口
- object-c 1
- Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(一)
- SQL Server 2008 Values 新用途
- DB2中的系统表SYSIBM.SYSDUMMY1
- .net Work Flow 4.0
- ContentProvider的使用
- input失效后,怎么改变它默认就有的灰色
- js精要之对象属性
- 扎实基础之从零开始-Nginx集群分布式.NET应用
- laravel中数据库在哪个文件中配置