天天看點

ES6中export、export default、import的了解

前言

ES6标準釋出後,module成為标準,标準使用是以export指令導出接口,以import引入子產品,但是在我們一貫的node子產品中,我們依然采用的是CommonJS規範,使用require引入子產品,使用module.exports導出接口。

一個子產品就是一個獨立的檔案。該檔案内部的所有變量,外部無法擷取。如果你希望外部能夠讀取子產品内部的某個變量,就必須使用​

​export​

​關鍵字輸出該變量。

export導出子產品

export文法聲明用于導出函數、對象、指定檔案(或子產品)的原始值。

// 導出單個特性
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}

// 導出清單
export { name1, name2, …, nameN };

// 重命名導出
export { variable1 as name1, variable2 as name2, …, nameN };

// 預設導出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

// Aggregating modules
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;      

export有兩種子產品導出方式:命名式導出(名稱導出)和預設導出(定義式導出),命名式導出每個子產品可以多個,而預設導出每個子產品僅一個。

命名式導出

子產品可以通過export字首關鍵詞聲明導出對象,導出對象可以是多個。這些導出對象用名稱進行區分,稱之為命名式導出。

//------ lib.js ------  
export const sqrt = Math.sqrt;  
export function square(x) {  
    return x * x;  
}  
export function add (x, y) {  
    return x + y;  
}  
  
//------ main.js ------  
import { square, add } from 'lib';  
console.log(square(10)); //100  
console.log(add(2,4));  //6        

這樣導入的變量名必須和導出的名稱一緻。以上對于每一個要導出的變量都加了export,我們也可以直接導出一個清單,例如上面的lib.js可以改寫成:

//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
    return x * x;
}
function add (x, y) {
    return x + y;
}
export {sqrt, square, add}      

​export​

​清單可以在子產品檔案最外層作用域的每一處聲明,不一定非要把它放在子產品檔案的末尾。

也可以直接導入整個子產品,此時的main.js子產品将變成這樣。

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(10)); //100
console.log(lib.add(2,4));  //6      

子產品導出時,可以指定子產品的導出成員。導出成員可以認為是類中的公有對象,而非導出成員可以認為是類中的私有對象:

var name = 'IT筆錄';
var domain = 'http://itbilu.com';
  
export {name, domain}; // 相當于導出
{name:name,domain:domain}      

子產品導出時,我們可以使用as關鍵字對導出成員進行重命名:

var name = 'IT筆錄';
var domain = 'http://itbilu.com';
  
export {name as siteName, domain};      

注意:export 指令規定的是對外接口,必須與子產品内部變量建立一一對應的關系

// 寫法一
export var m = 1;

// 寫法二
var m = 1;
export {m};

// 寫法三
var n = 1;
export {n as m};


// 報錯
export 1;

// 報錯
var m = 1;
export m;      

報錯的寫法原因是:沒有提供對外的接口,第一種直接輸出1,第二種雖然有變量m,但還是直接輸出1,導緻無法解構。

同樣的,​

​function​

​​和​

​class​

​的輸出,也必須遵守這樣的寫法。

// 報錯
function f() {}
export f;

// 正确
export function f() {};

// 正确
function f() {}
export {f};      

預設導出

預設導出也被稱做定義式導出。命名式導出可以導出多個值,但在在import引用時,也要使用相同的名稱來引用相應的值。而預設導出每個導出隻有一個單一值,這個輸出可以是一個函數、類或其它類型的值,這樣在子產品import導入時也會很容易引用。

export default function() {}; // 可以導出一個函數
export default class(){}; // 也可以出一個類      

注意這裡預設導出不需要用{}。當然也可以使用混合的導出。

//------ lib.js ------
export default function() {...};
export function each() {...};
 
//------ main.js ------
import _,{ each } from 'lib';      

export加default和不加的差別

// 第一組
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指令。

export default就是輸出一個叫做default的變量或方法,然後系統允許你為它取任意名字

// 正确
export var a = 1;
 
// 正确
var a = 1;
export default a;
 
// 錯誤
export default var a = 1;
// 上面代碼中,export default a的含義是将變量a的值賦給變量default。是以,最後一種寫法會報錯。
// 注意,不能使用 var、let 或 const 用于導出預設值 export default。      

因為export default 指令的本質是将後面的值,賦給default變量,是以可以直接将一個值寫在export default之後

// 正确
export default 42;
 
// 報錯 原因這一句報錯是因為沒有指定對外的接口,而前一句指定外對接口為default。
export 42;      

如果想在一條import語句中,同時輸入預設方法和其他接口,可以寫成下面這樣

import _, { each, each as forEach } from 'lodash';

===============

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();      

命名式導出與預設導出

預設導出可以了解為另一種形式的命名導出,預設導出可以認為是使用了default名稱的命名導出。

下面兩種導出方式是等價的:

const D = 123;
  
export default D;
export { D as default };      

在導出多個值時,命名導出非常有用。在導入期間,必須使用相應對象的相同名稱。但是,可以使用任何名稱導入預設導出,例如:

let k; export default k = 12; // 在 test.js檔案中定義導出
import m from './test' // 由于k 是預設導出,是以可以自由使用import m 替代import k
console.log(m);        // 輸出為 12      

Import加載子產品

export定義了子產品的對外接口後,其他JS檔案就可以通過import來加載這個子產品,

// main.js
import {firstName, lastName, year} from './profile';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}      

import指令接受一對大括号,裡面指定要從其他子產品導入的變量名,必須與被導入子產品(​

​profile.js​

​)對外接口的名稱相同。

如果想重新給導入的變量一個名字,可以用as關鍵字,

import { lastName as surname } from './profile';      

import後的from 可以指定需要導入子產品的路徑名,可以是絕對路徑,也可以是相對路徑, .js路徑可以省略,如果隻有子產品名,不帶有路徑,需要有配置檔案指定。

注意,​

​import​

​指令具有提升效果,會提升到整個子產品的頭部,首先執行。(是在編譯階段執行的)

// 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));      
import * as circle from './circle';

// 下面兩行都是不允許的
circle.foo = 'hello';
circle.area = function () {};      
  • ​import​

    ​​和​

    ​export​

    ​指令隻能在子產品的頂層,不能在代碼塊之中。否則會文法報錯。
  • 這樣的設計,可以提高編譯器效率,但是沒有辦法實作運作時加載。
  • 因為require是運作時加載,是以import指令沒有辦法代替require的動态加載功能。
  • 是以引入了import()函數。完成動态加載。