"Re-learn front end" study notes (8)

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

  1. In ES5 and previous versions, there is only one source file types, only script.

  2. ES6 introduced in the beginning of the module mechanism, JavaScript, there are two source files, called scripts, called modules.

  3. Scripts can be introduced by the browser or execution environment node, and the module can only be used by the JavaScript code importexecution introduced.

  4. 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.

  5. 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 importand export.

  6. Script is a custom version of previously compatible in this mode, there importwill need to deal with loading " .js" File problem.
    Modern browsers can support with scriptthe introduction of the module or script tag, if you want to introduce module, you must give scriptadd tags type=“module”. If the introduction of the script, you do not need to type.

    <script type="module" src="xxxxx.js"></script>
    
  7. scriptLabel if not type=“module”, we think the default file is loaded rather than block script, if we wrote in the script export, of course, will throw wrong.

Scripts can be included in the statement.

Module may comprise three content: import 声明,Export 声明和语句。下面来讲讲Import 声明和Export声明。
Here Insert Picture Description

import declaration

importStatement in two ways:

  • It is a direct importone 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 fromof import, it introduced some of the information in the module. You can turn them into local variables.
import "mod"; // 引入一个模块
import v from "mod"; // 把模块默认的导出值放入变量 v

With fromthe importsubdivision 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 asdefault 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 importthe relative, exportstatements assume that the export task.

There are two ways to export variable modules:

  • One is used independently exportstatement

    Stand-alone exportstatement is a exportkeyword plus a list of variable names, such as:

    export {a, b, c};
    
  • Another is added directly before the statement type statement exportkeyword

    Here exportcan be added prior to any declaration nature of the statement, summarized as follows:

    • var
    • function(With asyncand generator)
    • class
    • let
    • const
  • exportThere is also a special usage, is now defaultused in combination.

    export defaultExport representing a default variable value, it can be used functionand class. Here are the variables exported without a name, you can use import 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语句。

关于函数体、模块和脚本能使用的语句,有个表格,可以参考:
Here Insert Picture Description

讲完了三种语法结构,下面介绍两个 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();
  1. 这个例子中,引入了with语句,用with(o)创建了一个作用域,并把 o 对象加入词法环境,在其中使用了var a = 2;语句。

  2. 在预处理阶段,只认var中声明的变量,所以同样为 foo 的作用域创建了 a 这个变量,但是没有赋值。

  3. 在执行阶段,当执行到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声明在全局的行为跟 functionvar 都不一样。

  • 在 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设计比 functionvar 更符合直觉,而且在遇到一些比较奇怪的用法时,倾向于抛出错误。

指令序言机制

脚本和模块都支持一种特别的语法,叫做指令序言(Directive Prologs)。

这里的指令序言最早是为了use strict设计的,它规定了一种给 JavaScript 代码添加元信息的方式。

"use strict"是 JavaScript 标准中规定的唯一一种指令序言,但是设计指令序言的目的是,留给 JS 的引擎和实现者一些统一的表达方式,在静态扫描时指定 JS 代码的一些特性。

发布了33 篇原创文章 · 获赞 73 · 访问量 2770

Guess you like

Origin blog.csdn.net/weixin_46124214/article/details/104212212
Recommended