require是es6語法嗎

2022-10-21 18:01:16

不是,require是CommonJS規範的模組化語法;而es6規範的模組化語法是import。require是執行時載入,import是編譯時載入;require可以寫在程式碼的任意位置,import只能寫在檔案的最頂端且不可在條件語句或函數作用域中使用;require執行時才引入模組的屬性所以效能相對較低,import編譯時引入模組的屬性所所以效能稍高。

前端(vue)入門到精通課程:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:

本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。

require是CommonJS規範的模組化語法;而es6規範的模組化語法是import。

CommonJS介紹

CommonJS實現了Javascript的模組化規範,規範了模組的特性和各模組之間的相互依賴性,使得程式碼可以更好的編寫和維護,同時提高了程式碼的複用性。定義每個檔案都是一個模組(module變數代表當前模組)並有自己的作用域,而每個檔案裡面定義的變數、函數、類,都是私有的,對於其他模組是不可見。模組的exports屬性是對外的介面,只有通過exports匯出的屬性才能被載入識別。Node就是基於CommonJs規範實現的,因為CommonJS規範載入模組是同步的,而伺服器中的Node模組都直接儲存在伺服器本地硬碟中的,載入起來自然比較快,可以說node專案是CommonJs規範目前的最好實踐應用。

ECMAScript 6 (簡稱ES6)

ECMAScript是(European Computer Manufacturers Association Script)的縮寫,它是由ECMA國際標準化客製化的一項JavaScript指令碼語言的標準化規範。ECMAScript 6 是ECMA在2015年釋出的JavaScript 的規範標準,解決 ES5 的很多先天不足並引入了模組化思想。ES6模組的設計思想是儘量的靜態化,使得編譯時就能確定模組的依賴關係以及輸入和輸出的變數。

require與import的區別

1,require是執行時載入,import是編譯時載入;

2,require可以寫在程式碼的任意位置,import只能寫在檔案的最頂端且不可在條件語句或函數作用域中使用;

3,require通過module.exports匯出的值就不能再變化,import通過export匯出的值可以改變;

4;require通過module.exports匯出的是exports物件,import通過export匯出是指定輸出的程式碼;

5,require執行時才引入模組的屬性所以效能相對較低,import編譯時引入模組的屬性所所以效能稍高。

javascript中的require、import與export

理想情況下,開發者只需要實現核心的業務邏輯,其他都可以載入別人已經寫好的模組。

但是,Javascript不是一種模組化程式語言,在es6以前,它是不支援」類」(class),所以也就沒有」模組」(module)了。

require時代

Javascript社群做了很多努力,在現有的執行環境中,實現」模組」的效果。

原始寫法

模組就是實現特定功能的一組方法。
只要把不同的函數(以及記錄狀態的變數)簡單地放在一起,就算是一個模組。

function m1(){
 //...
}
function m2(){
 //...  
}
登入後複製

上面的函數m1()m2() ,組成一個模組。使用的時候,直接呼叫就行了。

這種做法的缺點很明顯:」汙染」了全域性變數,無法保證不與其他模組發生變數名衝突,而且模組成員之間看不出直接關係。

物件寫法

為了解決上面的缺點,可以把模組寫成一個物件,所有的模組成員都放到這個物件裡面

var module1 = new Object({
_count : 0,
 m1 : function (){
  //...
 },
 m2 : function (){
  //...
 }
});
登入後複製

上面的函數m1()m2() ,都封裝在module1物件裡。使用的時候,就是呼叫這個物件的屬性

module1.m1();
登入後複製

這樣的寫法會暴露所有模組成員,內部狀態可以被外部改寫。比如,外部程式碼可以直接改變內部計數器的值。

module._count = 1;
登入後複製

立即執行函數寫法

使用」立即執行函數」(Immediately-Invoked Function Expression,IIFE),可以達到不暴露私有成員的目的

var module = (function() {
var _count = 0;
var m1 = function() {
alert(_count)
}
var m2 = function() {
alert(_count + 1)
}
 
return {
m1: m1,
m2: m2
}
})()
登入後複製

使用上面的寫法,外部程式碼無法讀取內部的_count變數。

  console.info(module._count); //undefined
登入後複製

module就是Javascript模組的基本寫法。

主流模組規範

在es6以前,還沒有提出一套官方的規範,從社群和框架推廣程度而言,目前通行的javascript模組規範有兩種:CommonJS 和 AMD

CommonJS規範

2009年,美國程式設計師Ryan Dahl創造了node.js專案,將javascript語言用於伺服器端程式設計。

這標誌」Javascript模組化程式設計」正式誕生。前端的複雜程度有限,沒有模組也是可以的,但是在伺服器端,一定要有模組,與作業系統和其他應用程式互動,否則根本沒法程式設計。

node程式設計中最重要的思想之一就是模組,而正是這個思想,讓JavaScript的大規模工程成為可能。模組化程式設計在js界流行,也是基於此,隨後在瀏覽器端,requirejs和seajs之類的工具包也出現了,可以說在對應規範下,require統治了ES6之前的所有模組化程式設計,即使現在,在ES6 module被完全實現之前,還是這樣。

在CommonJS中,暴露模組使用module.exports和exports,很多人不明白暴露物件為什麼會有兩個,後面會介紹區別

在CommonJS中,有一個全域性性方法require() ,用於載入模組。假定有一個數學模組math.js,就可以像下面這樣載入。

var math = require('math');
登入後複製

然後,就可以呼叫模組提供的方法:

 var math = require('math');
 math.add(2,3); // 5
登入後複製

正是由於CommonJS 使用的require方式的推動,才有了後面的AMD、CMD 也採用的require方式來參照模組的風格

AMD規範

有了伺服器端模組以後,很自然地,大家就想要使用者端模組。而且最好兩者能夠相容,一個模組不用修改,在伺服器和瀏覽器都可以執行。

但是,由於一個重大的侷限,使得CommonJS規範不適用於瀏覽器環境。還是上一節的程式碼,如果在瀏覽器中執行,會有一個很大的問題

var math = require('math');
math.add(2, 3);
登入後複製

第二行math.add(2, 3) ,在第一行require(‘math')之後執行,因此必須等math.js載入完成。也就是說,如果載入時間很長,整個應用就會停在那裡等。

這對伺服器端不是一個問題,因為所有的模組都存放在本地硬碟,可以同步載入完成,等待時間就是硬碟的讀取時間。但是,對於瀏覽器,這卻是一個大問題,因為模組都放在伺服器端,等待時間取決於網速的快慢,可能要等很長時間,瀏覽器處於」假死」狀態。

因此,瀏覽器端的模組,不能採用」同步載入」(synchronous),只能採用」非同步載入」(asynchronous)。這就是AMD規範誕生的背景。

AMD是」Asynchronous Module Definition」的縮寫,意思就是」非同步模組定義」。它採用非同步方式載入模組,模組的載入不影響它後面語句的執行。所有依賴這個模組的語句,都定義在一個回撥函數中,等到載入完成之後,這個回撥函數才會執行。

模組必須採用特定的define()函數來定義。

define(id?, dependencies?, factory)
登入後複製
登入後複製
  • id:字串,模組名稱(可選)
  • dependencies: 是我們要載入的依賴模組(可選),使用相對路徑。,注意是陣列格式
  • factory: 工廠方法,返回一個模組函數

如果一個模組不依賴其他模組,那麼可以直接定義在define()函數之中。

// math.js
  define(function (){
    var add = function (x,y){
      return x+y;
    };
    return {
      add: add
    };
  });
登入後複製

如果這個模組還依賴其他模組,那麼define()函數的第一個引數,必須是一個陣列,指明該模組的依賴性。

define(['Lib'], function(Lib){
    function foo(){
      Lib.doSomething();
    }
    return {
      foo : foo
    };
  });
登入後複製

require()函數載入上面這個模組的時候,就會先載入Lib.js檔案。

AMD也採用require()語句載入模組,但是不同於CommonJS,它要求兩個引數:

require([module], callback);
登入後複製

第一個引數[module],是一個陣列,裡面的成員就是要載入的模組;第二個引數callback,則是載入成功之後的回撥函數。如果將前面的程式碼改寫成AMD形式,就是下面這樣:

require(['math'], function (math) {
 math.add(2, 3);
});
登入後複製

math.add()與math模組載入不是同步的,瀏覽器不會發生假死。所以很顯然,AMD比較適合瀏覽器環境。

目前,主要有兩個Javascript庫實現了AMD規範:require.js和curl.js。

CMD規範

CMD (Common Module Definition), 是seajs推崇的規範,CMD則是依賴就近,用的時候再require。它寫起來是這樣的:

define(function(require, exports, module) {
var clock = require('clock');
clock.start();
});
登入後複製

CMD與AMD一樣,也是採用特定的define()函數來定義,用require方式來參照模組

define(id?, dependencies?, factory)
登入後複製
登入後複製
  • id:字串,模組名稱(可選)
  • dependencies: 是我們要載入的依賴模組(可選),使用相對路徑。,注意是陣列格式
  • factory: 工廠方法,返回一個模組函數
define('hello', ['jquery'], function(require, exports, module) {
 
// 模組程式碼
 
});
登入後複製

如果一個模組不依賴其他模組,那麼可以直接定義在define()函數之中。

define(function(require, exports, module) {
// 模組程式碼
});
登入後複製

注意:帶 id 和 dependencies 引數的 define 用法不屬於 CMD 規範,而屬於 Modules/Transport 規範。

CMD與AMD區別

AMD和CMD最大的區別是對依賴模組的執行時機處理不同,而不是載入的時機或者方式不同,二者皆為非同步載入模組。

AMD依賴前置,js可以方便知道依賴模組是誰,立即載入;

而CMD就近依賴,需要使用把模組變為字串解析一遍才知道依賴了那些模組,這也是很多人詬病CMD的一點,犧牲效能來帶來開發的便利性,實際上解析模組用的時間短到可以忽略。

現階段的標準

ES6標準釋出後,module成為標準,標準使用是以export指令匯出介面,以import引入模組,但是在我們一貫的node模組中,我們依然採用的是CommonJS規範,使用require引入模組,使用module.exports匯出介面。

export匯出模組

export語法宣告用於匯出函數、物件、指定檔案(或模組)的原始值。

注意:在node中使用的是exports,不要混淆了

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

export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const
 
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
 
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
登入後複製
  • name1… nameN-匯出的「識別符號」。匯出後,可以通過這個「識別符號」在另一個模組中使用import參照
  • default-設定模組的預設匯出。設定後import不通過「識別符號」而直接參照預設匯入
  • -繼承模組並匯出繼承模組所有的方法和屬性
  • as-重新命名匯出「識別符號」
  • from-從已經存在的模組、指令碼檔案…匯出

命名式匯出

模組可以通過export字首關鍵詞宣告匯出物件,匯出物件可以是多個。這些匯出物件用名稱進行區分,稱之為命名式匯出。

export { myFunction }; // 匯出一個已定義的函數
export const foo = Math.sqrt(2); // 匯出一個常數
登入後複製

我們可以使用*和from關鍵字來實現的模組的繼承:

export * from 'article';
登入後複製

模組匯出時,可以指定模組的匯出成員。匯出成員可以認為是類中的公有物件,而非匯出成員可以認為是類中的私有物件:

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 1; // 絕對不可以
 
var a = 100;
export a;
登入後複製

export在匯出介面的時候,必須與模組內部的變數具有一一對應的關係。直接匯出1沒有任何意義,也不可能在import的時候有一個變數與之對應

export a雖然看上去成立,但是a的值是一個數位,根本無法完成解構,因此必須寫成export {a}的形式。即使a被賦值為一個function,也是不允許的。而且,大部分風格都建議,模組中最好在末尾用一個export匯出所有的介面,例如:

export {fun as default,a,b,c};
登入後複製

預設匯出

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

export default function() {}; // 可以匯出一個函數
export default class(){}; // 也可以出一個類
登入後複製

命名式匯出與預設匯出

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

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

const D = 123;
 
export default D;
export { D as default };
登入後複製

export使用範例

使用名稱匯出一個模組時:

// "my-module.js" 模組
export function cube(x) {
return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
export { foo };
登入後複製

在另一個模組(指令碼檔案)中,我們可以像下面這樣參照:

import { cube, foo } from 'my-module';
console.log(cube(3)); // 27
console.log(foo); // 4.555806215962888
登入後複製

使用預設匯出一個模組時:

// "my-module.js"模組
export default function (x) {
return x * x * x;
}
登入後複製

在另一個模組(指令碼檔案)中,我們可以像下面這樣參照,相對名稱匯出來說使用更為簡單:

// 參照 "my-module.js"模組
import cube from 'my-module';
console.log(cube(3)); // 27
登入後複製

import引入模組

import語法宣告用於從已匯出的模組、指令碼中匯入函數、物件、指定檔案(或模組)的原始值。

import模組匯入與export模組匯出功能相對應,也存在兩種模組匯入方式:命名式匯入(名稱匯入)和預設匯入(定義式匯入)。

import的語法跟require不同,而且import必須放在檔案的最開始,且前面不允許有其他邏輯程式碼,這和其他所有程式語言風格一致。

import defaultMember from "module-name";
import * as name from "module-name";
import { member } 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, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";
登入後複製
  • name-從將要匯入模組中收到的匯出值的名稱
  • member, memberN-從匯出模組,匯入指定名稱的多個成員
  • defaultMember-從匯出模組,匯入預設匯出成員
  • alias, aliasN-別名,對指定匯入成員進行的重新命名
  • module-name-要匯入的模組。是一個檔名
  • as-重新命名匯入成員名稱(「識別符號」)
  • from-從已經存在的模組、指令碼檔案等匯入

命名式匯入

我們可以通過指定名稱,就是將這些成員插入到當作用域中。匯出時,可以匯入單個成員或多個成員:

注意:花括號裡面的變數與export後面的變數一一對應

import {myMember} from "my-module";
import {foo, bar} from "my-module";
登入後複製

通過*符號,我們可以匯入模組中的全部屬性和方法。當匯入模組全部匯出內容時,就是將匯出模組('my-module.js')所有的匯出繫結內容,插入到當前模組('myModule')的作用域中:

import * as myModule from "my-module";
登入後複製

匯入模組物件時,也可以使用as對匯入成員重新命名,以方便在當前模組內使用:

import {reallyReallyLongModuleMemberName as shortName} from "my-module";
登入後複製

匯入多個成員時,同樣可以使用別名:

import {reallyReallyLongModuleMemberName as shortName, anotherLongModuleName as short} 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使用範例

// --file.js--
function getJSON(url, callback) {
let xhr = new XMLHttpRequest();
xhr.onload = function () {
callback(this.responseText)
};
xhr.open("GET", url, true);
xhr.send();
}
 
export function getUsefulContents(url, callback) {
getJSON(url, data => callback(JSON.parse(data)));
}
 
// --main.js--
import { getUsefulContents } from "file";
getUsefulContents("http://itbilu.com", data => {
doSomethingUseful(data);
});
登入後複製

default關鍵字

// d.js
export default function() {}
 
// 等效於:
function a() {};
export {a as default};
登入後複製

在import的時候,可以這樣用:

import a from './d';
 
// 等效於,或者說就是下面這種寫法的簡寫,是同一個意思
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這個函數的別名,但是在模組外面,認不到a,只能認到fun。

import中的as就很簡單,就是你在使用模組裡面的方法的時候,給這個方法取一個別名,好在當前的檔案裡面使用。之所以是這樣,是因為有的時候不同的兩個模組可能通過相同的介面,比如有一個c.js也通過了fun這個介面:

// c.js
export function fun() {};
登入後複製

如果在b.js中同時使用a和c這兩個模組,就必須想辦法解決介面重名的問題,as就解決了。

CommonJS中module.exports 與 exports的區別

Module.exports

The module.exports object is created by the Module system. Sometimes this is not acceptable; many want their module to be an instance of some class. To do this, assign the desired export object to module.exports. Note that assigning the desired object to exports will simply rebind the local exports variable, which is probably not what you want to do.

譯文:module.exports物件是由模組系統建立的。 有時這是難以接受的;許多人希望他們的模組成為某個類的範例。 為了實現這個,需要將期望匯出的物件賦值給module.exports。 注意,將期望的物件賦值給exports會簡單地重新系結到本地exports變數上,這可能不是你想要的。

Module.exports

The exports variable is available within a module's file-level scope, and is assigned the value of module.exports before the module is evaluated. It allows a shortcut, so that module.exports.f = … can be written more succinctly as exports.f = …. However, be aware that like any variable, if a new value is assigned to exports, it is no longer bound to module.exports:

譯文:exports變數是在模組的檔案級別作用域內有效的,它在模組被執行前被賦於 module.exports 的值。它有一個快捷方式,以便 module.exports.f = … 可以被更簡潔地寫成exports.f = … 。 注意,就像任何變數,如果一個新的值被賦值給exports,它就不再繫結到module.exports(其實是exports.屬性會自動掛載到沒有命名衝突的module.exports.屬性)

從Api檔案上面的可以看出,從require匯入方式去理解,關鍵有兩個變數(全域性變數module.exports,區域性變數exports)、一個返回值(module.exports)

function require(...) {
var module = { exports: {} };
((module, exports) => {
// 你的被引入程式碼 Start
// var exports = module.exports = {}; (預設都有的)
function some_func() {};
exports = some_func;
// 此時,exports不再掛載到module.exports,
// export將匯出{}預設物件
module.exports = some_func;
// 此時,這個模組將匯出some_func物件,覆蓋exports上的some_func
// 你的被引入程式碼 End
})(module, module.exports);
// 不管是exports還是module.exports,最後返回的還是module.exports
return module.exports;
}
登入後複製

demo.js:

console.log(exports); // {}
console.log(module.exports); // {}
console.log(exports === module.exports); // true
console.log(exports == module.exports); // true
console.log(module);
/**
Module {
id: '.',
exports: {},
parent: null,
filename: '/Users/larben/Desktop/demo.js',
loaded: false,
children: [],
paths:
[ '/Users/larben/Desktop/node_modules',
'/Users/larben/node_modules',
'/Users/node_modules',
'/node_modules' ] }
*/
登入後複製

注意

每個js檔案一建立,都有一個var exports = module.exports = {} , 使exports和module.exports都指向一個空物件。

module.exports和exports所指向的記憶體地址相同

【相關推薦:、】

以上就是require是es6語法嗎的詳細內容,更多請關注TW511.COM其它相關文章!