1.JS的词法定义
JavaScript 源代码中的输入可以这样分类:
- WhiteSpace 空白字符
- LineTerminator 换行符
- Comment 注释
- Token 词
- IdentifierName 标识符名称,典型案例是我们使用的变量名,注意这里关键字也包含在内了。
- Punctuator 符号,我们使用的运算符和大括号等符号。
- NumericLiteral 数字直接量,就是我们写的数字。
- StringLiteral 字符串直接量,就是我们用单引号或者双引号引起来的直接量。
- Template 字符串模板,用反引号` 括起来的直接量。
空白字符
- (或称) 是 U+0009,是缩进 TAB 符,也就是字符串中写的 \t
- 是 U+000B,也就是垂直方向的 TAB 符
- 是 U+0020,就是最普通的空格了。
- 是 U+00A0,非断行空格,在文字排版中,可以避免因为空格在此处发生断行,其它方面和普通空格完全一样。多数的 JavaScript 编辑环境都会把它当做普通空格(。HTML 中,很多人喜欢用的 最后生成的就是它了。
- (旧称) 是 U+FEFF,这是 ES5 新加入的空白符,是 Unicode 中的零宽非断行空格,即字符的length长度为0。
换行符
- 是 U+000A,就是最正常换行符,在字符串中的\n。
- 是 U+000D,这个字符真正意义上的“回车”,在字符串中是\r,在一部分 Windows 风格文本编辑器中,换行是两个字符\r\n。
标识符名称
注意和是 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
2.JS的语法定义
语法规则:分号自动补全
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。
3.JS语句的分类
- 普通语句:声明语句,表达式语句,空语句,debugger语句等
- 控制型语句:if,try,while等
- 带标签的语句:在JS语句前加上标签(比如: "inner:" ),用于跳转
- 语句块:用大括号括起来的一组语句
JS语句执行机制
Completion Record( 用于描述异常、跳出等语句执行过程)。
Completion Record 表示一个语句执行完之后的结果,它有三个字段:
- [[type]] 表示完成的类型,有 break continue return throw 和 normal 几种类型;
- [[value]] 表示语句的返回值,如果语句没有,则是 empty;
- [[target]] 表示语句的目标,通常是一个 JavaScript 标签
普通语句的执行
普通语句执行后,会得到 [[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之后就不会再往后继续执行。
控制语句
控制类语句分成两部分
- 一类是对其内部造成影响,如 if、switch、while/for、try。
- 一类是对外部造成影响如break、continue、return、throw,这两类语句的配合,会产生控制代码执行顺序和执行逻辑的效果,这也是我们编程的主要工作。
一般来说, 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 结构的结果。
3.表达式语句
普通的表达式规则这里就不再赘述,主要列出几个比较特殊的。
乘方表达式
++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
文章参考:极客时间-重学前端
最后
感谢你能看到这里,如果你觉得这篇文章对你有用的话,不妨点个赞再走呀~