重学前端学习笔记(三十)--JavaScript语法的基本规则

笔记说明

重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系,笔者主要整理学习过程的一些要点笔记以及感悟,完整的可以加入winter的专栏学习【原文有winter的语音】,如有侵权请联系我,邮箱:kaimo313@foxmail.com。

一、脚本和模块

JavaScript 有两种源文件,一种叫做脚本,一种叫做模块。在 ES5 和之前的版本中,就只有一种源文件类型(就只有脚本),ES6 引入了模块机制。

1.1、区别

1、脚本:是可以由浏览器或者 node 环境引入执行的;模块:只能由 JavaScript 代码用 import 引入执行。

2、从概念上,脚本:具有主动性的 JavaScript 代码段,是控制宿主完成一定任务的代码;模块:是被动性的 JavaScript 代码段,是等待被调用的库。

3、如果要引入模块,必须给 script 标签添加 type="module"。如果引入脚本,则不需要 type。

<script type="module" src="xxx.js"></script>
复制代码

1.2、import 声明

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

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

1、带 from 的 import 细分有三种用法

  • import x from "./a.js" 引入模块中导出的默认值。
  • import {a as x, modify} from "./a.js"; 引入模块中的变量。
  • import * as x from "./a.js" 把模块中所有的变量以类似对象属性的方式引入。

第一种方式可以跟后两种组合使用。

  • import d, {a as x, modify} from "./a.js"
  • import d, * as x from "./a.js"

2、例子

假设有两个模块 a 和 b。我们在模块 a 中声明了变量和一个修改变量的函数,并且把它们导出。用 b 模块导入了变量和修改变量的函数。

模块 a

export var a = 1;

export function modify(){
    a = 2;
}
复制代码

模块 b

import {a, modify} from "./a.js";

console.log(a); // 1

modify();

console.log(a); // 2
复制代码

1.3、export 声明

与 import 相对,export 声明承担的是导出的任务。模块中导出变量的方式有两种,一种是独立使用 export 声明,另一种是直接在声明型语句前添加 export 关键字。

1、独立使用 export 声明

一个 export 关键字加上变量名列表.

export {a, b, c};
复制代码

2、export 可以加在任何声明性质的语句之前

  • var
  • function (含 async 和 generator)
  • class
  • let
  • const

3、export default 表示导出一个默认变量值,它可以用于 function 和 class。这里导出的变量是没有名称的,可以使用 import x from "./a.js" 这样的语法,在模块中引入。

4、export default 还支持一种语法,后面跟一个表达式

// 注意:a 的变化与导出的值就无关,修改变量 a,不会使得其他模块中引入的 default 值发生改变。
var a = {};
export default a;
复制代码

5、在 import 语句前无法加入 export,但是可以直接使用 export from 语法。

export a from "a.js"
复制代码

二、函数体

执行函数的行为通常是:在 JavaScript 代码执行时,注册宿主环境的某些事件触发的,执行的过程:就是执行函数体(函数的花括号中间的部分)。函数体:其实也是一个语句的列表。跟脚本和模块比起来,函数体中的语句列表中多了 return 语句可以用。

2.1、普通函数体

function foo(){
    //Function body
}
复制代码

2.2、异步函数体

async function foo(){
    //Function body
}
复制代码

2.3、生成器函数体

function *foo(){
    //Function body
}
复制代码

2.4、异步生成器函数体

async function *foo(){
    //Function body
}
复制代码

2.5、上面四种函数体的区别

区别在于:能否使用 await 或者 yield 语句。

三、预处理

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

3.1、var 声明

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

3.1.1、例子一
var a = 1;

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

foo(); // undefined
复制代码

预处理过程在执行之前,所以有函数体级的变量 a,就不会去访问外层作用域中的变量 a 了,而函数体级的变量 a 此时还没有赋值,所以是 undefined。

3.1.2、例子二
var a = 1;

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

foo(); // undefined
复制代码

首先 if(false) 中的代码永远不会被执行,但是预处理阶段并不管这个,var 的作用能够穿透一切语句结构,它只认脚本、模块和函数体三种语法结构。

3.1.3、例子三
var a = 1;

function foo() {
    var o= {a:3}
    with(o) {
        var a = 2;
    }
    console.log(o.a);
    console.log(a);
}

foo(); // 2  undefine
复制代码
3.1.4、例子四

早年 JavaScript 没有 let 和 const,只能用 var,而 var 除了脚本和函数体都会穿透,可用立即执行的函数表达式(IIFE)来产生作用域。

for(var i = 0; i < 20; i ++) {
    void function(i){
        var div = document.createElement("div");
        div.innerHTML = i;
        div.onclick = function(){
            console.log(i);
        }
        document.body.appendChild(div);
    }(i);
}
// 点击div对应的序号

for(var i = 0; i < 20; i ++) {
    var div = document.createElement("div");
    div.innerHTML = i;
    div.onclick = function(){
        console.log(i);
    }
    document.body.appendChild(div);
}
// 点击div全是20
复制代码

3.2、function 声明

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

3.2.1、例子一
console.log(foo);
function foo(){

}

// ƒ foo(){}
复制代码
3.2.2、例子二
console.log(foo);
if(true) {
    function foo(){

    }
}
// undefined
复制代码

3.3、class 声明

class 声明在全局的行为跟 function 和 var 都不一样。

3.3.1、例子一
console.log(c);
class c{

}
// 报错
复制代码
3.3.2、例子二
var c = 1;
function foo(){
    console.log(c);
    class c {}
}
foo();

// 还是报错,这说明,class 声明也是会被预处理的,它会在作用域中创建变量,并且要求访问它时抛出错误。
复制代码

四、指令序言机制

脚本和模块都支持一种特别的语法,叫做指令序言(Directive Prologs)。最早是为了 use strict 设计的,它规定了一种给 JavaScript 代码添加元信息的方式。

"use strict"; // 单引号也不影响它是指令序言。
function f(){
    console.log(this);
};
"use strict"; // 没有出现在最前,所以不是指令序言。
f.call(null);

// null
复制代码

个人总结

两个 JavaScript 语法的全局机制:预处理和指令序言。winter:“这两个机制对于我们解释一些 JavaScript 的语法现象非常重要。不理解预处理机制我们就无法理解 var 等声明类语句的行为,而不理解指令序言,我们就无法解释严格模式。”

转载于:https://juejin.im/post/5cfabe1051882551b47a40ec

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值