Grammar JavaScript: JavaScript syntax (1):
semicolon
Automatic semicolon insertion rules
Semicolon is automatically inserted in fact independent of all the rules of grammar production defines its rules to say is very simple, only three.
- Have line breaks, and the next symbol is not consistent syntax, then try to insert a semicolon.
- There are line breaks, and the syntax specified here can not have a line break, then automatically inserted semicolon.
- At the end of the source code, can not form a complete script or module structure, then automatically inserted semicolon.
no LineTerminator here Rule
JavaScript syntax (2):
Scripts and modules
-
In ES5 and previous versions, there is only one source file types, only script.
-
ES6 introduced in the beginning of the module mechanism, JavaScript, there are two source files, called scripts, called modules.
-
Scripts can be introduced by the browser or execution environment node, and the module can only be used by the JavaScript code
import
execution introduced. -
Conceptually, we can think of the script proactive JavaScript code snippet, code control is host to complete certain tasks; and passive module is a JavaScript code snippet, the library is waiting to be called.
-
We do some comparison of standard grammar productions, not difficult to find, the difference between the module and the script is really only that contains
import
andexport
. -
Script is a custom version of previously compatible in this mode, there
import
will need to deal with loading ".js
" File problem.
Modern browsers can support withscript
the introduction of the module or script tag, if you want to introduce module, you must givescript
add tagstype=“module”
. If the introduction of the script, you do not need to type.<script type="module" src="xxxxx.js"></script>
-
script
Label if nottype=“module”
, we think the default file is loaded rather than block script, if we wrote in the scriptexport
, of course, will throw wrong.
Scripts can be included in the statement.
Module may comprise three content: import 声明,
Export 声明和语句。下面来讲讲
Import 声明和
Export声明。
import declaration
import
Statement in two ways:
- It is a direct
import
one module. This module is only to ensure that the code is executed, the module is referenced it can not get any of its information. - The other is a band
from
ofimport
, it introduced some of the information in the module. You can turn them into local variables.
import "mod"; // 引入一个模块
import v from "mod"; // 把模块默认的导出值放入变量 v
With from
the import
subdivision there are three usage, we can look at an example of each:
import x from "./a.js"
The introduction of defaults module export.import {a as x, modify} from "./a.js";
Injection module variables.import * as x from "./a.js"
The modules all variables in a similar manner as the introduction of object properties.
The first approach can also be used in combination with the latter two :( syntax requirements with no as
default value is always in the front.)
import d, {a as x, modify} from "./a.js"
import d, * as x from "./a.js"
export declaration
And import
the relative, export
statements assume that the export task.
There are two ways to export variable modules:
-
One is used independently
export
statementStand-alone
export
statement is aexport
keyword plus a list of variable names, such as:export {a, b, c};
-
Another is added directly before the statement type statement
export
keywordHere
export
can be added prior to any declaration nature of the statement, summarized as follows:var
function
(Withasync
andgenerator
)class
let
const
-
export
There is also a special usage, is nowdefault
used in combination.export default
Export representing a default variable value, it can be usedfunction
andclass
. Here are the variables exported without a name, you can useimport x from "./a.js"
this syntax, introduced in the module.
export default
Also supports syntax, with an expression later,
var a = {};
export default a;
但是,这里的行为跟导出变量是不一致的,这里导出的是值,导出的就是普通变量 a 的值,以后 a 的变化与导出的值就无关了,修改变量 a,不会使得其他模块中引入的 default 值发生改变。
函数体
执行函数的行为通常是在 JavaScript 代码执行时,注册宿主环境的某些事件触发的,而执行的过程,就是执行函数体(函数的花括号中间的部分)。
函数体其实也是一个语句的列表。跟脚本和模块比起来,函数体中的语句列表中多了return
语句可以用。
函数体实际上有四种:
-
普通函数体
-
异步函数体
-
生成器函数体,例如:
function *foo(){ //Function body }
-
异步生成器函数体,例如:
async function *foo(){ //Function body }
上面四种函数体的区别在于:能否使用
await
或者yield
语句。
关于函数体、模块和脚本能使用的语句,有个表格,可以参考:
讲完了三种语法结构,下面介绍两个 JavaScript 语法的全局机制:预处理和指令序言。
这两个机制对于我们解释一些 JavaScript 的语法现象非常重要。不理解预处理机制我们就无法理解 var 等声明类语句的行为,而不理解指令序言,我们就无法解释严格模式。
预处理
JavaScript 执行前,会对脚本、模块和函数体中的语句进行预处理。预处理过程将会提前处理 var、函数声明、class、const 和 let 这些语句,以确定其中变量的意义。
var 声明
var 声明永远作用于脚本、模块和函数体这个级别,在预处理阶段,不关心赋值的部分,只管在当前作用域声明这个变量。
var 的作用能够穿透一切语句结构,它只认脚本、模块和函数体三种语法结构。
一个例子:
var a = 1;
function foo() {
var o= {a:3}
with(o) {
var a = 2;
}
console.log(o.a);
console.log(a);
}
foo();
-
这个例子中,引入了
with
语句,用with(o)
创建了一个作用域,并把 o 对象加入词法环境,在其中使用了var a = 2;
语句。 -
在预处理阶段,只认var中声明的变量,所以同样为 foo 的作用域创建了 a 这个变量,但是没有赋值。
-
在执行阶段,当执行到
var a = 2
时,作用域变成了with
语句内,这时候的 a 被认为访问到了对象 o 的属性 a,所以最终执行的结果,我们得到了 2 和undefined。这个行为是 JavaScript 公认的设计失误之一,一个语句中的 a 在预处理阶段和执行阶段被当做两个不同的变量,严重违背了直觉,但是今天,在 JavaScript 设计原则“don’t break the web”之下,已经无法修正了,所以你需要特别注意。
function 声明
function
声明的行为原本跟 var
非常相似,但是在最新的 JavaScript 标准中,对它进行了一定的修改,这让情况变得更加复杂了。
-
在全局(脚本、模块和函数体),
function
声明表现跟 var 相似,不同之处在于,function
声明不但在作用域中加入变量,还会给它赋值。console.log(foo); function foo(){ }
这里声明了函数 foo,在声明之前,我们用
console.log
打印函数 foo,我们可以发现,已经是函数 foo 的值了。 -
function
声明出现在if
等语句中的情况有点复杂,它仍然作用于脚本、模块和函数体级别,在预处理阶段,仍然会产生变量,它不再被提前赋值。console.log(foo); if(true) { function foo(){ } }
这段代码得到
undefined
。如果没有函数声明,则会抛出错误。这说明
function
在预处理阶段仍然发生了作用,在作用域中产生了变量,没有产生赋值,赋值行为发生在了执行阶段。出现在
if
等语句中的function
,在if
创建的作用域中仍然会被提前,产生赋值效果。
class 声明
class
声明在全局的行为跟 function
和 var
都不一样。
-
在 class 声明之前使用
class
名,会抛错:console.log(c); class c{ }
这段代码我们试图在
class
前打印变量 c,我们得到了个错误,这个行为很像是class
没有预处理,但是实际上并非如此。 -
看个复杂一点的例子:
var c = 1; function foo(){ console.log(c); class c {} } foo();
这个例子中,我们把
class
放进了一个函数体中,在外层作用域中有变量 c。然后试图在class
之前打印 c。执行后,仍然抛出了错误,如果去掉class
声明,则会正常打印出 1,也就是说,出现在后面的class
声明影响了前面语句的结果。这说明,
class
声明也是会被预处理的,它会在作用域中创建变量,并且要求访问它时抛出错误。
class
的声明作用不会穿透if
等语句结构,所以只有写在全局环境才会有声明作用
这样的 class
设计比 function
和 var
更符合直觉,而且在遇到一些比较奇怪的用法时,倾向于抛出错误。
指令序言机制
脚本和模块都支持一种特别的语法,叫做指令序言(Directive Prologs)。
这里的指令序言最早是为了use strict
设计的,它规定了一种给 JavaScript 代码添加元信息的方式。
"use strict
"是 JavaScript 标准中规定的唯一一种指令序言,但是设计指令序言的目的是,留给 JS 的引擎和实现者一些统一的表达方式,在静态扫描时指定 JS 代码的一些特性。