JS 中的 import 与 export

本文详细介绍了JavaScript的模块化发展历程,从最初的函数、对象和立即执行函数表达式,到CommonJS、CMD、AMD等主流模块规范,再到ES6的export和import。重点讲解了命名式导出、默认导出以及如何导入模块,帮助开发者理解JavaScript模块化的各种实现方式。
摘要由CSDN通过智能技术生成

模块

模块就是实现特定功能的一组方法。

理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。但是,JavaScript 不是一种模块化编程语言,在 ES6 以前,它是不支持类,所以也就没有模块了。
JavaScript 社区做了很多努力,在现有的运行环境中,实现”模块”的效果。

原始写法
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。

function f1(){
 //...
}
function f2(){
 //...  
}

上面的函数 f1() 和 f2(),组成一个模块。使用的时候,直接调用就行了。
缺点:污染全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。

对象写法
为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。

var module1 = new Object({
  num: 0,
 f1: function (){
  //...
 },
 f2: function (){
  //...
 }
});

上面的函数 f1() 和 f2(),都封装在 module1 对象里。使用的时候,调用这个对象的属性
module1.f1()

这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。
module1.num = 1

立即执行函数写法
使用立即执行函数,可以达到不暴露私有成员的目的

var module2 = (function() {
var num = 0;
var f1 = function() {
  alert(num)
}
var f2 = function() {
  alert(num + 1)
}
return {
  f1: f1,
  f2: f2
}
})()

使用上面的写法,外部代码无法读取内部的 num 变量。
console.log(module2.num) //undefined

主流模块规范

CommonJS
CMD
AMD

现阶段的标准

ES6 标准发布后,module 成为标准,标准使用是以 export 指令导出接口,以 import 引入模块,但是在 node 模块中,我们依然采用的是 CommonJS 规范,使用 require 引入模块,使用 module.exports 导出接口。

export 导出模块

export 语法声明用于导出函数、对象、指定文件(或模块)的原始值。
export 有两种模块导出方式:命名式导出(名称导出)和默认导出(定义式导出),命名式导出每个模块可以多个,而默认导出每个模块仅一个。

export { name1, name2};
export { a1 as name1, a2 as name2 };
export let name1, name2;
export let name1 = a1, name2 = a1;
 
export default expression;
export default function (a) { console.log(a) } 
export default function f1(a) { console.log(a) } 
export { name1 as default};
 
export * from name1;
export { name1, name2} from a1;
export { a1 as name1, a2 as name2 } from b;
  • name1 ,name2 ,导出的标识符。导出后,可以通过这个标识符在另一个模块中使用 import 引用
  • default 设置模块的默认导出。设置后import不通过标识符而直接引用默认导出
  • “*” 继承模块并导出继承模块所有的方法和属性
  • as 重命名导出“标识符”
  • from 从已经存在的模块、脚本文件导出

命名式导出
模块可以通过 export 前缀关键词声明导出对象,导出对象可以是多个。这些导出对象用名称进行区分,称之为命名式导出。

export { fn }; // 导出一个已定义的函数
export const foo = Math.sqrt(2); // 导出一个常量

我们可以使用 * 和 from 关键字来实现的模块的继承

export * from 'article';

模块导出时,可以指定模块的导出成员。导出成员可以认为是类中的公有对象,而非导出成员可以认为是类中的私有对象:

var name = '石昊';
var age = 6;
 
export {name, age}; 
// 相当于导出 {name:name, age:age}

模块导出时,我们可以使用 as 关键字对导出成员进行重命名:

var name = '石昊';
var age = 6;
 
export {name as nickName, age};

注意,下面的语法有严重错误的情况:

// 错误演示1
export 1; 
 
// 错误演示12
var a = 100;
export a;
 

export在导出接口的时候,必须与模块内部的变量具有一一对应的关系。直接导出1没有任何意义,也不可能在 import 的时候有一个变量与之对应.
export a 虽然看上去成立,但是 a 的值是一个数字,根本无法完成解构,因此必须写成 export {a} 的形式。即使a被赋值为一个 function,也是不允许的。而且,大部分风格都建议,模块中最好在末尾用一个export导出所有的接口,例如:

export {f1 as default,a,b,c};

默认导出
默认导出也被称做定义式导出。命名式导出可以导出多个值,但在在 import 引用时,也要使用相同的名称来引用相应的值。而默认导出每个导出只有一个单一值,这个输出可以是一个函数、类或其它类型的值,这样在模块 import 导入时也会很容易引用。

export default function() {}; // 可以导出一个函数
export default class(){}; // 也可以出一个类

命名式导出与默认导出
默认导出可以理解为另一种形式的命名式导出,默认导出可以认为是使用了 default 名称的命名式导出。

下面两种导出方式是等价的:

const name = '石昊';
export default name;
export { name as default };

export 使用示例

使用名称导出一个模块时:

// "info.js" 模块
export function getName(name) {
  if(name === '唐三'){
  	return '海神'
  }else if(name == '千仞雪'){
  	return '天使神'
  }else if(name == '比比东'){
  	return '罗刹神'
  }else{
    return '酱油'
  }
}
const weapon = '海神三叉戟';
export { weapon };

在另一个模块(脚本文件)中,我们可以像下面这样引用:

import { getName, weapon } from 'info';
console.log(getGodName("比比东")); // 罗刹神
console.log(weapon); // 海神三叉戟

使用默认导出一个模块时:

// "info.js"模块
export default function(name) {
  if(name === '唐三'){
  	return '海神'
  }else if(name == '千仞雪'){
  	return '天使神'
  }else if(name == '比比东'){
  	return '罗刹神'
  }else{
    return '酱油'
  }
}

在另一个模块(脚本文件)中,我们可以像下面这样引用,相对名称导出来说使用更为简单:

// 引用 "info.js"模块
import getName from 'info';
console.log(getName("唐三")); // 海神

import 导入模块

import 语法用于从已导出的模块、脚本中导入函数、对象、指定文件(或模块)的原始值。

import 模块导入与 export 模块导出功能相对应,也存在两种模块导入方式:命名式导入(名称导入)和默认导入(定义式导入)。

import 的语法跟 require 不同,而且 import 必须放在文件的最开始,且前面不允许有其他逻辑代码,这和其他所有编程语言风格一致。

import defaultMember from "module-name";
import * as name from "module-name";
import { member1 } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member1 [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";
  • defaultMember 导入默认导出成员
  • name 从将要导入模块中收到的导出值的名称
  • member1,member1 导入指定名称的多个成员
  • alias 别名,对指定导入成员进行的重命名
  • module-name 要导入的模块,是一个文件名
  • as 重命名导入成员名称
  • from 从已经存在的模块、脚本文件等导入

命名式导入
我们可以通过指定名称,就是将这些成员插入到当作用域中。导入时,可以导入单个成员或多个成员:

// 花括号里面的变量与 export 后面的变量一一对应
import { member } from "my-module";
import { name, age } from "my-module";

通过 * 符号,我们可以导入模块中的全部属性和方法。当导入模块全部导出内容时,就是将导出模块所有的导出绑定内容,插入到当前模块的作用域中:

import * as myModule from "my-module";
// myModule 可以访问到"my-module"所有的导出绑定内容

导入模块对象时,也可以使用as对导入成员重命名,以方便在当前模块内使用:

import {reallyReallyLongModuleMemberName as shortName} from "my-module";

导入多个成员时,同样可以使用别名:

import {reallyReallyLongModuleMemberName1 as shortName1, reallyReallyLongModuleMemberName2 as shortName2} from "my-module";

导入一个模块,但不进行任何绑定:

import "my-module";

默认导入
在模块导出时,可能会存在默认导出。同样的,在导入时可以使用import指令导出这些默认值。

直接导入默认值:

import myDefault from "my-module";

也可以在命名式导入中,同时使用默认导入:

import myDefault, * as myModule from "my-module"; // myModule 做为命名空间使用
import myDefault, {foo, bar} from "my-module"; // 指定成员导入

import 使用示例

// 导出文件 a.js
function getData(url, callback) {
    let xhr = new XMLHttpRequest();
    xhr.onload = function () {
        callback(this.responseText)
    };
    xhr.open("GET", url, true);
    xhr.send();
}

export function getUserInfo(url, callback) {
    getData(url, data => callback(JSON.parse(data)));
}

// 导入文件 b.js
import { getUserInfo } from "b";
getUserInfo("http://baidu.com", data => {
    console.log(data);
}

default 关键字

// a.js
export default function() {}
 
// 等效于
function a() {};
export {a as default};

在import的时候,可以这样用:

import a from './a';
 
// 等效于
import {default as a} from './d';

这个语法糖的好处就是import的时候,可以省去花括号{},简单的说,如果import的时候,你发现某个变量没有花括号括起来(没有*号),那么你在脑海中应该把它还原成有花括号的as语法。

import $,{ each, map } from 'jquery';
// import 后面第一个 $ 是 {defalut as $} 的替代写法

as 关键字
as简单的说就是取一个别名 export 和 import 中都可以使用:

// a.js
var a = function() {};
export {a as fun};
 
// b.js
import {fun as a} from './a';
a();

上面这段代码,export 的时候,对外提供的接口是 fun,它是a.js 内部 a 这个函数的别名。
import 中的 as 就很简单,就是你在使用模块里面的方法的时候,给这个方法取一个别名,好在当前的文件里面使用。之所以是这样,是因为有的时候不同的两个模块可能通过相同的接口,比如有一个c.js也通过了fun这个接口:

// c.js
export function fun() {};

如果在 b.js 中同时使用 a 和 c 这两个模块,就必须想办法解决接口重名的问题,as 就解决了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值