Sintaxis del módulo
1. Introducción
Antes de ES6, había principalmente dos tipos de CommonJS
y AMD
. CommonJS para el servidor, AMD para el navegador. ES6 implementa funciones de módulo a nivel de estándares de lenguaje, y la implementación es bastante simple.Puede reemplazar completamente las especificaciones de CommonJS y AMD y convertirse en una solución de módulo común para navegadores y servidores. Entró en la gran etapa de unificación.
La idea de diseño de los módulos ES6 es ser lo más estáticos posible, haciendotiempo de compilaciónPuede determinar las dependencias del módulo, así como las variables de entrada y salida. Los módulos CommonJS y AMD solo se pueden usar entiempo de ejecuciónAsegúrate de estas cosas.
// CommonJS模块
let {
stat, exists, readfile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
Este tipo de carga se denomina "carga en tiempo de ejecución", porque este objeto solo se puede obtener en tiempo de ejecución, por lo que no se puede realizar una "optimización estática" en tiempo de compilación.
export
Los módulos ES6 no son objetos, sino códigos que especifican explícitamente la salida a través de comandos y luego import
la entrada a través de comandos.
// ES6模块
import {
stat, exists, readFile } from 'fs';
La esencia del código anterior es cargar 3 métodos desde el módulo fs, y otros métodos no se cargan. Esta carga se denomina "carga en tiempo de compilación" o carga estática. Es decir, ES6 puede completar la carga del módulo en tiempo de compilación, lo que es más eficiente que la carga del módulo CommonJS. Por supuesto, esto también hace que sea imposible hacer referencia al propio módulo ES6, ya que no es un objeto.
2. Modo estricto
Los módulos ES6 adoptan automáticamente el modo estricto, ya sea que lo agregue al encabezado del módulo o no "use strict"
.
El modo estricto tiene principalmente las siguientes restricciones.
- Las variables deben declararse antes de que puedan usarse
- Los parámetros de la función no pueden tener atributos con el mismo nombre, de lo contrario se reportará un error
- no se puede usar la sentencia with
- No puede asignar un valor a un atributo de solo lectura; de lo contrario, se informará un error
- Los atributos que no se pueden eliminar no se pueden eliminar; de lo contrario, se informará un error
- Las variables no se pueden eliminar
delete prop
, se informará un error, solo se pueden eliminar los atributosdelete global[prop]
eval
No introduce variables en su ámbito exterior.eval
yarguments
no se puede reasignararguments
No refleja automáticamente los cambios en los parámetros de la función- Fuera de servicio
arguments.callee
- Fuera de servicio
arguments.caller
- No
this
apuntes al objeto global. - No se puede usar
fn.caller
yfn.arguments
obtener la pila de una llamada de función - Se agregaron palabras reservadas (como
protected
,static
yinterface
)
3. comando de exportación
La función del módulo consta principalmente de dos comandos: export
y import
. export
El comando se usa para especificar la interfaz externa del módulo, y import
el comando se usa para ingresar las funciones proporcionadas por otros módulos.
Un módulo es un solo archivo. Todas las variables dentro del archivo no se pueden obtener desde el exterior. Si desea que el exterior pueda leer una variable dentro del módulo, debe usar export
la palabra clave para generar la variable. A continuación se muestra un archivo JS que utiliza export
variables de salida de comandos.
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
// 另一种写法
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {
firstName, lastName, year };
export
Además de generar variables, los comandos también pueden generar funciones o clases.
export function multiply(x, y) {
return x * y;
};
El código anterior genera una función externamente multiply
.
Normalmente, export
la variable de salida es el nombre original, pero se puede as
renombrar usando palabras clave.
function v1() {
... }
function v2() {
... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
Lo que necesita especial atención es que export
el comando especifica la interfaz externa, que debe establecer una correspondencia uno a uno con las variables dentro del módulo.
// 报错
export 1;
// 报错
var m = 1;
export m;
Las dos formas de escritura anteriores informarán un error, porque no se proporciona una interfaz externa. La primera forma de escritura se emite directamente 1
, y la segunda forma de escritura m
se emite directamente a través de variables 1
. 1
Solo un valor, no una interfaz. La forma correcta de escribir es la siguiente.
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {
m};
// 写法三
var n = 1;
export {
n as m};
Los tres métodos de escritura anteriores son correctos y especifican la interfaz externa m. Otros scripts pueden obtener el valor 1 a través de esta interfaz. Su esencia es establecer una correspondencia uno a uno entre el nombre de la interfaz y las variables internas del módulo.
Del mismo modo, la salida de function
y class
también debe seguir esta forma de escritura.
// 报错
function f() {
}
export f;
// 正确
export function f() {
};
// 正确
function f() {
}
export {
f};
4. comando de importación
Después de usar export
el comando para definir la interfaz externa del módulo, otros archivos JS pueden import
cargar este módulo a través del comando.
// main.js
import {
firstName, lastName, year } from './profile.js';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
Si desea cambiar el nombre de la variable de entrada, import
el comando debe usar as
palabras clave para cambiar el nombre de la variable de entrada.
import {
lastName as surname } from './profile.js';
import
Las variables ingresadas por el comando son todas de solo lectura, porque su esencia es una interfaz de entrada. En otras palabras, no está permitido reescribir la interfaz en el script que carga el módulo.
import {
a} from './xxx.js'
a = {
}; // Syntax Error : 'a' is read-only;
En el código anterior, el script carga la variable a
y, si se reasigna, se informará un error, porque a es una interfaz de solo lectura. Sin embargo, si a
es un objeto, a
se permiten propiedades anuladas.
import {
a} from './xxx.js'
a.foo = 'hello'; // 合法操作
5. Carga total de módulos
Además de especificar cargar un valor de salida determinado, también puede usar la carga general, es decir, usar un asterisco (*) para especificar un objeto, y todos los valores de salida se cargan en este objeto.
A continuación se muestra un archivo circle.js que exporta dos métodos, área y circunferencia.
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
Ahora, cargue este módulo.
// main.js
import {
area, circumference } from './circle';
console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));
El método anterior es para especificar los métodos que se cargarán uno por uno, y el método de carga general es el siguiente.
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
Tenga en cuenta que el objeto donde se carga el módulo como un todo (círculo en el ejemplo anterior) debe ser analizable estáticamente, por lo que no se permiten cambios en el tiempo de ejecución. La siguiente escritura no está permitida.
import * as circle from './circle';
// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {
};
6. exportar comando predeterminado
Al usar import
el comando, el usuario necesita saber el nombre de la variable o función a cargar, de lo contrario no se puede cargar. Sin embargo, los usuarios definitivamente quieren comenzar rápidamente y es posible que no estén dispuestos a leer la documentación. Entonces, por conveniencia, el comando se usa para export default
especificar la salida predeterminada para el módulo.
// export-default.js
export default function () {
console.log('foo');
}
El código anterior es un archivo de módulo export-default.js
y su salida predeterminada es una función.
Cuando otros módulos cargan este módulo, import
el comando puede especificar cualquier nombre para la función anónima.
// import-default.js
import customName from './export-default';
customName(); // 'foo'
El comando del código anterior import
puede usar cualquier nombre para apuntar al export-default.js
método de salida. En este momento, no es necesario conocer el nombre de la función de salida del módulo original. Cabe señalar que import
las llaves no se utilizan después del comando en este momento.
export default
También es posible usar comandos antes de funciones no anónimas.
// export-default.js
export default function foo() {
console.log('foo');
}
// 或者写成
function foo() {
console.log('foo');
}
export default foo;
En el código anterior, foo
el nombre de función de la función foo
no es válido fuera del módulo. Al cargar, se considera como una carga de función anónima.
// 第一组
export default function crc32() {
// 输出
// ...
}
import crc32 from 'crc32'; // 输入
// 第二组
export function crc32() {
// 输出
// ...
};
import {
crc32} from 'crc32'; // 输入
Los dos grupos de códigos anteriores están escritos, el primer grupo se usa export default
, la declaración correspondiente import
no necesita usar llaves; el segundo grupo no se usa export default
, la declaración correspondiente import
necesita usar llaves.
El comando exportar predeterminado se usa para especificar la salida predeterminada del módulo. Obviamente, un módulo solo puede tener una salida predeterminada, por lo que el comando exportar predeterminado solo se puede usar una vez. Por lo tanto, no es necesario usar llaves después del comando de importación, ya que solo puede corresponder al comando predeterminado de exportación.
En esencia, export default
es exportar una default
variable o método llamado , y luego el sistema te permite darle cualquier nombre. Por lo tanto, el siguiente escrito es válido.
// 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';
7. Escritura compuesta de exportación e importación
Si en un módulo, el mismo módulo se ingresa primero y luego se emite, import
la declaración se puede export
escribir junto con la declaración.
export {
foo, bar } from 'my_module';
// 可以简单理解为
import {
foo, bar } from 'my_module';
export {
foo, bar };
En el código anterior, la instrucción export
AND import
se puede combinar y escribir en una sola línea. Sin embargo, debe tenerse en cuenta que después de escribirse en una línea, foo
y bar
en realidad no se importa al módulo actual, sino que es equivalente a reenviar estas dos interfaces externamente, de modo que el módulo actual no puede usar directamente foo
y bar
.
El cambio de nombre de la interfaz y la salida general del módulo también se pueden escribir de esta manera.
// 接口改名
export {
foo as myFoo } from 'my_module';
// 整体输出
export * from 'my_module';
La interfaz predeterminada se escribe de la siguiente manera.
export {
default } from 'foo';
El método de escritura para cambiar la interfaz con nombre a la interfaz predeterminada es el siguiente.
export {
es6 as default } from './someModule';
// 等同于
import {
es6 } from './someModule';
export default es6;
Del mismo modo, la interfaz predeterminada también se puede renombrar a una interfaz con nombre.
export {
default as es6 } from './someModule';
Antes de ES2020, había una declaración de importación, pero no había un método de escritura compuesto correspondiente.
import * as someIdentifier from "someModule";
ES2020 agregó esta forma de escribir.
export * as ns from "mod";
// 等同于
import * as ns from "mod";
export {
ns};