[web]ES6学习笔记(二)

来源:互联网 发布:电视直播软件电脑版 编辑:程序博客网 时间:2024/06/08 14:15

一.module语法

1.概述

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

// ES6模块import { stat, exists, readFile } from 'fs';

上面代码的实质是从fs模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

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)

其中,尤其需要注意this的限制。ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。

3.export命令

模块的功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块输入的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。

// profile.jsexport var firstName = 'Michael';export var lastName = 'Jackson';export var year = 1958;

或者

// profile.jsvar firstName = 'Michael';var lastName = 'Jackson';var year = 1958;export {firstName, lastName, year};

export除了输出变量,还可输出函数或类

export function multiply(x, y) {  return x * y;};

上面代码对外输出一个函数multiply

4.import命令

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

// main.jsimport {firstName, lastName, year} from './profile';function setName(element) {  element.textContent = firstName + ' ' + lastName;}

上面代码的import命令,用于加载profile.js文件,并从中输入变量。import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。

由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。(一般人也不会这么干吧…….)

5.模块的整体加载

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。
下面是一个circle.js文件,它输出两个方法area和circumference。

// circle.jsexport function area(radius) {  return Math.PI * radius * radius;}export function circumference(radius) {  return 2 * Math.PI * radius;}

现在加载这个模块:

// main.jsimport { 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与import的复合写法

如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。

export { foo, bar } from 'my_module';// 等同于import { foo, bar } from 'my_module';export { foo, bar };

7.跨模块常量

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); // 1console.log(constants.B); // 3// test2.js 模块import {A, B} from './constants';console.log(A); // 1console.log(B); // 3

8.import()

前面介绍过,import命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行(叫做”连接“更合适)。所以,下面的代码会报错。

// 报错if (x === 2) {  import MyModual from './myModual';}

上面代码中,引擎处理import语句是在编译时,这时不会去分析或执行if语句,所以import语句放在if代码块之中毫无意义,因此会报句法错误,而不是执行时错误。也就是说,import和export命令只能在模块的顶层,不能在代码块之中(比如,在if代码块之中,或在函数之中)。

这样的设计,固然有利于编译器提高效率,但也导致无法在运行时加载模块。从语法上,条件加载就不可能实现。如果import命令要取代 Node 的require方法,这就形成了一个障碍。因为require是运行时加载模块,import命令无法取代require的动态加载功能。

因此,有一个提案,建议引入import()函数,完成动态加载。

import(specifier)

上面代码中,import函数的参数specifier,指定所要加载的模块的位置。import命令能够接受什么参数,import()函数就能接受什么参数,两者区别主要是后者为动态加载。

const main = document.querySelector('main');import(`./section-modules/${someVariable}.js`)  .then(module => {    module.loadPageInto(main);  })  .catch(err => {    main.textContent = err.message;  });

import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载

参考网址:
http://es6.ruanyifeng.com/#docs/module-loader

原创粉丝点击