美文网首页
JavaScript 知识框架二

JavaScript 知识框架二

作者: wyc0859 | 来源:发表于2022-06-01 23:57 被阅读0次

    JS 语句

    JS 语句有哪些特别之处,首先我们要看一个不太常见的例子, 向你介绍 语句执行机制涉及的一种基础类型:
    Completion 类型

    Completion Record 表示一个语句执行完之后的结果,它有三个字段:
    [type] 表示完成的类型,有 break continue return throw 和 normal 几种类型;
    [value] 表示语句的返回值,如果语句没有,则是 empty;
    [target] 表示语句的目标,通常是一个 JavaScript 标签

    JS正是依靠语句的 Completion Record 类型,方才可以在语句的复杂嵌套结构中,实现各种控制


    1、普通语句执行后,会得到 [type] 为 normal 的 Completion Record,JS引擎遇到这样的 Completion Record,会继续执行下一条语句。
    2、普通语句中,仅表达式语句执行后,会产生 [value]
    用浏览器控制台执行代码,可以看到:声明语句返回undefined,表达式语句返回value


    语句块

    语句块就是拿大括号括起来的一组语句,它是一种语句的复合结构,可以嵌套
    语句块本身并不复杂,先来看个普通语句的语句块

    {
      var i = 1; // normal
      i ++; // normal
      console.log(i) //normal
    } // normal  这个语句块也是normal
    

    我们看到,在一个 block 中,如果每一个语句都是 normal 类型,那么它会顺次执行。接下来我们加入 return 试试看。

    {
      var i = 1; // normal
      return i; // return
      i ++; 
      console.log(i)
    } // return  这个语句块是return,非 normal 
    

    这个结构就保证了非 normal 的完成类型可以穿透复杂的语句嵌套结构,产生控制效果。

    控制型语句

    控制类语句分成两部分
    一类是对其内部造成影响,如 if、switch、while/for、try
    另一类是对外部造成影响,如 break、continue、return、throw
    这两类语句的配合,会产生控制代码执行顺序和执行逻辑的效果,这也是我们编程的主要工作


    带标签的语句

    任何 JavaScript 语句是可以加标签的,在语句前加冒号即可,如:加了abcd标签


    大部分时候,这个东西类似于注释,没有任何用处。唯一有作用的时候是:与完成记录类型中的 target 相配合,用于跳出多层循环。

    outer: while(true) {
          inner: while(true) {
              break outer;
          }
    }
    // break/continue 语句如果后跟了关键字,会产生带 target 的完成记录
    // 一旦完成记录带了 target,那么只有拥有对应 label 的循环语句会消费它
    

    JS语法

    先说下源文件,JS 有两种源文件,一种叫做脚本,一种叫做模块。
    脚本是可以由浏览器或者 node 环境引入执行的,而模块只能由 JavaScript 代码用 import 引入执行。

    从概念上,可认为脚本具有主动性的 JavaScript 代码段,是控制宿主完成一定任务的代码;
    而模块是被动性的 JavaScript 代码段,是等待被调用的库。
    区别仅仅在于是否包含 import 和 export。


    import 声明有两种用法
    一个是直接 import 一个模块
    另一个是带 from 的 import,它能引入模块里的一些信息

    import  "mod";  //引入一个模块
    import v from "mod";  //把模块默认的导出值放入变量v
    

    直接 import 一个模块,只是保证了这个模块代码被执行,引用它的模块无法获得它的任何信息
    带 from 的 import 意思是引入模块中的一部分信息,可以把它们变成本地的变量

    函数体

    执行函数的行为通常是在 JavaScript 代码执行时,注册宿主环境的某些事件触发的,而执行的过程,就是执行函数体(函数的花括号中间的部分)。
    我们先看一个例子,感性地理解一下:

    setTimeout(function(){
    console.log("go go go");
    }, 10000)
    这段代码通过 setTimeout 函数注册了一个函数给宿主,当一定时间之后,宿主就会执行这个函数。宿主会为这样的函数创建宏任务。
    可以认为,宏任务中可能会执行的代码包括“脚本 (script)”、“模块(module)”和“函数体(function body)”

    预处理

    JavaScript 执行前,会对脚本、模块和函数体中的语句进行预处理。预处理过程将会提前处理 var、函数声明、class、const 和 let 这些语句,以确定其中变量的意义。

    1、var 声明**

    永远作用于脚本、模块和函数体这个级别,在预处理阶段,不关心赋值的部分,只管在当前作用域声明这个变量。

    var a = 1
    function foo() {
      console.log(a) //undefind
      var a = 2
    }
    foo()
    

    这段代码声明了一个脚本级别的 a,又声明了 foo 函数体级别的 a
    但是预处理过程在执行之前,所以有函数体级的变量 a,就不会去访问外层作用域中的变量 a 了,而函数体级的变量 a 此时还没有赋值,所以是 undefined

    接着看这段代码:

    var a = 1;
    function foo() {
        console.log(a);
        if(false) {
            var a = 2;
        }
    }
    foo();
    

    这段代码比上一段代码 多了一段 if,我们知道 if(false) 中的代码永远不会被执行,但是预处理阶段并不管这个,var 的作用能够穿透一切语句结构,它只认脚本、模块和函数体三种语法结构。所以这里结果跟前一段代码完全一样,我们会得到 undefined。

    2、function 声明**

    在全局(脚本、模块和函数体),function 声明表现跟 var 相似,不同之处在于,function 声明不但在作用域中加入变量,还会给它赋值。

    看这段代码

    console.log(foo) //[Function: foo]
    function foo() {}
    

    在foo声明之前,打印函数 foo,可以发现,已经是函数 foo 的值了

    接着看

    console.log(foo) //undefined
    if (true) {
       function foo() {}
    }
    

    这段代码得到 undefined,这说明 function 在预处理阶段仍然发生了作用,在作用域中产生了变量,没有产生赋值,赋值行为发生在了执行阶段。所以没报错,但值为未定义

    3、class 声明

    class 声明在全局的行为跟 function 和 var 都不一样,在 class 声明之前使用 class 名,会抛错:

    console.log(c);
    class c{
    }
    

    看上去很像是 class 没有预处理,但是实际上并非如此
    class 的声明作用不会穿透 if 等语句结构,所以只有写在全局环境才会有声明作用,这样的 class 设计比 function 和 var 更符合直觉,而且在遇到一些比较奇怪的用法时,倾向于抛出错误。按照现代语言设计的评价标准,及早抛错是好事

    相关文章

      网友评论

          本文标题:JavaScript 知识框架二

          本文链接:https://www.haomeiwen.com/subject/hcadprtx.html