成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JS模塊化編程

騫諱護(hù) / 1268人閱讀

摘要:也就是說,外部模塊輸出值變了,當(dāng)前模塊的導(dǎo)入值不會(huì)發(fā)生變化。三規(guī)范的出現(xiàn),使得模塊化在環(huán)境中得到了施展機(jī)會(huì)。模塊化這種加載稱為編譯時(shí)加載或者靜態(tài)加載。總結(jié)的模塊化規(guī)范經(jīng)過了模塊模式的演進(jìn),利用現(xiàn)在常用的打包工具,非常方便我們編寫模塊化代碼。

前言

什么是模塊化?

模塊就是實(shí)現(xiàn)特定功能的一組方法,而模塊化是將模塊的代碼創(chuàng)造自己的作用域,只向外部暴露公開的方法和變量,而這些方法之間高度解耦。

寫 JS 為什么需要模塊化編程?
當(dāng)寫前端還只是處理網(wǎng)頁的一些表單提交,點(diǎn)擊交互的時(shí)候,還沒有強(qiáng)化 JS 模塊化的概念,當(dāng)前端邏輯開始復(fù)雜,交互變得更多,數(shù)據(jù)量越來越龐大時(shí),前端對(duì) JS 模塊化編程的需求就越加強(qiáng)烈。

在很多場景中,我們需要考慮模塊化:

團(tuán)隊(duì)多人協(xié)作,需要引用別人的代碼

項(xiàng)目交接,我們?cè)陂喿x和重構(gòu)別人的代碼

代碼審查時(shí),檢驗(yàn)?zāi)愕拇a是否規(guī)范,是否存在問題

寫完代碼,回顧自己寫的代碼是否美觀:)

不同的環(huán)境,環(huán)境變量不同

基于以上場景,所以,當(dāng)前 JS 模塊化主要是這幾個(gè)目的:

代碼復(fù)用性

功能代碼松耦合

解決命名沖突

代碼可維護(hù)性

代碼可閱讀性

先給結(jié)論:JS 的模塊化編程經(jīng)歷了幾個(gè)階段:

命名空間形式的代碼封裝

通過立即執(zhí)行函數(shù)(IIFE)創(chuàng)建的命名空間

服務(wù)器端運(yùn)行時(shí) Nodejs 的 CommonJS 規(guī)范

將模塊化運(yùn)行在瀏覽器端的 AMD/CMD 規(guī)范

兼容 CMD 和 AMD 的 UMD 規(guī)范

通過語言標(biāo)準(zhǔn)支持的 ES Module

先給結(jié)論圖:

一、命名空間

我們知道,在 ES6 之前,JS 是沒有塊作用域的,私有變量和方法的隔離主要靠函數(shù)作用域,公開變量和方法的隔離主要靠對(duì)象的屬性引用。

封裝函數(shù)

在 JS 還沒有模塊化規(guī)范的時(shí)候,將一些通用的、底層的功能抽象出來,獨(dú)立成一個(gè)個(gè)函數(shù)來實(shí)現(xiàn)模塊化:
比方寫一個(gè) utils.js 工具函數(shù)文件

//  utils.js
function add(x, y) {
    if(typeof x !== "number" || typeof y !== "number") return;
    return x + y;
}

function square(x) {
    if(typeof x !== "number") return;
    return x * x;
}


通過 js 函數(shù)文件劃分的方式,此時(shí)的公開函數(shù)其實(shí)是掛載到了全局對(duì)象 window 下,當(dāng)在別人也想定義一個(gè)叫 add 函數(shù),或者多個(gè) js 文件合并壓縮的時(shí)候,會(huì)存在命名沖突的問題。

掛載到全局變量下:

后來我們想到通過掛載函數(shù)到全局對(duì)象字面量下的方式,利用 JAVA 包的概念,希望減輕命名沖突的嚴(yán)重性。

var mathUtils1 = {
    add: function(x, y) {
        return x + y;
    },
}

var mathUtils2 = {
    add: function(x, y, z) {
        return x + y + z;
    },
}

mathUtils.add();

mathUtils.square();

這種方式仍然創(chuàng)建了全局變量,但如果包的路徑很長,那么到最后引用方法可能就會(huì)以module1.subModule.subSubModule.add 的方式引用代碼了。

IIFE
考慮模塊存在私有變量,于是我們利用IIFE(立即執(zhí)行表達(dá)式)創(chuàng)建閉包來封裝私有變量:

var module = (function(){
    var count = 0;
    return {
        inc: function(){
            count += 1;
        },
        dec: function(){
            count += -1;
        }
    }
})()

module.inc();
module.dec();

這樣私有變量對(duì)于外部來說就是不可訪問的,那如果模塊需要引入其他依賴呢?

var utils = (function ($) {
    var $body = $("body"); 
    var _private = 0;
    var foo = function() {
        ...
    }
    var bar = function () {
        ...
    }
    
    return {
        foo: foo,
        bar: bar
    }
})(jQuery);

以上封裝模塊的方式叫作:模塊模式,在 jQuery 時(shí)代,大量使用了模塊模式:





jQuery 的插件必須在 JQuery.js 文件之后 ,文件的加載順序被嚴(yán)格限制住,依賴越多,依賴關(guān)系越混亂,越容易出錯(cuò)。

二、CommonJS

Nodejs 的出現(xiàn),讓 JavaScript 能夠運(yùn)行在服務(wù)端環(huán)境中,此時(shí)迫切需要建立一個(gè)標(biāo)準(zhǔn)來實(shí)現(xiàn)統(tǒng)一的模塊系統(tǒng),也就是后來的 CommonJS。

// math.js
exports.add = function(x, y) {
    return x + y;
}

// base.js
var math = require("./math.js");
math.add(2, 3);  // 5

// 引用核心模塊
var http = require("http");
http.createServer(...).listen(3000);

CommonJS 規(guī)定每個(gè)模塊內(nèi)部,module 代表當(dāng)前模塊,這個(gè)模塊是一個(gè)對(duì)象,有 id,filename, loaded,parent, children, exports 等屬性,module.exports 屬性表示當(dāng)前模塊對(duì)外輸出的接口,其他文件加載該模塊,實(shí)際上就是讀取 module.exports 變量。

// utils.js
// 直接賦值給 module.exports 變量
module.exports = function () {
    console.log("I"m utils.js module");
}

// base.js
var util = require("./utils.js")
util();  // I"m utils.js module

或者掛載到 module.exports 對(duì)象下
module.exports.say = function () {
    console.log("I"m utils.js module");
}

// base.js
var util = require("./utils.js")
util.say();

為了方便,Node 為每個(gè)模塊提供一個(gè) exports 自由變量,指向 module.exports。這等同在每個(gè)模塊頭部,有一行這樣的命令。

var exports = module.exports;

exports 和 module.exports 共享了同個(gè)引用地址,如果直接對(duì) exports 賦值會(huì)導(dǎo)致兩者不再指向同一個(gè)內(nèi)存地址,但最終不會(huì)對(duì) module.exports 起效。

// module.exports 可以直接賦值
module.exports = "Hello world";  

// exports 不能直接賦值
exports = "Hello world";

CommonJS 總結(jié):
CommonJS 規(guī)范加載模塊是同步的,用于服務(wù)端,由于 CommonJS 會(huì)在啟動(dòng)時(shí)把內(nèi)置模塊加載到內(nèi)存中,也會(huì)把加載過的模塊放在內(nèi)存中。所以在 Node 環(huán)境中用同步加載的方式不會(huì)有很大問題。

另,CommonJS模塊加載的是輸出值的拷貝。也就是說,外部模塊輸出值變了,當(dāng)前模塊的導(dǎo)入值不會(huì)發(fā)生變化。

三、AMD

CommonJS 規(guī)范的出現(xiàn),使得 JS 模塊化在 NodeJS 環(huán)境中得到了施展機(jī)會(huì)。但 CommonJS 如果應(yīng)用在瀏覽器端,同步加載的機(jī)制會(huì)使得 JS 阻塞 UI 線程,造成頁面卡頓。

利用模塊加載后執(zhí)行回調(diào)的機(jī)制,有了后面的 RequireJS 模塊加載器, 由于加載機(jī)制不同,我們稱這種模塊規(guī)范為 AMD(Asynchromous Module Definition 異步模塊定義)規(guī)范, 異步模塊定義誕生于使用 XHR + eval 的開發(fā)經(jīng)驗(yàn),是 RequireJS 模塊加載器對(duì)模塊定義的規(guī)范化產(chǎn)出。

AMD 的模塊寫法:

// 模塊名 utils
// 依賴 jQuery, underscore
// 模塊導(dǎo)出 foo, bar 屬性


// main.js
require.config({
  baseUrl: "script",
  paths: {
    "jquery": "jquery.min",
    "underscore": "underscore.min",
  }
});

// 定義 utils 模塊,使用 jQuery 模塊
define("utils", ["jQuery", "underscore"], function($, _) {
    var body = $("body");
    var deepClone = _.deepClone({...});
    return {
        foo: "hello",
        bar: "world"
    }
})

AMD 的特點(diǎn)在于:

延遲加載

依賴前置

AMD 支持兼容 CommonJS 寫法:

define(function (require, exports, module){
  var someModule = require("someModule");
  var anotherModule = require("anotherModule");

  someModule.sayHi();
  anotherModule.sayBye();

  exports.asplode = function (){
    someModule.eat();
    anotherModule.play();
  };
});
四、CMD

SeaJS 是國內(nèi) JS 大神玉伯開發(fā)的模塊加載器,基于 SeaJS 的模塊機(jī)制,所有 JavaScript 模塊都遵循 CMD(Common Module Definition) 模塊定義規(guī)范.

CMD 模塊的寫法:




// 定義模塊
// utils.js
define(function(require, exports, module) {
  exports.each = function (arr) {
    // 實(shí)現(xiàn)代碼 
  };

  exports.log = function (str) {
    // 實(shí)現(xiàn)代碼
  };
});

// 輸出模塊
define(function(require, exports, module) {
  var util = require("./util.js");
  
  var a = require("./a"); //在需要時(shí)申明,依賴就近
  a.doSomething();
  
  exports.init = function() {
    // 實(shí)現(xiàn)代碼
    util.log();
  };
});

CMD 和 AMD 規(guī)范的區(qū)別:
AMD推崇依賴前置,CMD推崇依賴就近:
AMD 的依賴需要提前定義,加載完后就會(huì)執(zhí)行。
CMD 依賴可以就近書寫,只有在用到某個(gè)模塊的時(shí)候再去執(zhí)行相應(yīng)模塊。
舉個(gè)例子:

// main.js
define(function(require, exports, module) {
  console.log("I"m main");
  var mod1 = require("./mod1");
  mod1.say();
  var mod2 = require("./mod2");
  mod2.say();

  return {
    hello: function() {
      console.log("hello main");
    }
  };
});

// mod1.js
define(function() {
  console.log("I"m mod1");
  return {
    say: function() {
      console.log("say: I"m mod1");
    }
  };
});

// mod2.js
define(function() {
  console.log("I"m mod2");
  return {
    say: function() {
      console.log("say: I"m mod2");
    }
  };
});

以上代碼分別用 Require.js 和 Sea.js 執(zhí)行,打印結(jié)果如下:
Require.js:
先執(zhí)行所有依賴中的代碼

I"m mod1
I"m mod2
I"m main
say: I"m mod1
say: I"m mod2

Sea.js:
用到依賴時(shí),再執(zhí)行依賴中的代碼

I"m main

I"m mod1
say: I"m mod1
I"m mod2
say: I"m mod2
五、UMD

umd(Universal Module Definition) 是 AMD 和 CommonJS 的兼容性處理,提出了跨平臺(tái)的解決方案。

(function (root, factory) {
    if (typeof exports === "object") {
        // commonJS
        module.exports = factory();
    } else if (typeof define === "function" && define.amd) {
        // AMD
        define(factory);
    } else {
        // 掛載到全局
        root.eventUtil = factory();
    }
})(this, function () {
    function myFunc(){};

    return {
        foo: myFunc
    };
});

應(yīng)用 UMD 規(guī)范的 JS 文件其實(shí)就是一個(gè)立即執(zhí)行函數(shù),通過檢驗(yàn) JS 環(huán)境是否支持 CommonJS 或 AMD 再進(jìn)行模塊化定義。

六、ES6 Module

CommonJS 和 AMD 規(guī)范都只能在運(yùn)行時(shí)確定依賴。而 ES6 在語言層面提出了模塊化方案, ES6 module 模塊編譯時(shí)就能確定模塊的依賴關(guān)系,以及輸入和輸出的變量。ES6 模塊化這種加載稱為“編譯時(shí)加載”或者靜態(tài)加載。

寫法:

// math.js
// 命名導(dǎo)出
export function add(a, b){
    return a + b;
}
export function sub(a, b){
    return a - b;
}
// 命名導(dǎo)入
import { add, sub } from "./math.js";
add(2, 3);
sub(7, 2);

// 默認(rèn)導(dǎo)出
export default function foo() {
  console.log("foo");
}
// 默認(rèn)導(dǎo)入
import someModule from "./utils.js";
ES6 模塊的運(yùn)行機(jī)制與 CommonJS 不一樣。JS 引擎對(duì)腳本靜態(tài)分析的時(shí)候,遇到模塊加載命令import,就會(huì)生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。原始值變了,import加載的值也會(huì)跟著變。因此,ES6 模塊是動(dòng)態(tài)引用,并且不會(huì)緩存值,模塊里面的變量綁定其所在的模塊。

另,在 webpack 對(duì) ES Module 打包, ES Module 會(huì)編譯成 require/exports 來執(zhí)行的。

總結(jié)

JS 的模塊化規(guī)范經(jīng)過了模塊模式、CommonJS、AMD/CMD、ES6 的演進(jìn),利用現(xiàn)在常用的 gulp、webpack 打包工具,非常方便我們編寫模塊化代碼。掌握這幾種模塊化規(guī)范的區(qū)別和聯(lián)系有助于提高代碼的模塊化質(zhì)量,比如,CommonJS 輸出的是值拷貝,ES6 Module 在靜態(tài)代碼解析時(shí)輸出只讀接口,AMD 是異步加載,推崇依賴前置,CMD 是依賴就近,延遲執(zhí)行,在使用到模塊時(shí)才去加載相應(yīng)的依賴。

@Starbucks  2019/03/10

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109063.html

相關(guān)文章

  • JS進(jìn)階篇--RequireJS塊化編程詳解

    摘要:所有依賴這個(gè)模塊的語句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。 1.模塊的寫法 模塊化編程一般都有這么幾個(gè)過渡過程,如下描述。 原始方法 function m1(){   //... } function m2(){   //... } 上面的函數(shù)m1()和m2(),組成一個(gè)模塊。使用的時(shí)候,直接調(diào)用就行了。 這種做法的缺點(diǎn)很明顯:污染了全局變量,無法保證不與...

    妤鋒シ 評(píng)論0 收藏0
  • requirejs塊化編程:前端塊化編程指南

    摘要:好棒,應(yīng)該可以滿足絕大部分公司的變態(tài)需求了額。??梢栽诨卣{(diào)函數(shù)中調(diào)用其方法。。等下會(huì)大幅度減少滴。。。。百度搜索到官網(wǎng)點(diǎn)擊下載對(duì)應(yīng)著自己電腦的版本。??勺詈蟮恼?qǐng)求是這樣的由此可見,。 序言 -# 公司大了,業(yè)務(wù)多了,前端代碼量也逐漸增大,我們漸漸的依賴js實(shí)現(xiàn)的交互越來越多,長期以來會(huì)導(dǎo)致我們的代碼維護(hù)越來越困難,所以依賴的插件也越來越多。。比如這樣頁面中有大量的js外鏈引入。。 ...

    harryhappy 評(píng)論0 收藏0
  • 使用CommonJS,AMD以及CMD編寫塊化JavaScripts

    摘要:模塊化編程首先,我想說說模塊化編程這個(gè)概念當(dāng)我不清楚這個(gè)概念的時(shí)候,其實(shí)說什么模塊化編程多好多好都是懵逼的而我一直不覺得有多好,其實(shí)也是因?yàn)槲覐拈_始寫,就一直都在模塊化編程啊我們寫一個(gè)文件然后我們?cè)谖募幸肴缓笳{(diào)用方法哈哈這樣已經(jīng)是模塊化 模塊化編程 首先,我想說說模塊化編程這個(gè)概念當(dāng)我不清楚這個(gè)概念的時(shí)候,其實(shí)說什么模塊化編程多好多好都是懵逼的而我一直不覺得有多好,其實(shí)也是因?yàn)槲覐?..

    nifhlheimr 評(píng)論0 收藏0
  • 大前端2018現(xiàn)在上車還還得及么

    摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個(gè)屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對(duì)象事件框架選擇框架。核心模塊和對(duì)象全局對(duì)象,,,事件驅(qū)動(dòng),事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對(duì)...

    stormgens 評(píng)論0 收藏0
  • 大前端2018現(xiàn)在上車還還得及么

    摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個(gè)屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對(duì)象事件框架選擇框架。核心模塊和對(duì)象全局對(duì)象,,,事件驅(qū)動(dòng),事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對(duì)...

    mylxsw 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<