javascript
JavaScript 中的 require / exports、import / export、浅谈JavaScript、ES5、ES6
Node.js 的基礎教學 之 exports 和 module.exports:https://zhuanlan.zhihu.com/p/82057593
淺談 JavaScript、ES5、ES6:https://www.cnblogs.com/lovesong/p/4908871.html
1、為什么有模塊概念
理想情況下,開發者只需要實現核心的業務邏輯,其他都可以加載別人已經寫好的模塊。
但是,Javascript 不是一種模塊化編程語言,在es6以前,它是不支持 "類"(class),所以也就沒有 "模塊"(module)了。
2、require 時代
Javascript 社區做了很多努力,在現有的運行環境中,實現 "模塊"?的效果。
原始寫法
模塊就是實現特定功能的一組方法。只要把不同的函數(以及記錄狀態的變量)簡單地放在一起,就算是一個模塊。
function m1(){//... } function m2(){//... }上面的函數 m1() 和 m2(),組成一個模塊。使用的時候,直接調用就行了。
這種做法的缺點很明顯:"污染" 了全局變量,無法保證不與其他模塊發生變量名沖突,而且模塊成員之間看不出直接關系。
對象寫法
為了解決上面的缺點,可以把模塊寫成一個對象,所有的模塊成員都放到這個對象里面
var module1 = new Object({_count: 0,m1: function () {//...},m2: function () {//...} });上面的函數 m1() 和 m2(),都封裝在 module1 對象里。使用的時候,就是調用這個對象的屬性:module1.m1();
這樣的寫法會暴露所有模塊成員,內部狀態可以被外部改寫。比如,外部代碼可以直接改變內部計數器的值:module1._?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 模塊的基本寫法。
nodejs 模塊機制
Nodejs 的模塊機制 及 require 用法:https://www.cnblogs.com/typttxs/p/12196649.html
Nodejs 中提供了 exports 和 require 兩個對象,
- exports 是 模塊公開的接口。
- require 用于 從外部獲取一個模塊的接口,即 獲取模塊的 exports 對象。
有關 exports 和 module.exports 的區別
- 正常對外暴露 屬性或者方法,使用 exports
- 如需要暴露 對象(類似class,包含了很多屬性和方法),就使用 module.exports
具體可以參考 --- Node.js 模塊系統:https://www.runoob.com/nodejs/nodejs-module-system.html
hello.js
exports.world = function() {console.log('Hello World'); }main.js,代碼 require 引入了當前目錄下的 hello.js 文件,使用了模塊 exports 出來的對象或者方法
var hello = require('./hello'); hello.world();hello.js 中通過 exports 對象將 world 作為模塊的訪問接口,在 main.js 內通過 require 加載這個模塊,然后就可以直接訪問 hello.js 里的 exports 對象的成員函數了。
修改 hello.js 和 main.js
//hello.js function Hello() { var name; this.setName = function(thyName) { name = thyName; }; this.sayHello = function() { console.log('Hello ' + name); }; }; module.exports = Hello;直接獲取對象
//main.js var Hello = require('./hello'); hello = new Hello(); hello.setName('BYVoid'); hello.sayHello();模塊接口的唯一變化是使用 module.exports = Hello 代替了exports.world = function(){}。
在外部引用該模塊時,其接口對象就是要輸出的 Hello 對象本身,而不是原先的 exports。
exports 可以理解為?module.exports 的一個 快捷方式。。。
require 查找機制
Node.js 的 require 方法中的文件查找策略如下:
從文件模塊緩存中加載
盡管原生模塊與文件模塊的優先級不同,但是都會優先從文件模塊的緩存中加載已經存在的模塊。
從原生模塊加載
原生模塊的優先級僅次于文件模塊緩存的優先級。require 方法在解析文件名之后,優先檢查模塊是否在原生模塊列表中。以http模塊為例,盡管在目錄下存在一個 http/http.js/http.node/http.json 文件,require("http") 都不會從這些文件中加載,而是從原生模塊中加載。
原生模塊也有一個緩存區,同樣也是優先從緩存區加載。如果緩存區沒有被加載過,則調用原生模塊的加載方式進行加載和執行。
從文件加載
當文件模塊緩存中不存在,而且不是原生模塊的時候,Node.js 會解析 require 方法傳入的參數,并從文件系統中加載實際的文件,加載過程中的包裝和編譯細節在前一節中已經介紹過,這里我們將詳細描述查找文件模塊的過程,其中,也有一些細節值得知曉。
require方法接受以下幾種參數的傳遞:
- http、fs、path等,原生模塊。
- ./mod或../mod,相對路徑的文件模塊。
- /pathtomodule/mod,絕對路徑的文件模塊。
- mod,非原生模塊的文件模塊。
下面是一個完整的,在路徑 Y 下執行 require(X) 語句執行順序:
1. 如果 X 是內置模塊a. 返回內置模塊b. 停止執行 2. 如果 X 以 '/' 開頭a. 設置 Y 為文件根路徑 3. 如果 X 以 './' 或 '/' or '../' 開頭a. LOAD_AS_FILE(Y + X)b. LOAD_AS_DIRECTORY(Y + X) 4. LOAD_NODE_MODULES(X, dirname(Y)) 5. 拋出異常 "not found"LOAD_AS_FILE(X) 1. 如果 X 是一個文件, 將 X 作為 JavaScript 文本載入并停止執行。 2. 如果 X.js 是一個文件, 將 X.js 作為 JavaScript 文本載入并停止執行。 3. 如果 X.json 是一個文件, 解析 X.json 為 JavaScript 對象并停止執行。 4. 如果 X.node 是一個文件, 將 X.node 作為二進制插件載入并停止執行。LOAD_INDEX(X) 1. 如果 X/index.js 是一個文件, 將 X/index.js 作為 JavaScript 文本載入并停止執行。 2. 如果 X/index.json 是一個文件, 解析 X/index.json 為 JavaScript 對象并停止執行。 3. 如果 X/index.node 是一個文件, 將 X/index.node 作為二進制插件載入并停止執行。LOAD_AS_DIRECTORY(X) 1. 如果 X/package.json 是一個文件,a. 解析 X/package.json, 并查找 "main" 字段。b. let M = X + (json main 字段)c. LOAD_AS_FILE(M)d. LOAD_INDEX(M) 2. LOAD_INDEX(X)LOAD_NODE_MODULES(X, START) 1. let DIRS=NODE_MODULES_PATHS(START) 2. for each DIR in DIRS:a. LOAD_AS_FILE(DIR/X)b. LOAD_AS_DIRECTORY(DIR/X)NODE_MODULES_PATHS(START) 1. let PARTS = path split(START) 2. let I = count of PARTS - 1 3. let DIRS = [] 4. while I >= 0,a. if PARTS[I] = "node_modules" CONTINUEb. DIR = path join(PARTS[0 .. I] + "node_modules")c. DIRS = DIRS + DIRd. let I = I - 1 5. return DIRS3、主流模塊規范
在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 。(?exports 可以理解為?module.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); // 5第二行 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() 函數之中。
define(function (require, exports, module) {// 模塊代碼 });注意:帶 id 和 dependencies 參數的 define 用法不屬于 CMD 規范,而屬于 Modules/Transport 規范。
CMD 與 AMD 區別
AMD 和 CMD 最大的區別是對依賴模塊的執行時機處理不同,而不是加載的時機或者方式不同,二者皆為異步加載模塊。
- AMD 依賴前置,js可以方便知道依賴模塊是誰,立即加載;
- CMD就近依賴,需要使用把模塊變為字符串解析一遍才知道依賴了那些模塊,這也是很多人詬病CMD的一點,犧牲性能來帶來開發的便利性,實際上解析模塊用的時間短到可以忽略。
4、現階段的標準
- ES6 標準發布后,module 成為標準,標準使用是以 export 指令導出接口,以 import 引入模塊,
- 但是在我們一貫的 node 模塊中,我們依然采用的是 CommonJS 規范,使用 require 引入模塊,使用 module.exports 導出接口。
export 導出模塊
export 語法聲明用于導出函數、對象、指定文件(或模塊)的原始值。
注意:
- 在 node 中使用的是 exports ,不是 export。不要混淆了
- ES6 標準發布后,module 成為標準,標準使用是以 export 導出接口,以 import 引入模塊,
export 有兩種模塊導出方式:
- 命名式導出(名稱導出),命名式導出每個模塊可以多個
- 默認導出(定義式導出),默認導出每個模塊僅一個。
- 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)); // 275、淺談 JavaScript、ES5、ES6
什么是 JavaScript
JavaScript一種 動態類型、弱類型、基于原型 的客戶端腳本語言,用來給HTML網頁增加動態功能。
- 動態:在運行時確定數據類型。變量使用之前不需要類型聲明,通常變量的類型是被賦值的那個值的類型。
- 弱類型:計算時可以不同類型之間對使用者透明地隱式轉換,即使類型不正確,也能通過隱式轉換來得到正確的類型。
- 原型:新對象繼承對象(作為模版),將自身的屬性共享給新對象,模版對象稱為原型。這樣新對象實例化后不但可以享有自己創建時和運行時定義的屬性,而且可以享有原型對象的屬性。
PS:新對象指函數,模版對象是實例對象,實例對象是不能繼承原型的,函數才可以的。
JavaScript 是基于原型的客戶端腳本語言,用來給 HTML 網頁增加動態功能。JavaScript 由三部分組成:
- ECMAScript(核心):它規定了語言的組成部分:語法、類型、語句、關鍵字、保留字、操作符、對象
- DOM(文檔對象模型):DOM 把整個頁面映射為一個多層節點結果,開發人員可借助 DOM提供的 API,輕松地刪除、添加、替換或修改任何節點。
- BOM(瀏覽器對象模型):支持可以訪問和操作瀏覽器窗口的瀏覽器對象模型,開發人員可以控制瀏覽器顯示的頁面以外的部分。
ES 全稱就是 ECMAScript,作為 JavaScript 語言的核心,規定了語言的組成部分:語法、類型、語句、關鍵字、保留字、操作符、對象。
什么是 ES5
作為 ECMAScript第五個版本(第四版因為過于復雜廢棄了),瀏覽器支持情況可看第一副圖,增加特性如下。
- 1. strict模式。嚴格模式,限制一些用法,'use?strict';
- 2.?Array 增加方法。增加了every、some?、forEach、filter?、indexOf、lastIndexOf、isArray、map、reduce、reduceRight 方法。(?PS: 還有其他方法?Function.prototype.bind、String.prototype.trim、Date.now )
- 3.?Object 方法。
? ? ? ? Object.getPrototypeOf
? ? ? ? Object.create
? ? ? ? Object.getOwnPropertyNames
? ? ? ? Object.defineProperty
? ? ? ? Object.getOwnPropertyDescriptor
? ? ? ? Object.defineProperties
? ? ? ? Object.keys
? ? ? ? Object.preventExtensions / Object.isExtensible
? ? ? ? Object.seal / Object.isSealed
? ? ? ? Object.freeze / Object.isFrozen
什么是 ES6
ECMAScript6 在保證向下兼容的前提下,提供大量新特性。
ES6 特性如下:
1. 塊級作用域 關鍵字 let,常量 const
2. 對象字面量的屬性賦值簡寫(property value shorthand)
var obj = {// __proto____proto__: theProtoObj,// Shorthand for ‘handler: handler’handler,// Method definitionstoString() {// Super callsreturn "d " + super.toString();},// Computed (dynamic) property names[ 'prop_' + (() => 42)() ]: 42 };3. 賦值解構
let singer = { first: "Bob", last: "Dylan" }; let { first: f, last: l } = singer; // 相當于 f = "Bob", l = "Dylan" let [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2015-10-25"); let [x, y] = [1, 2, 3]; // x = 1, y = 24. 函數參數 (?默認值、參數打包、?數組展開(Default 、Rest 、Spread) )
//Default function findArtist(name='lu', age='26') {... }//Rest function f(x, ...y) {// y is an Arrayreturn x * y.length; } f(3, "hello", true) == 6//Spread function f(x, y, z) {return x + y + z; } // Pass each elem of array as argument f(...[1,2,3]) == 65. 箭頭函數 Arrow functions
- (1). 簡化了代碼形式,默認 return 表達式結果。
- (2). 自動綁定語義 this,即定義函數時的 this。如上面例子中,forEach 的匿名函數參數中用到的 this。
6. 字符串模板 Template strings
var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` // return "Hello Bob, how are you today?"7. Iterators(迭代器)+ for..of
迭代器有個 next 方法,調用會返回:
- (1). 返回迭代對象的一個元素:{ done: false, value: elem }
- (2). 如果已到迭代對象的末端:{ done: true, value: retVal }
8. 生成器 (Generators)
9. Class
Class,有 constructor、extends、super,但本質上是語法糖(對語言的功能并沒有影響,但是更方便程序員使用)。
class Artist {constructor(name) {this.name = name;}perform() {return this.name + " performs ";} }class Singer extends Artist {constructor(name, song) {super.constructor(name);this.song = song;}perform() {return super.perform() + "[" + this.song + "]";} }let james = new Singer("Etta James", "At last"); james instanceof Artist; // true james instanceof Singer; // truejames.perform(); // "Etta James performs [At last]"10. Modules
ES6的內置模塊功能借鑒了CommonJS和AMD各自的優點:
- (1). 具有 CommonJS 的精簡語法、唯一導出出口 (single exports) 和 循環依賴 (cyclic dependencies) 的特點。
- (2). 類似 AMD,支持異步加載和可配置的模塊加載。
11. Map + Set + WeakMap + WeakSet
四種集合類型,WeakMap、WeakSet 作為屬性鍵的對象如果沒有別的變量在引用它們,則會被回收釋放掉。
// Sets var s = new Set(); s.add("hello").add("goodbye").add("hello"); s.size === 2; s.has("hello") === true;// Maps var m = new Map(); m.set("hello", 42); m.set(s, 34); m.get(s) == 34;//WeakMap var wm = new WeakMap(); wm.set(s, { extra: 42 }); wm.size === undefined// Weak Sets var ws = new WeakSet(); ws.add({ data: 42 });//Because the added object has no other references, it will not be held in the set12. Math + Number + String + Array + Object APIs
一些新的 API
Number.EPSILON Number.isInteger(Infinity) // false Number.isNaN("NaN") // falseMath.acosh(3) // 1.762747174039086 Math.hypot(3, 4) // 5 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2"abcde".includes("cd") // true "abc".repeat(3) // "abcabcabc"Array.from(document.querySelectorAll('*')) // Returns a real Array Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior[0, 0, 0].fill(7, 1) // [0,7,7] [1, 2, 3].find(x => x == 3) // 3 [1, 2, 3].findIndex(x => x == 2) // 1 [1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2] ["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"] ["a", "b", "c"].keys() // iterator 0, 1, 2 ["a", "b", "c"].values() // iterator "a", "b", "c"Object.assign(Point, { origin: new Point(0,0) })13.?Proxies
使用代理(Proxy)監聽對象的操作,然后可以做一些相應事情。
var target = {}; var handler = {get: function (receiver, name) {return `Hello, ${name}!`;} };var p = new Proxy(target, handler); p.world === 'Hello, world!';可監聽的操作: get、set、has、deleteProperty、apply、construct、getOwnPropertyDescriptor、defineProperty、getPrototypeOf、setPrototypeOf、enumerate、ownKeys、preventExtensions、isExtensible。
14. Symbols
Symbol 是一種基本類型。Symbol 通過調用symbol函數產生,它接收一個可選的名字參數,該函數返回的 symbol 是唯一的。
var key = Symbol("key"); var key2 = Symbol("key"); key == key2 //false15. Promises
Promises 是處理異步操作的對象,使用了 Promise 對象之后可以用一種鏈式調用的方式來組織代碼,讓代碼更加直觀(類似 jQuery 的 deferred?對象)。
function fakeAjax(url) {return new Promise(function (resolve, reject) {// setTimeouts are for effect, typically we would handle XHRif (!url) {return setTimeout(reject, 1000);}return setTimeout(resolve, 1000);}); }// no url, promise rejected fakeAjax().then(function () {console.log('success'); },function () {console.log('fail'); });ES5 和 ES6 區別
初學者在 ES6 和 ES5 之間的取舍?:https://www.zhihu.com/question/30608934
1.? es5 使用 require 導包,es6 使用 import from
ES5 中的引用需要先使用 require 導入包,成為對象,再去進行真正引用
// ES5 var React = require("react"); var { Component,PropTypes } = React; //引用React抽象組件var ReactNative = require("react-native"); var { Image,Text } = ReactNative; //引用具體的React Native組件在 ES6 里,可以使用 import 直接實現系統庫引用,不需要額外制作一個類庫對象:
//ES6 import { Component,PropTypes } from 'react'; import { Image,Text } from 'react-native'2.??導出及引用單個類
ES5 中,要導出一個類給別的模塊使用,一般通過?module.exports?來實現,引用時通過?require方法來獲取:
//ES5導出 var MyComponent = React.createClass({... }); module.exports = MyComponent;//ES5導入 var MyComponent = require('./MyComponent');ES6 中,使用 export default 實現同樣的功能,但要使用 import 方法來實現導入:
//ES6導出(一般都繼承于Component類) export default class MyComponent extends Component{... }//ES6導入 import MyComponent from './MyComponent';3.??ES5 和 ES6 繼承的區別
// ES5的繼承 function FatherClass(name) {this.family = ['father', 'mother', 'daughter'];this.name = name }; FatherClass.prototype.getName = function () {console.log(this.name); }; function ChilderClass(name, age) {// 子類繼承父類(沒涉及到父類原型)FatherClass.call(this, name)this.age = age; }; function F() { }; //過渡函數的原型繼承父對象 F.prototype = FatherClass.prototype; ChilderClass.prototype = new F(); var c = new ChilderClass('lily', 18);這個就是 es5 的類繼承,構造函數繼承和原型繼承是分開的(兩種不同的方式)。子類繼承父類的時候,先創造子類的實例對象this,然后再將父類的屬性和方法添加到this上面(FatherClass.call(this,name)),然后再去繼承原型鏈。
// ES6的繼承 class FatherClass {family = ['father', 'mother', 'daughter'];constructor(name) {this.name = name}getName() {console.log(this.name);} };class ChilderClass extends FatherClass {constructor(name, age) {super(name);this.age = age;} }; var c = new ChilderClass('lily', 18);這里類繼承機制完全和 es5 不一樣,是調用 super 方法將父類的屬性和方法加到 this 上面。在子類的構造函數中,只有調用 super 之后,才可以使用 this 關鍵字,否則會報錯。這是因為子類的構建基于父類,只有 super 方法才能調用父類構造函數。
- ES5 的繼承實質上是先創建子類的實例對象 this,然后再將父類的方法添加到 this 上(Parent.apply(this)),然后繼承原型。
- ES6 的繼承機制完全不同,它是在子類的構造器中先調用 super 方法,創建出父類實例對象 this,然后再去修改子類中的 this 完善子類。
6、es6 的?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); });7、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 $} 的替代寫法。
8、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就解決了。
9、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.exportsreturn 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' ] }*/注意
每一個 node.js 執行文件,都自動創建一個 module 對象,同時,module對象會創建一個叫 exports 的屬性,初始化的值是 {}
require 是值的拷貝,不是值引用,所以不用擔心異步導致的數據污染。
區別主要有3點:
前兩條很好理解,第三條,意思是:
- 如果 exports = newObject,那么 exports 斷開了與 module.exports 的引用,兩者就不再指向一個內存地址了,所有才有了如下的寫法:
? ? ? ? exports = module.exports = somethings
? ? 等價于
? ? ? ? module.exports = somethings
? ? ? ? exports = module.exports - 如果 module.exports = newObject,exports 跟 module.exports 不再指向同一個值(或者叫內存地址),那么 exports 就失效了。
總結:
- 如果不使用 module.exports,僅使用 exports,那么 exports 只能得到屬性,不能重新賦值對象。建議使用?exports.x?等寫法。
- 如果使用了module.exports = newObject,那么這時候 exports 就失效了,必須使用?exports = module.exports = newObject?建立聯系
?
總結
以上是生活随笔為你收集整理的JavaScript 中的 require / exports、import / export、浅谈JavaScript、ES5、ES6的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CompletableFuture详解~
- 下一篇: 全能终端神器 --- MobaXterm