關(guān)于import的思考

前言

起因是因為一次來回引入引發(fā)的問題葵诈。例如A引入了B的方法,B引入了A的方法掌腰,然后在調(diào)用時發(fā)現(xiàn)遇到了問題,于是決定做一篇關(guān)于模塊引入的整理张吉。


Module語法

歷史上齿梁,JavaScript 一直沒有模塊(module)體系,無法將一個大程序拆分成互相依賴的小文件肮蛹,再用簡單的方法拼裝起來勺择。

在 ES6 之前,社區(qū)制定了一些模塊加載方案伦忠,最主要的有 CommonJS 和 AMD 兩種省核。前者用于服務(wù)器,后者用于瀏覽器昆码。ES6 在語言標準的層面上气忠,實現(xiàn)了模塊功能邻储,而且實現(xiàn)得相當簡單,完全可以取代 CommonJS 和 AMD 規(guī)范旧噪,成為瀏覽器和服務(wù)器通用的模塊解決方案吨娜。

ES6 模塊的設(shè)計思想是盡量的靜態(tài)化,使得編譯時就能確定模塊的依賴關(guān)系淘钟,以及輸入和輸出的變量宦赠。CommonJS 和 AMD 模塊,都只能在運行時確定這些東西米母。比如袱瓮,CommonJS 模塊就是對象,輸入時必須查找對象屬性爱咬。

require和import本質(zhì)上就是commonjs和ES6module的區(qū)別尺借。一個是JavaScript社區(qū)指定的野生規(guī)范,一個是后續(xù)指定的官方規(guī)范精拟。

// 常規(guī)用法
// CommonJS模塊
let { stat, exists, readFile } = require('fs');

// ES6模塊
import { stat, exists, readFile } from 'fs';

import的一些注意事項

import命令輸入的變量都是只讀的燎斩,因為它的本質(zhì)是輸入接口。也就是說蜂绎,不允許在加載模塊的腳本里面栅表,改寫接口。

// 報錯
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';
}

由于import是靜態(tài)執(zhí)行师枣,所以不能使用表達式和變量怪瓶,這些只有在運行時才能得到結(jié)果的語法結(jié)構(gòu)。
因此也不存在著頁面根據(jù)條件判斷去import資源這種說法践美。

import會執(zhí)行所加載模塊洗贰。例如:import 'lodash',僅僅執(zhí)行l(wèi)odash模塊陨倡,但是不輸入任何值敛滋。

require('core-js/modules/es6.symbol');
require('core-js/modules/es6.promise');
import React from 'React';

目前階段,通過 Babel 轉(zhuǎn)碼兴革,CommonJS 模塊的require命令和 ES6 模塊的import命令绎晃,可以寫在同一個模塊里面,但是最好不要這樣做杂曲。因為import在靜態(tài)解析階段執(zhí)行庶艾,所以它是一個模塊之中最早執(zhí)行的。下面的代碼可能不會得到預(yù)期結(jié)果擎勘。

關(guān)于import()函數(shù)咱揍,其誕生的目的是為了讓import也有動態(tài)加載的能力,實現(xiàn)require先前擁有的基本功能货抄。


關(guān)于require和import本質(zhì)區(qū)別

寸志:形式上看上去五花八門述召,本質(zhì)可以涵蓋為以下三點朱转。

  1. CommonJS 還是 ES6 Module 輸出都可以看成是一個具備多個屬性或者方法的對象蟹地;
  2. default 是 ES6 Module 所獨有的關(guān)鍵字积暖,export default fs 輸出默認的接口對象,import fs from 'fs' 可直接導(dǎo)入這個對象怪与;
  3. ES6 Module 中導(dǎo)入模塊的屬性或者方法是強綁定的夺刑,包括基礎(chǔ)類型;而 CommonJS 則是普通的值傳遞或者引用傳遞分别。

第3點具體該如何理解遍愿?

// counter.js
exports.count = 0
setTimeout(function () { ++exports.count }, 500)

// commonjs.js
const {count} = require('./counter')
setTimeout(function () {
  console.log('read count after 1000ms in commonjs is', count)
}, 1000)
// 輸出結(jié)果:read count after 1000ms in commonjs is 0

//es6.js
import {count} from './counter'
setTimeout(function () {
  console.log('read count after 1000ms in es6 is', count)
}, 1000)
// 輸出結(jié)果:read count after 1000ms in es6 is 1

count為number類型,因此commonjs中是值傳遞耘斩,傳遞0之后后續(xù)原來的參數(shù)發(fā)生變化并不會影響到引用方的count值沼填。而es6中為強綁定,因此后續(xù)也跟著發(fā)生了改變括授。


關(guān)于循環(huán)引用

解答這個問題需要先了解commonjs和es6底層的加載原理

commonjs

CommonJS 的一個模塊坞笙,就是一個腳本文件。require命令第一次加載該腳本荚虚,就會執(zhí)行整個腳本薛夜,然后在內(nèi)存生成一個對象。

{
  id: '...', // 模塊名
  exports: { ... }, // 模塊輸出的各個接口
  loaded: true, // 表示是否執(zhí)行完畢
  ...
}

執(zhí)行一次后版述,再次執(zhí)行只會去exports上取值梯澜,返回第一次運行的結(jié)果。
CommonJS 模塊遇到循環(huán)加載時渴析,返回的是當前已經(jīng)執(zhí)行的部分的值晚伙,而不是代碼全部執(zhí)行后的值,兩者可能會有差異俭茧。所以撬腾,輸入變量的時候,必須非常小心恢恼。

es6

ES6 模塊是動態(tài)引用民傻,如果使用import從一個模塊加載變量(即import foo from 'foo'),那些變量不會被緩存场斑,而是成為一個指向被加載模塊的引用漓踢,需要開發(fā)者自己保證,真正取值的時候能夠取到值漏隐。

// a.mjs
import {bar} from './b';
console.log('a.mjs');
console.log(bar);
export let foo = 'foo';

// b.mjs
import {foo} from './a';
console.log('b.mjs');
console.log(foo);
export let bar = 'bar';

直接拋出這個典型的例子喧半。執(zhí)行a.mjs后,引用b青责,已知它從a輸入了foo接口挺据,這時不會去執(zhí)行a取具,而是認為這個接口已經(jīng)存在了。因此執(zhí)行到第三行時發(fā)現(xiàn)foo并沒有定義ReferenceError扁耐。
可以通過將bar暇检,foo寫成函數(shù)的形式,這是因為函數(shù)具有提升作用婉称,在執(zhí)行import {bar} from './b'時块仆,函數(shù)foo就已經(jīng)有定義了,所以b.mjs加載的時候不會報錯王暗。這也意味著悔据,如果把函數(shù)foo改寫成函數(shù)表達式,也會報錯俗壹。


心得體會

關(guān)于import的討論科汗,先前分析過它預(yù)加載、動態(tài)加載的行為绷雏。本次熟悉了語法用法和加載的機制头滔,盡量通過函數(shù)的形式去返回可能存在循環(huán)調(diào)用的模塊,在發(fā)生循環(huán)調(diào)用時也要尤為警惕避免產(chǎn)生不可預(yù)見的bug之众。


參考

Module 的語法 - 阮老師的es6教程
Module 的加載實現(xiàn) - 阮老師的es6教程
require拙毫,import區(qū)別?-知乎

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末棺禾,一起剝皮案震驚了整個濱河市缀蹄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌膘婶,老刑警劉巖缺前,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異悬襟,居然都是意外死亡衅码,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門脊岳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逝段,“玉大人,你說我怎么就攤上這事割捅∧糖” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵亿驾,是天一觀的道長嘹黔。 經(jīng)常有香客問我,道長莫瞬,這世上最難降的妖魔是什么儡蔓? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任郭蕉,我火速辦了婚禮,結(jié)果婚禮上喂江,老公的妹妹穿的比我還像新娘召锈。我一直安慰自己,他們只是感情好开呐,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布烟勋。 她就那樣靜靜地躺著规求,像睡著了一般筐付。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阻肿,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天瓦戚,我揣著相機與錄音,去河邊找鬼丛塌。 笑死较解,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的赴邻。 我是一名探鬼主播印衔,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姥敛!你這毒婦竟也來了奸焙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤彤敛,失蹤者是張志新(化名)和其女友劉穎与帆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體墨榄,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡玄糟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了袄秩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阵翎。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖之剧,靈堂內(nèi)的尸體忽然破棺而出郭卫,到底是詐尸還是另有隱情,我是刑警寧澤猪狈,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布箱沦,位于F島的核電站,受9級特大地震影響雇庙,放射性物質(zhì)發(fā)生泄漏谓形。R本人自食惡果不足惜灶伊,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寒跳。 院中可真熱鬧聘萨,春花似錦、人聲如沸童太。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽书释。三九已至翘贮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間爆惧,已是汗流浹背狸页。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扯再,地道東北人芍耘。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像熄阻,于是被迫代替她去往敵國和親斋竞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 上一章介紹了模塊的語法秃殉,本章介紹如何在瀏覽器和 Node 之中加載 ES6 模塊坝初,以及實際開發(fā)中經(jīng)常遇到的一些問題...
    emmet7life閱讀 2,771評論 0 1
  • 瀏覽器加載 傳統(tǒng)方法 HTML網(wǎng)頁中,瀏覽器通過 標簽加載JavaScript腳本复濒。 上面代碼中脖卖,由于瀏覽器腳本的...
    oWSQo閱讀 665評論 0 0
  • 【轉(zhuǎn)】 遵循的模塊化規(guī)范不一樣 模塊化規(guī)范:即為 JavaScript 提供一種模塊編寫、模塊依賴和模塊運行的方案...
    houruyaogeili閱讀 3,308評論 0 2
  • 模塊通常是指編程語言所提供的代碼組織機制巧颈,利用此機制可將程序拆解為獨立且通用的代碼單元畦木。所謂模塊化主要是解決代碼分...
    MapleLeafFall閱讀 1,171評論 0 0
  • 本文為阮一峰大神的《ECMAScript 6 入門》的個人版提純! babel babel負責將JS高級語法轉(zhuǎn)義砸泛,...
    Devildi已被占用閱讀 1,988評論 0 4