重学前端-JavaScript部分-语法(一)
概述
本文我们开始介绍语法部分相关内容
脚本与模块
ES6之前,JS中只有一种源文件,称之为脚本;ES6之后,出现了另外一种源文件,称之为:模块
模块的使用
我们可以在script标签中加入type="module"
属性,这时候加载的文件就是模块,反之则为脚本
<script type="module" src="xxxxx.js"></script>
在脚本文件中我们可以使用import声明与export声明
关于import 与 export的用法可以参考ES6系列-5-Class与Module
函数体
宏任务中可能会执行的代码包括“脚本”、“模块”和“函数体”,函数体其实也是一个语句的列表。跟脚本和模块比起来,函数体中的语句列表中多了 return 语句可以用,函数体实际上有四种,下图做了对比
JS语法的全局机制
JavaScript 语法的全局机制:预处理和指令序言,这两个机制对于我们解释一些 JavaScript 的语法现象非常重要
预处理
JavaScript 执行前,会对脚本、模块和函数体中的语句进行预处理。预处理过程将会提前处理 var、函数声明、class、const 和 let 这些语句,以确定其中变量的意义。
var 声明
在预处理阶段,var 的作用能够穿透一切语句结构(包括if(false)
也无法阻挡),它只认脚本、模块和函数体三种语法结构。在ES6出现之前,为了模拟函数作用域,经常使用IIFE
function 声明
在全局(脚本、模块和函数体),function 声明表现跟 var 相似,不同之处在于,function 声明不但在作用域中加入变量,还会给它赋值:
console.log(foo); // 打印出函数体
function foo(){}
如果出现在if(false)
下,仍然会产生变量,只是不会被提前赋值
console.log(foo); // undefined
if(true) {
function foo(){} // 如果没有此处代码,上面console部分会报错
}
class 声明
class 声明在全局的行为跟 function 和 var 都不一样
提前使用会报错,这符合正常预期
console.log(c); // 报错 Uncaught ReferenceError: c is not defined
class c{}
但下面这种情况仍然会报错
var c = 1;
function foo(){
console.log(c); // ReferenceError: Cannot access 'c' before initialization
class c {}
}
foo();
这说明,class 声明也是会被预处理的,它会在作用域中创建变量,并且要求访问它时抛出错误。class 的声明作用不会穿透 if 等语句结构,所以只有写在全局环境才会有声明作用
指令序列
脚本和模块都支持一种特别的语法,叫做指令序言(Directive Prologs)。的目的是留给 JavaScript 的引擎和实现者一些统一的表达方式,在静态扫描时指定 JavaScript 代码的一些特性。
“use strict”是 JavaScript 标准中规定的唯一一种指令序言
自动插入分号规则
因为JavaScript 语言提供了相对可用的分号自动补全规则,所以很多开发者倾向于不写分号。
分号自动补全规则有三条:
- 有换行符,且下一个符号是不符合语法的,那么就尝试插入分号。
- 有换行符,且语法中规定此处不能有换行符,那么就自动插入分号。
- 源代码结束处,不能形成完整的脚本或者模块结构,那么就自动插入分号。
但在实际应用中,还有一个no LineTerminator here 规则:表示它所在的结构中的这一位置不能插入换行符
举个例子:
function foo(){
return
1
}
foo(); // undefined;
上面例子中,根据no LineTerminator here规则,return后面不能插入换行符,但现在存在换行符,那个一句分号自动补全规则的第二条:有换行符,且语法中规定此处不能有换行符,那么就自动插入分号
,引擎会在return后面直接插入分号,那么执行结果就不难理解了。
日常开发中还会出现几种不写分号,极易造成错误的情况:
以括号开头的语句
(function(a){ console.log(a); })()/*这里没有被自动插入分号*/ (function(a){ console.log(a); })()
以数组开头的语句
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)
以模板字符串开头的语句
var f = function(){ return ""; } var g = f/*这里没有被自动插入分号*/ `Template`.match(/(a)/); console.log(RegExp.$1)