ES6的export和import

news/2024/7/10 22:42:41 标签: es6, javascript, 前端

ES6中的模块加载

ES6 模块是编译时加载,编译时就能确定模块的依赖关系,以及输入和输出的变量,相比于CommonJS 和 AMD 模块都只能在运行时确定输入输出变量的加载效率要高。

严格模式

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

export 命令

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

如果你没引入变量,即使你引入执行了该模块你仍然无法获取模块的变量。

javascript">javascript">// a.js
let str = 'aaaa'

//b.js
import './a.js'     //相当于执行了 a.js 文件代码
console.log(str);   //报错  str is not defined

export命令可以出现在模块的任何位置,但必须处于模块顶层,如果处于块级作用域内,就会报错(即不能包含在任何代码块中,比如不能在函数体内)。

export 命令有多种写法,下面将逐一介绍:

export 后面直接加声明语句(export var a = '')

javascript">javascript">// a.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
export function fn(x, y) {
return x * y;
};

// 此时的导入import语法
import {firstName, lastName, year} from './a.js'

使用大括号指定输出(export {})

这是推荐使用的输出方式,因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。

javascript">javascript">// a.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };

// 此时的导入语法
import {firstName, lastName, year} from './a.js'

在export命令后面,使用大括号指定所要输出的一组变量与 export 直接加声明语句是等价的,但是应该优先考虑使用这种写法。

as关键字重命名(export {a as newName})

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。as 关键字后面的变量名是输出的名字。

javascript">javascript">let str = 'aaa'
function v1() { ... }
function v2() { ... }
export {
    str as str2,
    v1 as streamV1,
    v2 as streamV2,
    v2 as streamLatestVersion
};

// 此时的导入语法
// 此时不能用之前的名字导入,只能用重命名后的名字导入
import {str2, streamV1, streamV2, streamLatestVersion} from "./index";

使用重命名,同一个变量可以用不同的名字输出两次,这样在外部可以用不同的名字引入该变量。

export default

使用前面的语法进行输出时,输入的用户必须得知道输出的变量名才能使用,有时这并不怎么方便。使用export default命令可以为模块指定默认输出,用户可以为输入的变量起一个任意的名字,就不需要提前知道输出的变量名便可上手使用了。

使用 default 语法,后面可以直接跟变量名,这点跟其他输出语法不一样。

javascript">javascript">// a.js 输出
export default function foo() {
  console.log('foo');
}
//或者写成下面两种都可以
export default function() {
  console.log('foo');
}
function foo() {
  console.log('foo')
}
export default foo

// 此时的导入语法
import fn from './export-default';
fn();

上面代码的import命令,可以用任意名称指向a.js输出的方法,这时就不需要知道原模块输出的函数名。default 命令后面的变量名在模块外部是无效的,同匿名函数输出的形式一致。

注意,使用 export default 命令输出的模块,import命令后面不需要大括号。一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。

可以用 default 导出一个对象来导出多个值:

javascript">javascript">//a.js
export default {
  a() {
    console.log('aaa')
  },
  b() {
    console.log('bbb')
  },
  c: 'ccc',
}

//此时的导入语法
import obj from './a.js'
obj.a() //aaa
console.log(obj.c) //ccc
//注意,此时不能用大括号导入,下面会报错
import { a, b, c } from './a.js' //报错

export 的错误语法

export命令不能直接输出变量,因为变量的值必须在运行阶段才能确定,而 export 命令的输出是在编译阶段就已经输出。

javascript">javascript">// 报错
export 1;
// 报错
var m = 1;
export m;
// 报错
function f() {}
export f;

export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。

  • export 1 ,这里输出的是一个值 1,没有与任何模块内部变量建立联系,所以直接报错。

  • var m = 1; export m; 这里看起来就像是输出了一个变量m作为对外的接口,我们可能会认为 m 这个变量被输出到模块外被使用,并且与模块内的 m 产生了引用的关系。然而现实情况是,变量m在模块中作为一个变量存在,但是通过export导出m时,被导出的只有m的值 1,所以同样不与内部变量产生联系,于是报错。

这跟函数的传参是一个道理:

javascript">javascript">let x = 1;                 //声明变量
const foo= (x) => { x=2 };    //声明一个函数
foo(x)                   //传入x
console.log(x)           //    1

上面代码中,变量 x 作为 foo 的参数,只把变量 x 的值传入 foo,x 只作为数值的载体,函数内部 x 并没有与变量 x 产生直接联系。只是复制了变量 x 的值(这种复制值然后再使用的形式与CommonJS加载模式类似)。

export default 的错误语法

export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句,与export有点相反的意思。

javascript">javascript">// 错误
export default var a = 1;

同样地,因为export default命令的本质是将后面的值,赋给default变量,所以可以直接将一个值写在export default之后。

javascript">javascript">// 正确
export default 42;
// 报错
export 42;

export 中 default 和其他输出结合使用

export 中 default 是可以和其他输出结合使用的

javascript">javascript">// a.js 输出  下面将输出三个函数
export default function (obj) {
}
export function each(obj, iterator, context) {
}
export { each as forEach };
// b.js 输入  这里可以分别输入三个值
import  aaa, { each, forEach } from 'a.js';

import 命令

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

import命令输入的变量都是只读的,不可以在加载后修改引入的变量。但如果引入的变量是对象的话,可以修改对象的属性,但非常不推荐使用。

javascript">javascript">import {obj} from './a.js'
obj = {}; // Syntax Error : 'obj' is read-only;

上面代码中,对引入变量重新赋值就会报错。但是,如果a是一个对象,改写a的属性是允许的,但非常不推荐修改引入的变量,因为其他引入的模块也可以读到改写后的值,这种写法很难查错,所以凡是输入的变量,都当作完全只读,轻易不要改变它的属性。

import命令具有提升效果,会提升到整个模块的头部,首先执行,因为 import 命令是编译阶段执行的,在代码运行之前。

javascript">javascript">foo();
import { foo } from 'my_module';       //这里不会报错,因为import的执行早于foo的调用

import语句会执行所加载的模块,因此可以有下面的写法。如果多次重复执行同一句import语句,那么只会执行一次模块的文件代码,而不会执行多次。因为import最终会被转换为node的require,而require会对引入文件缓存。(更多请查看这两篇文章node模块化 和 require原理require与import的区别

javascript">javascript">import 'a.js';//这里仅仅执行lodash模块,但是不输入任何值,所以并不能使用 a.js 里面定义的变量
//下面代码加载了两次lodash,但是只会执行一次。
import 'lodash';
import 'lodash';
import { foo } from 'my_module';
import { bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';

目前阶段,通过 Babel 转码,require命令和import命令可以写在同一个模块里面,但是最好不要这样做。因为import在静态解析阶段执行,所以它是一个模块之中最早执行的。

javascript">javascript">//下面的代码可能不会得到预期结果,因为import将会于require之前执行
require('core-js/modules/es6.symbol');
require('core-js/modules/es6.promise');
import React from 'React';

import 的语法有多种,下面将逐一介绍:

import {} from 'xxx.js'

javascript">javascript">// a.js 导出
var firstName = 'Michael';
var lastName = 'Jackson';
export { firstName, lastName};

// b.js 引入
import { firstName, lastName} from './a.js';
//引入后可以直接使用
console.log(firstName,lastName)

import {a as newName} from 'xxx.js'

如果想为输入的变量重新取一个名字可以使用as关键字,将输入的变量重命名。as 关键字后面的是输入的变量名,即你想使用的名字

javascript">javascript">import { firstName as newName } from './a.js';

import * as newName from 'xxx.js'(模块的整体加载)

除了指定加载某个输出值,还可以使用整体加载,即加载模块的整个输出对象。用星号(*)指定一个对象,所有输出值都将加载在这个对象上面。

javascript">javascript">// a.js
export var area = 'aaa'
export function circumference(radius) {
return 2 * Math.PI * radius;
}

//b.js
import * as newObj from './circle';
console.log(newObj .area);
console.log(newObj .circumference(14));

不允许修改整体加载的对象

javascript">javascript">import * as circle from './circle';

// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {};

import 的错误语法

import不能使用表达式和变量,因为 import 是静态执行的,即编译阶段执行,而这些语法只有在运行时才能得到结果。

javascript">javascript">// 报错
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';
}

export 和 import 的复合写法

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

javascript">javascript">export { foo, bar } from 'my_module';

// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };

但需要注意的是,写成一行以后,foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo和bar。

其他的复合写法:

javascript">javascript">// 接口改名输出
export { foo as myFoo } from 'my_module';
// 等同于
import {foo} from 'mu_module'
export {foo as myFoo}

// 整体输出
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';

http://www.niftyadmin.cn/n/133743.html

相关文章

项目执行差,你应该如何推进解决?(万千项目)

在日常工作中,项目成员可能存在以下问题:1、沟通能力不足。团队成员之间不主动反馈沟通导致问题堆积影响项目进度;2、执行力不足。成员推一下动一下,不主动积极执行工作任务;3、技术能力不不足。一写代码全是bug&#…

unity 实现使用三张图片来表达车速,通过传值达到车速

//速度 public Image SpeedNums_Unit; public Image SpeedNums_Ten; //public Image SpeedNums_Hundred; //kw public Image MileageNums_Unit; public Image MileageNums_Ten; /// /// 仪表速度UI /// private void SpeedUI(string speedStr) {if (SpeedNums_Unit == null) …

STM32F4 DWT功能 实现程序运行时间精确测试

时间戳相关寄存器定义 /* 在Cortex-M里面有一个外设叫DWT(Data Watchpoint and Trace), 该外设有一个32位的寄存器叫CYCCNT,它是一个向上的计数器, 记录的是内核时钟运行的个数,最长能记录的时间为: 10.74s2的32次方/4…

Apache DolphinScheduler GitHub Star 突破 10000!

点击蓝字 关注我们今天,Apache DolphinScheduler GitHub Star 突破 10000,项目迎来一个重要里程碑。这表明 Apache DolphinScheduler 已经在全球的开发者和用户中获得了广泛的认可和使用。DolphinScheduler 旨在解决公司日常运营中的大数据处理工作流调度…

【强化学习】强化学习数学基础:时序差分方法

时序差分方法Temporal Difference Learning举个例子TD learning of state values算法描述TD learning of action values: SarsaTD learning of action values: Expected SarsaTD learning of action values: n-step SarsaTD learning of optimal action values: Q-learningA un…

蓝牙耳机哪款佩戴舒服?2023佩戴最舒适蓝牙耳机

由于每天坐在电脑桌前的时间比较长,需要音乐和运动调剂一下自己,而说到与蓝牙耳机,大家更加关注的是音质和佩戴舒适度吧,作为一位音乐发烧友,使用过的耳机数不胜数,接下来跟大家分享一下我的体验感受。 第…

WPF DataGrid控件的使用 使用列模板来进行数据格式的美化

<Grid><Grid.RowDefinitions><RowDefinition Height"0.1*" /><RowDefinition /></Grid.RowDefinitions><Button Content"刷新"FontSize"25"Command"{Binding ExecuteRefreshCommand}" /><Dat…

二叉树经典14题——初学二叉树必会的简单题

此篇皆为leetcode、牛客中的简单题型和二叉树基础操作&#xff0c;无需做过多讲解&#xff0c;仅付最优解。有需要的小伙伴直接私信我~ 目录 1.二叉树的节点个数 2.二叉树叶子节点个数 3.二叉树第K层节点个数 4.查找值为X的节点 5.leetcode——二叉树的最大深度 6.leetc…