export,,export default,import || export,module.exports,require

參考地址 http://www.itdecent.cn/p/be2d4eab3878

module.exports

Node應(yīng)用由模塊組成,采用CommonJS模塊規(guī)范。根據(jù)這個(gè)規(guī)范,每個(gè)文件就是一個(gè)模塊,有自己的作用域。在這些文件里面定義的變量、函數(shù)、類(lèi),都是私有的,對(duì)外不可見(jiàn),因此規(guī)避掉了作用域污染。

根據(jù)CommonJS規(guī)定,每個(gè)模塊內(nèi)部,module變量代表當(dāng)前模塊,這個(gè)變量是一個(gè)對(duì)象,它的exports屬性(即module.exports)是對(duì)外的接口。加載某個(gè)模塊,其實(shí)就是加載該模塊的exports屬性。

舉例:通過(guò)module.exports輸出變量 age 和 sayHelloTo 函數(shù)。

//./MyModule.js
    var age = 7; 
    var sayHelloTo= function (name) { 
        return "hello " + name;
    }; 
    module.exports.age = age; 
    module.exports.sayHelloTo=sayHelloTo;

require:用于加載模塊

var temp = require('./MyModule.js');  
//這里也可以使用 import temp from './MyModule.js'
console.log(temp.age); // 7 
console.log(temp.sayHelloTo("Steve")); // hello 

exports 與 module.exports

為了方便,node為每個(gè)模塊提供了一個(gè)exports變量,指向module.exports。這等同于在每個(gè)模塊頭部,有這么一行代碼:

var exports = module.exports;

因此,我們可以直接在exports對(duì)象上添加方法(等同于在 module.exports 添加一樣)

//./MyModule.js
    var age = 7; 
    var sayHelloTo= function (name) { 
        return "hello " + name;
    }; 
    exports.age = age;  //等效于:  module.exports.age = age;
    exports.sayHelloTo=sayHelloTo;  //等效于: module.exports.sayHelloTo=sayHelloTo;

P.S.不能直接將exports指向一個(gè)值,這會(huì)切斷 exports 與 module.exports 的聯(lián)系(但是可以用module.exports來(lái)指向一個(gè)值)

//./MyModule.js
    var age = 7; 
    var sayHelloTo= function (name) { 
        return "hello " + name;
    }; 
    exports = age;  //不要這么干。這么做會(huì)切斷exports與module.exports的聯(lián)系, require該文件會(huì)得到一個(gè)空對(duì)象
    module.exports = age; //這樣寫(xiě)沒(méi)有問(wèn)題,require該文件會(huì)得到一個(gè)值7

不同于CommonJS,ES6使用 export 和 import 來(lái)導(dǎo)入、導(dǎo)出模塊

用 export 導(dǎo)出的模塊,需要用 import 來(lái)進(jìn)行導(dǎo)入,而不能用 require。
P.S.:export 命令規(guī)定的是對(duì)外的接口,必須與模塊內(nèi)部的變量建立一一對(duì)應(yīng)的關(guān)系

const utils = {
    showSth : function(){
        console.log("showSth");
    },
    saySth : function(){
        console.log("saySth");
    }
}
//導(dǎo)出的3種方式
// 方式1,這種方式在引用的時(shí)候需要這樣: import {m} from './utils.js';
export var m = utils; 
// 方式2,用大括號(hào)來(lái)導(dǎo)出變量,如果導(dǎo)出的變量有多個(gè),則{變量1,變量2,變量3...,變量N}。
//這種方式在引用的時(shí)候需要這樣: import {utils} from './utils.js';
export {utils}; 
// 方式3,這種方式在引用的時(shí)候需要這樣: import {myUtils} from './utils.js';
//在引用的地方,也可以直接指定別名,如:import {myUtils as utils} from './utils.js';
export {utils as myUtils}; 

export 和 export default

  1. export 和export default 均可用于導(dǎo)出(常量 | 函數(shù) | 文件 | 模塊)等。
  2. 可以在其他文件中通過(guò) import+(常量 | 函數(shù) | 文件 | 模塊)名的方式,將其導(dǎo)入,以便能夠進(jìn)行使用。
  3. 在一個(gè)文件或者模塊中,export、import 可以有多個(gè),但 export default 僅有一個(gè)。
const utils = {
    showSth : function(){
        console.log("showSth");
    },
    saySth : function(){
        console.log("saySth");
    }
}
const name = "my name is Artech";
export {name}; //命名導(dǎo)出
export {utils};

//對(duì)于命名方式導(dǎo)出的,在導(dǎo)入的時(shí)候必須使用相應(yīng)對(duì)象的相同名稱(chēng)
//引用的時(shí)候:
import {utils,name as myName} from './utils.js';
  1. 通過(guò) export 方式導(dǎo)出,在導(dǎo)入時(shí)要用花括號(hào){ };而通過(guò) export default 方式導(dǎo)出的,則不需要:
//如通過(guò) export default 導(dǎo)出
  export default utils;  
//則在使用的時(shí)候不用加花括號(hào),且導(dǎo)入時(shí)的名字可以自定義,如:
  import myUtils from './utils.js';  //對(duì)于默認(rèn)方式導(dǎo)出的,則導(dǎo)入的時(shí)候,名稱(chēng)可以隨便取

//默認(rèn)導(dǎo)出:不能使用 let,var 或 const 作為默認(rèn)導(dǎo)出

import *

將一個(gè)js文件中定義的方法,模塊,對(duì)象等,全部導(dǎo)出,一般結(jié)合別名使用,如:

//myModule.js
   export const fun1 = ()=>{}
   export const objInfo = {...};
//demo.js:引用mymODULE。JS
    import * as myAlias from './myModule';
    //fun1()和objInfo都是定義在myModule中的方法和對(duì)象
    myAlias.fun1();
    myAlias.objInfo;

補(bǔ)充:

1. 如果用export default導(dǎo)出,然后用import * 引入
//myModule.js
   export default {
    fun1: ()=>{},
    objInfo: {
      aaa: 'sss1'
    },
  }
//demo.js:引用myModule.js
    import * as myAlias from './myModule';
    //fun1()和objInfo都是定義在myModule中的default的方法和對(duì)象
    myAlias.default.fun1();
    myAlias.default.objInfo;

本質(zhì)上,export default就是輸出一個(gè)叫做default的變量或方法,然后系統(tǒng)允許你為它取任意名字。所以,下面的寫(xiě)法是有效的。

//demo.js:引用myModule.js
    import { default as myAlias } from './myModule';
    myAlias.fun1();
    myAlias.objInfo;

正是因?yàn)閑xport default命令其實(shí)只是輸出一個(gè)叫做default的變量,所以它后面不能跟變量聲明語(yǔ)句

// 正確
export var a = 1;

// 正確
var a = 1;
export default a;

// 錯(cuò)誤
export default var a = 1;

上面代碼中,export default a的含義是將變量a的值賦給變量default。所以,最后一種寫(xiě)法會(huì)報(bào)錯(cuò)。

同樣地,因?yàn)閑xport default命令的本質(zhì)是將后面的值,賦給default變量,所以可以直接將一個(gè)值寫(xiě)在export default之后。

// 正確
export default 42;

// 報(bào)錯(cuò)
export 42;

2. export語(yǔ)句輸出的接口,與其對(duì)應(yīng)的值是動(dòng)態(tài)綁定關(guān)系,即通過(guò)該接口,可以取到模塊內(nèi)部實(shí)時(shí)的值。
//myModule.js
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
//demo.js:引用myModule.js
  import * as myAlias from './myModule';
  console.log(myAlias.foo); //bar
  setTimeout(() => {
    console.log(myAlias.foo); //baz
  },1000);

這一點(diǎn)與 CommonJS 規(guī)范完全不同。CommonJS 模塊輸出的是值的緩存,不存在動(dòng)態(tài)更新


3. export 和 import 命令可以出現(xiàn)在模塊的任何位置,只要處于模塊頂層就可以。如果處于塊級(jí)作用域內(nèi),就會(huì)報(bào)錯(cuò),這是因?yàn)樘幱跅l件代碼塊之中,就沒(méi)法做靜態(tài)優(yōu)化了,違背了 ES6 模塊的設(shè)計(jì)初衷。
function foo() {
  export default 'bar' // SyntaxError
}
foo()

上面代碼中,export語(yǔ)句放在函數(shù)之中,結(jié)果報(bào)錯(cuò)。


4. import命令輸入的變量都是只讀的,因?yàn)樗谋举|(zhì)是輸入接口。也就是說(shuō),不允許在加載模塊的腳本里面,改寫(xiě)接口。
import {a} from './xxx.js'

a = {}; // Syntax Error : 'a' is read-only;

上面代碼中,腳本加載了變量a,對(duì)其重新賦值就會(huì)報(bào)錯(cuò),因?yàn)閍是一個(gè)只讀的接口。但是,如果a是一個(gè)對(duì)象,改寫(xiě)a的屬性是允許的。

import {a} from './xxx.js'

a.foo = 'hello'; // 合法操作

上面代碼中,a的屬性可以成功改寫(xiě),并且其他模塊也可以讀到改寫(xiě)后的值。不過(guò),這種寫(xiě)法很難查錯(cuò),建議凡是輸入的變量,都當(dāng)作完全只讀,輕易不要改變它的屬性。


5. import命令具有提升效果,會(huì)提升到整個(gè)模塊的頭部,首先執(zhí)行。
foo();

import { foo } from 'my_module';

上面的代碼不會(huì)報(bào)錯(cuò),因?yàn)?code>import的執(zhí)行早于foo的調(diào)用。這種行為的本質(zhì)是,import命令是編譯階段執(zhí)行的,在代碼運(yùn)行之前。


6. 由于import是靜態(tài)執(zhí)行,所以不能使用表達(dá)式和變量,這些只有在運(yùn)行時(shí)才能得到結(jié)果的語(yǔ)法結(jié)構(gòu)。
// 報(bào)錯(cuò)
import { 'f' + 'oo' } from 'my_module';

// 報(bào)錯(cuò)
let module = 'my_module';
import { foo } from module;

// 報(bào)錯(cuò)
if (x === 1) {
  import { foo } from 'module1';
} else {
  import { foo } from 'module2';
}

上面三種寫(xiě)法都會(huì)報(bào)錯(cuò),因?yàn)樗鼈冇玫搅吮磉_(dá)式、變量和if結(jié)構(gòu)。在靜態(tài)分析階段,這些語(yǔ)法都是沒(méi)法得到值的。


7.import語(yǔ)句會(huì)執(zhí)行所加載的模塊,因此可以有下面的寫(xiě)法。
import 'lodash';

上面代碼僅僅執(zhí)行lodash模塊,但是不輸入任何值。
如果多次重復(fù)執(zhí)行同一句import語(yǔ)句,那么只會(huì)執(zhí)行一次,而不會(huì)執(zhí)行多次。

import 'lodash';
import 'lodash';

上面代碼加載了兩次lodash,但是只會(huì)執(zhí)行一次。

import { foo } from 'my_module';
import { bar } from 'my_module';

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

上面代碼中,雖然foobar在兩個(gè)語(yǔ)句中加載,但是它們對(duì)應(yīng)的是同一個(gè)my_module實(shí)例。也就是說(shuō),import語(yǔ)句是 Singleton 模式。


8.目前階段,通過(guò) Babel 轉(zhuǎn)碼,CommonJS 模塊的require命令和 ES6 模塊的import命令,可以寫(xiě)在同一個(gè)模塊里面,但是最好不要這樣做。因?yàn)閕mport在靜態(tài)解析階段執(zhí)行,所以它是一個(gè)模塊之中最早執(zhí)行的。下面的代碼可能不會(huì)得到預(yù)期結(jié)果。
require('core-js/modules/es6.symbol');
require('core-js/modules/es6.promise');
import React from 'React';

9. 如果想在一條import語(yǔ)句中,同時(shí)輸入默認(rèn)方法和其他接口,可以寫(xiě)成下面這樣。
import _, { each, forEach } from 'lodash';

對(duì)應(yīng)上面代碼的export語(yǔ)句如下。

export default function (obj) {}
export function each(obj, iterator, context) { }
export function forEach () { }

10. export default也可以用來(lái)輸出類(lèi)。
// MyClass.js
export default class { ... }

// main.js
import MyClass from 'MyClass';
let o = new MyClass();

11. export 與 import 的復(fù)合寫(xiě)法

如果在一個(gè)模塊之中,先輸入后輸出同一個(gè)模塊,import語(yǔ)句可以與export語(yǔ)句寫(xiě)在一起。

export { foo, bar } from 'my_module';

// 可以簡(jiǎn)單理解為
import { foo, bar } from 'my_module';
export { foo, bar };

上面代碼中,exportimport語(yǔ)句可以結(jié)合在一起,寫(xiě)成一行。但需要注意的是,寫(xiě)成一行以后,foobar實(shí)際上并沒(méi)有被導(dǎo)入當(dāng)前模塊,只是相當(dāng)于對(duì)外轉(zhuǎn)發(fā)了這兩個(gè)接口,導(dǎo)致當(dāng)前模塊不能直接使用foobar。


未總結(jié)的:

模塊的繼承
跨模塊常量

如果要使用的常量非常多,可以建一個(gè)專(zhuān)門(mén)的constants目錄,將各種常量寫(xiě)在不同的文件里面,保存在該目錄下。然后,將這些文件輸出的常量,合并在index.js里面。

import() - 提案

importexport命令只能在模塊的頂層,不能在代碼塊之中(比如,在if代碼塊之中,或在函數(shù)之中)。

這樣的設(shè)計(jì),固然有利于編譯器提高效率,但也導(dǎo)致無(wú)法在運(yùn)行時(shí)加載模塊。在語(yǔ)法上,條件加載就不可能實(shí)現(xiàn)。如果import命令要取代 Node 的require方法,這就形成了一個(gè)障礙。因?yàn)?code>require是運(yùn)行時(shí)加載模塊,import命令無(wú)法取代require的動(dòng)態(tài)加載功能。

const path = './' + fileName;
const myModual = require(path);

上面的語(yǔ)句就是動(dòng)態(tài)加載,require到底加載哪一個(gè)模塊,只有運(yùn)行時(shí)才知道。import命令做不到這一點(diǎn)。

因此,有一個(gè)提案,建議引入import()函數(shù),完成動(dòng)態(tài)加載。
.....

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容