JavaScript 源代码中的输入可以这样分类:
注意和是 ES5 新加入的两个格式控制字符,它们都是 0 宽的。即length长度为0,也称为0宽非连接符和0宽连接符
十进制的 Number 可以带小数,小数点前后部分都可以省略,但是不能同时省略
.01
12.
12.01
以上的写法都是正确的,所以来看下一个问题
12.toString()
这个句代码在运行时会报错,因为12.会被看成一个带小数的整体。
正确的写法应该是
12..toString()
12 .toString()
(12).toString()
数字还支持科学计数法,注意E和e后面只能接整数
10.24E+2
10.24e-2
10.24e2
JS有一个no LineTerminator here 规则 :表示所在结构的此处不能插入换行符,假如此处插入了换行符,那么系统在编译的时候会自动补上分号
带换行的注释会被认为是换行符,系统会默认加上分号,所以返回undefined,下面看几个例子
function f(){
return/*
This is a return value.
*/1;
}
f();
上面得方法将返回undefined,因为return后面会自动补全一个分号。
var a = 1, b = 1, c = 1;
a
++
b
++
c
a,b,c后面会被加上分号,所以a还是为1,bc都为2
(function(){
console.log("a");
})()
(function(){
console.log("b");
})()
这里会报错 ,因为不会默认加分号 ,第三组括号会被理解成传参。
var a = [[]]/*这里没有被自动插入分号*/
[3, 2, 1, 0].forEach(e => console.log(e))
这里本来原本是想遍历数组,但是由于上面没有添加分号,并且不会自动补全分号,所以后面得数组变成得前面数组得下标和逗号表达式。
var x = 1, g = { test: () => 0 }, b = 1/*这里没有被自动插入分号*/
/ (a) / g.test("abc")
console.log(RegExp.$1)
这里由于没有自动补全分号,所以正则表达式得意思全部变了
总之不写分号可能会有问题,写分号一定没问题
脚本是可以由浏览器或者 node 环境引入执行的,而模块只能由 JavaScript 代码用 import 引入执行。
现代浏览器可以支持用 script 标签引入模块或者脚本,如果要引入模块,必须给 script 标签添加 type=“module”。如果引入脚本,则不需要 type。
Completion Record( 用于描述异常、跳出等语句执行过程)。
Completion Record 表示一个语句执行完之后的结果,它有三个字段:
普通语句执行后,会得到 [[type]] 为 normal 的 Completion Record,JavaScript 引擎遇到这样的 Completion Record,会继续执行下一条语句。
这些语句中,只有表达式语句会产生 [[value]],当然,从引擎控制的角度,这个 value 并没有什么用处。
如果你经常使用 Chrome 自带的调试工具,可以知道,输入一个表达式,在控制台可以得到结果,但是在前面加上 var,就变成了 undefined,因为语句从表达式语句变成了声明语句。
Chrome 控制台显示的正是语句的 Completion Record 的[[value]]。
语句块本身并不复杂,我们需要注意的是语句块内部的语句的 Completion Record 的[[type]] 如果不为 normal,会打断语句块后续的语句执行。
假如我们在正常 block 中插入了一条 return 语句,产生了一个非 normal 记录,那么整个 block 会成为非 normal。这个结构就保证了非 normal 的完成类型可以穿透复杂的语句嵌套结构,产生控制效果。
最简单的例子就是函数中的语句执行道return之后就不会再往后继续执行。
控制类语句分成两部分
一般来说, for/while - break/continue 和 try - throw 这样比较符合逻辑的组合,是大家比较熟悉的,但是,实际上,我们需要控制语句跟 break 、continue 、return 、throw 四种类型与控制语句两两组合产生的效果。
function foo(){
try{
return 0;
} catch(err) {
} finally {
console.log("a")
}
}
console.log(foo());
这里会在执行完finally之后再返回0,就是因为reutn会让try变成非normal的类型,但是也必须执行finally
或者:把try.catch.finally看做整体,会返回一个completion record, try return 0, finally console, 则输出console,接着返回这个completion record的vlaue。
在finally中也return一次
function foo(){
try{
return 0;
} catch(err) {
} finally {
return 1;
}
}
console.log(foo());
最终答案会输出1,因为finally 执行也得到了非 normal 记录,则会使 finally 中的记录作为整个 try 结构的结果。
普通的表达式规则这里就不再赘述,主要列出几个比较特殊的。
++i ** 30
2 ** 30 //正确
-2 ** 30 //报错
这里我们需要注意一下结合性,** 运算是右结合的,这跟其它正常的运算符(也就是左结合运算符)都不一样。
10 ** 20 **30
//相当于 10 ** (20 ** 30)
移位表达式由加法表达式构成,移位是一种位运算,分成三种:
移位运算把操作数看做二进制表示的整数,然后移动特定位数。
所以左移 n 位相当于乘以 2 的 n 次方,右移 n 位相当于除以2取整次。
普通移位会保持正负数。无符号移位会把减号视为符号位 1,同时参与移位:-1 >>> 1这个会得到 2147483647,也就是 2 的 31 次方,跟负数的二进制表示法相关(复数的二进制使用补码表示)。在 JavaScript 中,二进制操作整数并不能提高性能
按位与表达式 & :
按位与表达式把操作数视为二进制整数,然后把两个操作数按位做与运算。
10 & 3 = 2
10 & 5 = 0
按位异或 ^ :
按位异或表达式把操作数视为二进制整数,然后把两个操作数按位做异或运算。异或两位相同时得 0,两位不同时得 1。
10 & 3 = 9
10 & 5 = 15