标识标牌网站怎么做,ppt模板大全app,深圳网站建设联系电话,怎么开网页游戏平台目录
1.概述
2. 严格模式
3. export 命令
4. import 命令
5. 模块的整体加载
6. export default 命令
7. export 与 import 的复合写法
8. 模块的继承
9. 跨模块常量
10. import()
10.1简介
11适用场合
11.1按需加载
11.2条件加载
11.3动态的模块路径
11.4注意…目录
1.概述
2. 严格模式
3. export 命令
4. import 命令
5. 模块的整体加载
6. export default 命令
7. export 与 import 的复合写法
8. 模块的继承
9. 跨模块常量
10. import()
10.1简介
11适用场合
11.1按需加载
11.2条件加载
11.3动态的模块路径
11.4注意点
总结
“睡服“面试官系列之各系列目录汇总建议学习收藏 1.概述
历史上JavaScript 一直没有模块module体系无法将一个大程序拆分成互相依赖的小文件再用简单的方法拼装起来。其他语言都有这项功能 比如 Ruby 的 require 、Python 的 import 甚至就连 CSS 都有 import 但是 JavaScript 任何这方面的支持都没有这对开发大型的、复杂的项目 形成了巨大障碍。
在 ES6 之前社区制定了一些模块加载方案最主要的有 CommonJS 和 AMD 两种。前者用于服务器后者用于浏览器。ES6 在语言标准的层面上 实现了模块功能而且实现得相当简单完全可以取代 CommonJS 和 AMD 规范成为浏览器和服务器通用的模块解决方案。 ES6 模块的设计思想是尽量的静态化使得编译时就能确定模块的依赖关系以及输入和输出的变量。CommonJS 和 AMD 模块都只能在运行时确 定这些东西。比如CommonJS 模块就是对象输入时必须查找对象属性。
// CommonJS模块
let { stat, exists, readFile } require(fs);
// 等同于
let _fs require(fs);
let stat _fs.stat;
let exists _fs.exists;
let readfile _fs.readfile;
上面代码的实质是整体加载 fs 模块即加载 fs 的所有方法生成一个对象 _fs 然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加 载”因为只有运行时才能得到这个对象导致完全没办法在编译时做“静态优化”。 ES6 模块不是对象而是通过 export 命令显式指定输出的代码再通过 import 命令输入。
// ES6模块
import { stat, exists, readFile } from fs;
上面代码的实质是从 fs 模块加载 3 个方法其他方法不加载。这种加载称为“编译时加载”或者静态加载即 ES6 可以在编译时就完成模块加载效率要 比 CommonJS 模块的加载方式高。当然这也导致了没法引用 ES6 模块本身因为它不是对象。 由于 ES6 模块是编译时加载使得静态分析成为可能。有了它就能进一步拓宽 JavaScript 的语法比如引入宏macro和类型检验type system这些只能靠静态分析实现的功能。 除了静态加载带来的各种好处ES6 模块还有以下好处。 不再需要 UMD 模块格式了将来服务器和浏览器都会支持 ES6 模块格式。目前通过各种工具库其实已经做到了这一点。 将来浏览器的新 API 就能用模块格式提供不再必须做成全局变量或者 navigator 对象的属性。 不再需要对象作为命名空间比如 Math 对象未来这些功能可以通过模块提供
2. 严格模式
ES6 的模块自动采用严格模式不管你有没有在模块头部加上 use strict; 。 严格模式主要有以下限制。 变量必须声明后再使用 函数的参数不能有同名属性否则报错 不能使用 with 语句 不能对只读属性赋值否则报错 不能使用前缀 0 表示八进制数否则报错 不能删除不可删除的属性否则报错 不能删除变量 delete prop 会报错只能删除属性 delete global[prop] eval 不会在它的外层作用域引入变量 eval 和 arguments 不能被重新赋值 arguments 不会自动反映函数参数的变化 不能使用 arguments.callee 不能使用 arguments.caller 禁止 this 指向全局对象 不能使用 fn.caller 和 fn.arguments 获取函数调用的堆栈 增加了保留字比如 protected 、 static 和 interface 上面这些限制模块都必须遵守。由于严格模式是 ES5 引入的不属于 ES6所以请参阅相关 ES5 书籍本书不再详细介绍了。 其中尤其需要注意 this 的限制。ES6 模块之中顶层的 this 指向 undefined 即不应该在顶层代码使用 this 。
3. export 命令
模块功能主要由两个命令构成 export 和 import 。 export 命令用于规定模块的对外接口 import 命令用于输入其他模块提供的功能。 一个模块就是一个独立的文件。该文件内部的所有变量外部无法获取。如果你希望外部能够读取模块内部的某个变量就必须使用 export 关键字输出该 变量。下面是一个 JS 文件里面使用 export 命令输出变量。
// profile.js
export var firstName Michael;
export var lastName Jackson;
export var year 1958;
上面代码是 profile.js 文件保存了用户信息。ES6 将其视为一个模块里面用 export 命令对外部输出了三个变量。 export 的写法除了像上面这样还有另外一种。
// profile.js
var firstName Michael;
var lastName Jackson;
var year 1958;
export {firstName, lastName, year};
上面代码在 export 命令后面使用大括号指定所要输出的一组变量。它与前一种写法直接放置在 var 语句前是等价的但是应该优先考虑使用这种写 法。因为这样就可以在脚本尾部一眼看清楚输出了哪些变量。 export 命令除了输出变量还可以输出函数或类class。
export function multiply(x, y) {
return x * y;
};
上面代码对外输出一个函数 multiply 。 通常情况下 export 输出的变量就是本来的名字但是可以使用 as 关键字重命名。
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
上面代码使用 as 关键字重命名了函数 v1 和 v2 的对外接口。重命名后 v2 可以用不同的名字输出两次。 需要特别注意的是 export 命令规定的是对外的接口必须与模块内部的变量建立一一对应关系。
// 报错
export 1;
// 报错
var m 1;
export m;
上面两种写法都会报错因为没有提供对外的接口。第一种写法直接输出 1第二种写法通过变量 m 还是直接输出 1。 1 只是一个值不是接口。正确的 写法是下面这样
// 写法一
export var m 1;
// 写法二
var m 1;
export {m};
// 写法三
var n 1;
export {n as m};
上面三种写法都是正确的规定了对外的接口 m 。其他脚本可以通过这个接口取到值 1 。它们的实质是在接口名与模块内部变量之间建立了一一对应 的关系。 同样的 function 和 class 的输出也必须遵守这样的写法
// 报错
function f() {}
export f;
// 正确
export function f() {};
// 正确
function f() {}
export {f};
另外 export 语句输出的接口与其对应的值是动态绑定关系即通过该接口可以取到模块内部实时的值
export var foo bar;
setTimeout(() foo baz, 500)
上面代码输出变量 foo 值为 bar 500 毫秒之后变成 baz 。 这一点与 CommonJS 规范完全不同。CommonJS 模块输出的是值的缓存不存在动态更新详见下文《Module 的加载实现》一节。 最后 export 命令可以出现在模块的任何位置只要处于模块顶层就可以。如果处于块级作用域内就会报错下一节的 import 命令也是如此。这是因 为处于条件代码块之中就没法做静态优化了违背了 ES6 模块的设计初衷
function foo() {
export default bar // SyntaxError
}
foo()
上面代码中 export 语句放在函数之中结果报错。
4. import 命令
使用 export 命令定义了模块的对外接口以后其他 JS 文件就可以通过 import 命令加载这个模块。
// main.js
import {firstName, lastName, year} from ./profile;
function setName(element) {
element.textContent firstName lastName;
}
上面代码的 import 命令用于加载 profile.js 文件并从中输入变量。 import 命令接受一对大括号里面指定要从其他模块导入的变量名。大括号里面 的变量名必须与被导入模块 profile.js 对外接口的名称相同。 如果想为输入的变量重新取一个名字 import 命令要使用 as 关键字将输入的变量重命名。
import { lastName as surname } from ./profile;
import 后面的 from 指定模块文件的位置可以是相对路径也可以是绝对路径 .js 后缀可以省略。如果只是模块名不带有路径那么必须有配置文 件告诉 JavaScript 引擎该模块的位置。
import {myMethod} from util
上面代码中 util 是模块文件名由于不带有路径必须通过配置告诉引擎怎么取到这个模块。 注意 import 命令具有提升效果会提升到整个模块的头部首先执行。
foo();
import { foo } from my_module;
上面的代码不会报错因为 import 的执行早于 foo 的调用。这种行为的本质是 import 命令是编译阶段执行的在代码运行之前。 由于 import 是静态执行所以不能使用表达式和变量这些只有在运行时才能得到结果的语法结构。
// 报错
import { f oo } from my_module;
// 报错
let module my_module;
import { foo } from module;
// 报错
if (x 1) {
import { foo } from module1;
} else {
import { foo } from module2;
}
上面三种写法都会报错因为它们用到了表达式、变量和 if 结构。在静态分析阶段这些语法都是没法得到值的。 最后 import 语句会执行所加载的模块因此可以有下面的写法。
import lodash;
上面代码仅仅执行 lodash 模块但是不输入任何值。 如果多次重复执行同一句 import 语句那么只会执行一次而不会执行多次
import lodash;
import lodash;
上面代码加载了两次 lodash 但是只会执行一次。
import { foo } from my_module;
import { bar } from my_module;
// 等同于
import { foo, bar } from my_module;
上面代码中虽然 foo 和 bar 在两个语句中加载但是它们对应的是同一个 my_module 实例。也就是说 import 语句是 Singleton 模式。 目前阶段通过 Babel 转码CommonJS 模块的 require 命令和 ES6 模块的 import 命令可以写在同一个模块里面但是最好不要这样做。因为 import 在静态解析阶段执行所以它是一个模块之中最早执行的。下面的代码可能不会得到预期结果。
require(core-js/modules/es6.symbol);
require(core-js/modules/es6.promise);
import React from React;
5. 模块的整体加载
除了指定加载某个输出值还可以使用整体加载即用星号 * 指定一个对象所有输出值都加载在这个对象上面。 下面是一个 circle.js 文件它输出两个方法 area 和 circumference 。
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
现在加载这个模块
// main.js
import { area, circumference } from ./circle;
console.log(圆面积 area(4));
console.log(圆周长 circumference(14));
上面写法是逐一指定要加载的方法整体加载的写法如下。
import * as circle from ./circle;
console.log(圆面积 circle.area(4));
console.log(圆周长 circle.circumference(14));
注意模块整体加载所在的那个对象上例是 circle 应该是可以静态分析的所以不允许运行时改变。下面的写法都是不允许的
import * as circle from ./circle;
// 下面两行都是不允许的
circle.foo hello;
circle.area function () {}
6. export default 命令
从前面的例子可以看出使用 import 命令的时候用户需要知道所要加载的变量名或函数名否则无法加载。但是用户肯定希望快速上手未必愿意阅 读文档去了解模块有哪些属性和方法。 为了给用户提供方便让他们不用阅读文档就能加载模块就要用到 export default 命令为模块指定默认输出
// export-default.js
export default function () {
console.log(foo);
}
上面代码是一个模块文件 export-default.js 它的默认输出是一个函数。 其他模块加载该模块时 import 命令可以为该匿名函数指定任意名字。
// import-default.js
import customName from ./export-default;
customName(); // foo
上面代码的 import 命令可以用任意名称指向 export-default.js 输出的方法这时就不需要知道原模块输出的函数名。需要注意的是这时 import 命 令后面不使用大括号。 export default 命令用在非匿名函数前也是可以的
// export-default.js
export default function foo() {
console.log(foo);
}
// 或者写成
function foo() {
console.log(foo);
}
export default foo;
上面代码中 foo 函数的函数名 foo 在模块外部是无效的。加载的时候视同匿名函数加载。 下面比较一下默认输出和正常输出
// 第一组
export default function crc32() { // 输出
// ...
}
import crc32 from crc32; // 输入
// 第二组
export function crc32() { // 输出
// ...
};
import {crc32} from crc32; // 输入
上面代码的两组写法第一组是使用 export default 时对应的 import 语句不需要使用大括号第二组是不使用 export default 时对应的 import 语 句需要使用大括号。 export default 命令用于指定模块的默认输出。显然一个模块只能有一个默认输出因此 export default 命令只能使用一次。所以 import 命令后面 才不用加大括号因为只可能对应一个方法。 本质上 export default 就是输出一个叫做 default 的变量或方法然后系统允许你为它取任意名字。所以下面的写法是有效的
// modules.js
function add(x, y) {
return x * y;
}
export {add as default};
// 等同于
// export default add;
// app.js
import { default as foo } from modules;
// 等同于
// import foo from modules;
正是因为 export default 命令其实只是输出一个叫做 default 的变量所以它后面不能跟变量声明语句
// 正确
export var a 1;
// 正确
var a 1;
export default a;
// 错误
export default var a 1;
上面代码中 export default a 的含义是将变量 a 的值赋给变量 default 。所以最后一种写法会报错。 同样地因为 export default 本质是将该命令后面的值赋给 default 变量以后再默认所以直接将一个值写在 export default 之后
// 正确
export default 42;
// 报错
export 42;
上面代码中后一句报错是因为没有指定对外的接口而前一句指定外对接口为 default 。 有了 export default 命令输入模块时就非常直观了以输入 lodash 模块为例。
import _ from lodash
如果想在一条 import 语句中同时输入默认方法和其他接口可以写成下面这样
import _, { each, each as forEach } from lodash
对应上面代码的 export 语句如下。
export default function (obj) {
// ···
}
export function each(obj, iterator, context) {
// ···
}
export { each as forEach };
上面代码的最后一行的意思是暴露出 forEach 接口默认指向 each 接口即 forEach 和 each 指向同一个方法。 export default 也可以用来输出类。
// MyClass.js
export default class { ... }
// main.js
import MyClass from MyClass;
let o new MyClass();
7. export 与 import 的复合写法
如果在一个模块之中先输入后输出同一个模块 import 语句可以与 export 语句写在一起。
export { foo, bar } from my_module;
import { foo, bar } from my_module;
export { foo, bar };
上面代码中 export 和 import 语句可以结合在一起写成一行。 模块的接口改名和整体输出也可以采用这种写法。
// 接口改名
export { foo as myFoo } from my_module;
// 整体输出
export * from my_module
默认接口的写法如下。
export { default } from foo;
具名接口改为默认接口的写法如下
export { es6 as default } from ./someModule;
// 等同于
import { es6 } from ./someModule;
export default es6;
同样地默认接口也可以改名为具名接口
export { default as es6 } from ./someModule;
下面三种 import 语句没有对应的复合写法。
import * as someIdentifier from someModule;
import someIdentifier from someModule;
import someIdentifier, { namedIdentifier } from someModule;
为了做到形式的对称现在有提案提出补上这三种复合写法
export * as someIdentifier from someModule;
export someIdentifier from someModule;
export someIdentifier, { namedIdentifier } from someModule;
8. 模块的继承
模块之间也可以继承。 假设有一个 circleplus 模块继承了 circle 模块。
// circleplus.js
export * from circle;
export var e 2.71828182846;
export default function(x) {
return Math.exp(x);
}
上面代码中的 export * 表示再输出 circle 模块的所有属性和方法。注意 export * 命令会忽略 circle 模块的 default 方法。然后上面代码又输出 了自定义的 e 变量和默认方法。 这时也可以将 circle 的属性或方法改名后再输出。
// circleplus.js
export { area as circleArea } from circle;
上面代码表示只输出 circle 模块的 area 方法且将其改名为 circleArea 。 加载上面模块的写法如下。
// main.js
import * as math from circleplus;
import exp from circleplus;
console.log(exp(math.e));
上面代码中的 import exp 表示将 circleplus 模块的默认方法加载为 exp 方法。
9. 跨模块常量 const 声明的常量只在当前代码块有效。如果想设置跨模块的常量即跨多个文件或者说一个值要被多个模块共 享可以采用下面的写法
// constants.js 模块
export const A 1;
export const B 3;
export const C 4;
// test1.js 模块
import * as constants from ./constants;
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模块
import {A, B} from ./constants;
console.log(A); // 1
console.log(B); // 3
如果要使用的常量非常多可以建一个专门的 constants 目录将各种常量写在不同的文件里面保存在该目录下。
// constants/db.js
export const db {
url: http://my.couchdbserver.local:5984,
admin_username: admin,
admin_password: admin password
};
// constants/user.js
export const users [root, admin, staff, ceo, chief, moderator];
然后将这些文件输出的常量合并在 index.js 里面。
// constants/index.js
export {db} from ./db;
export {users} from ./users;
使用的时候直接加载 index.js 就可以了
// script.js
import {db, users} from ./index
10. import()
10.1简介
前面介绍过 import 命令会被 JavaScript 引擎静态分析先于模块内的其他模块执行叫做”连接“更合适。所以下面的代码会报错
// 报错
if (x 2) {
import MyModual from ./myModual;
}
上面代码中引擎处理 import 语句是在编译时这时不会去分析或执行 if 语句所以 import 语句放在 if 代码块之中毫无意义因此会报句法错误而 不是执行时错误。也就是说 import 和 export 命令只能在模块的顶层不能在代码块之中比如在 if 代码块之中或在函数之中。 这样的设计固然有利于编译器提高效率但也导致无法在运行时加载模块。在语法上条件加载就不可能实现。如果 import 命令要取代 Node 的 require 方法这就形成了一个障碍。因为 require 是运行时加载模块 import 命令无法取代 require 的动态加载功能。
const path ./ fileName;
const myModual require(path);
上面的语句就是动态加载 require 到底加载哪一个模块只有运行时才知道。 import 语句做不到这一点。 因此有一个提案建议引入 import() 函数完成动态加载。
import(specifier)
上面代码中 import 函数的参数 specifier 指定所要加载的模块的位置。 import 命令能够接受什么参数 import() 函数就能接受什么参数两者区别 主要是后者为动态加载。 import() 返回一个 Promise 对象。下面是一个例子
const main document.querySelector(main);
import(./section-modules/${someVariable}.js)
.then(module {
module.loadPageInto(main);
})
.catch(err {
main.textContent err.message;
});
import() 函数可以用在任何地方不仅仅是模块非模块的脚本也可以使用。它是运行时执行也就是说什么时候运行到这一句也会加载指定的模 块。另外 import() 函数与所加载的模块没有静态连接关系这点也是与 import 语句不相同。 import() 类似于 Node 的 require 方法区别主要是前者是异步加载后者是同步加载。
11适用场合
下面是 import() 的一些适用场合。
11.1按需加载
import() 可以在需要的时候再加载某个模块
button.addEventListener(click, event {
import(./dialogBox.js)
.then(dialogBox {
dialogBox.open();
})
.catch(error {
/* Error handling */
})
});
上面代码中 import() 方法放在 click 事件的监听函数之中只有用户点击了按钮才会加载这个模块。
11.2条件加载
import() 可以放在 if 代码块根据不同的情况加载不同的模块。
if (condition) {
import(moduleA).then(...);
} else {
import(moduleB).then(...);
}
上面代码中如果满足条件就加载模块 A否则加载模块 B。
11.3动态的模块路径
import() 允许模块路径动态生成。
import(f())
.then(...);
上面代码中根据函数 f 的返回结果加载不同的模块。
11.4注意点
import() 加载模块成功以后这个模块会作为一个对象当作 then 方法的参数。因此可以使用对象解构赋值的语法获取输出接口。
import(./myModule.js)
.then(({export1, export2}) {
// ...·
});
上面代码中 export1 和 export2 都是 myModule.js 的输出接口可以解构获得。 如果模块有 default 输出接口可以用参数直接获得。
import(./myModule.js)
.then(myModule {
console.log(myModule.default);
});
上面的代码也可以使用具名输入的形式。
import(./myModule.js)
.then(({default: theDefault}) {
console.log(theDefault);
});
如果想同时加载多个模块可以采用下面的写法。
Promise.all([
import(./module1.js),
import(./module2.js),
import(./module3.js),
])
.then(([module1, module2, module3]) {
···
});
import() 也可以用在 async 函数之中
async function main() {
const myModule await import(./myModule.js);
const {export1, export2} await import(./myModule.js);
const [module1, module2, module3]
await Promise.all([
import(./module1.js),
import(./module2.js),
import(./module3.js),
]);
}
main()
总结
本博客源于本人阅读相关书籍和视频总结创作不易谢谢点赞支持。学到就是赚到。我是歌谣励志成为一名优秀的技术革新人员。
欢迎私信交流一起学习一起成长。
推荐链接 其他文件目录参照
“睡服“面试官系列之各系列目录汇总建议学习收藏