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

資訊專欄INFORMATION COLUMN

javascript 之模塊化篇

huangjinnan / 2066人閱讀

摘要:模塊的加載第一個(gè)參數(shù),是一個(gè)數(shù)組,里面的成員就是要加載的模塊第二個(gè)參數(shù),則是加載成功之后的回調(diào)函數(shù)。異步加載,瀏覽器不會(huì)失去響應(yīng)它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后,才會(huì)運(yùn)行,解決了依賴性的問(wèn)題。

什么是模塊化?

模塊化就是把系統(tǒng)分離成獨(dú)立功能的方法,這樣我們需要什么功能,就加載什么功能。

優(yōu)點(diǎn):
可維護(hù)性:根據(jù)定義,每個(gè)模塊都是獨(dú)立的,良好設(shè)計(jì)的模塊會(huì)盡量與外部的代碼撇清關(guān)系,以便于獨(dú)立對(duì)其進(jìn)行改進(jìn)和維護(hù)。
可復(fù)用性:可以重復(fù)利用,而不用經(jīng)常復(fù)制自己之前寫過(guò)的代碼

原始JS開(kāi)發(fā)問(wèn)題

1、污染全局變量
//a.js 文件:

var test1="aaaaaa";
//b.js 文件
var test1="bbbbbb";
 
console test1 輸出"bbbbbb";悲劇啊

2、命名沖突

//a.js 文件:
function fun(){
    console.log("this is b");
}
 //b.js 文件
 
function fun(){
    console.log("this is b");
}
//main.js 文件



小張?jiān)赼.js定義了fun(),小李在b.js又定義了fun(),a,b被小王引入到main.js,執(zhí)行fun(),輸出this is b; 

3、依賴關(guān)系
b.js依賴a.js,標(biāo)簽的書寫順序必須是:



這樣在多人開(kāi)發(fā)的時(shí)候很難協(xié)調(diào)啊,令人頭疼的問(wèn)題。

解決沖突的方式

1、使用java式的命名空間
2、變量前加“_”
3、對(duì)象寫法

var module1={
    test1:"aaaaaa",
    fun:function(){
        console.log(this.test1);
    }
}
變量和函數(shù)封裝在對(duì)象里面,使用時(shí),調(diào)用對(duì)象的屬性即可:
module1.fun();//aaaaaa
但是這樣的寫法會(huì)暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫,
module1.test1="cccccc";

4、匿名閉包函數(shù)

var  module1=(function(){
    var test1="aaaaaa";
    var fun=function(){
        console.log("this is a");
    }
    return{
        fun:fun
    }
}());

匿名函數(shù)有自己的作用域,這樣外部代碼無(wú)法讀取 module1 function 里面的變量了,從而也不會(huì)修改變量或者是覆蓋同名變量了,但是還是有缺陷的,module1這個(gè)的變量還是暴露到全局了,而去隨著模塊的增多,全局變量會(huì)越來(lái)越多。
5、全局引入
像jquery庫(kù)使用的全局引入。和匿名閉包函數(shù)相似,只是傳入全局變量的方法不同
(function(window){

var test1="aaaaaa";
window.testFun=function(){//通過(guò)給window添加屬性而暴漏到全局
    console.log(test1);
}

}(window));

通過(guò)匿名函數(shù)包裝代碼,所依賴的外部變量傳給這個(gè)函數(shù),在函數(shù)內(nèi)部可以使用這些依賴,然后在函數(shù)的最后把模塊自身暴漏給window。

3,4,5解決方法都是通過(guò)定一個(gè)全局變量來(lái)把所有的代碼包含在一個(gè)函數(shù)內(nèi),由此來(lái)創(chuàng)建私有的命名空間和閉包作用域。

本文著重介紹幾種廣受歡迎的解決方案:CommonJS,AMD,CMD,ES模塊化。

CommonJs

根據(jù)CommonJs規(guī)范,每個(gè)文件就是一個(gè)模塊,有自己的作用域。在一個(gè)文件里面定義的變量、函數(shù)、類,都是私有的,對(duì)其他文件不可見(jiàn)。

commonJS中模塊可以加載多次,但是只會(huì)在第一次加載的時(shí)候運(yùn)行一次,然后運(yùn)行結(jié)構(gòu)被緩存,再次加載就是讀取緩存的結(jié)果。

CommonJS規(guī)范加載模塊是同步的,也就是說(shuō),加載完成才可以執(zhí)行后面的操作,Node.js主要用于服務(wù)器編程,模塊一般都是存在本地硬盤中,加載比較快,所以Node.js采用CommonJS規(guī)范。

CommonJS規(guī)范分為三部分:module(模塊標(biāo)識(shí)),require(模塊引用), exports(模塊定義),
module變量在每個(gè)模塊內(nèi)部,就代表當(dāng)前模塊;
exports屬性是對(duì)外的接口,用于導(dǎo)出當(dāng)前模塊的方法或變量;
require()用來(lái)加載外部模塊,讀取并執(zhí)行js文件,返回該模塊的exports對(duì)象;

1、commonJs模塊定義

module.exports定義模塊:

//math.js
let add=(x,y)=>{
    return x+y;
}
let sub=(x,y)=>{
    return x-y;
}

module.exports={
    add:add,
    sub:sub
};

exports 定義模塊:

let add=(x,y)=>{
    return x+y;
}
let sub=(x,y)=>{
    return x-y;
}
exports.add=add;
exports.sub=sub;

注意:不可以直接對(duì)exports賦值,exports=add;

exports和module.exports有什么區(qū)別呢?
在每個(gè)模塊中Node都提供了一個(gè)Module 對(duì)象,代表當(dāng)前模塊。

//console.log(Module);
Module {
  id: ".",
  exports: {},
  parent: null,
  filename: "/Users/zss/node-Demo/my-app/testNOde/b.js",
  loaded: false,
  children: [],
  paths: 
   [ "/Users/zss/node-Demo/my-app/testNOde/node_modules",
     "/Users/zss/node-Demo/my-app/node_modules",
     "/Users/zss/node-Demo/node_modules",
     "/Users/zss/node_modules",
     "/Users/node_modules",
     "/node_modules" 
     ] 
   }

module.exports屬性表示當(dāng)前模塊對(duì)外輸出的接口,其他文件加載該模塊,實(shí)際上就是讀取module.exports變量。
為了方便,Node為每個(gè)模塊提供一個(gè)exports變量,指向module.exports。我們把它們都打印出來(lái)看看究竟,

//test.js
console.log(module.exports);
console.log(exports);
console.log(module.exports===exports);

exports.test = ()=>{
    console.log("exports 1");
};
module.exports.test1 = ()=>{
    console.log("module.exports 1");
};
console.log(module.exports);
console.log(exports);

//輸出:
{}
{}
true
{ test: [Function], test1: [Function] }
{ test: [Function], test1: [Function] }

從上例可以看出:
1.每個(gè)模塊文件一創(chuàng)建,有個(gè)var exports = module.exports = {};使exports和module.exports都指向一個(gè)空對(duì)象。
**2.module是全局內(nèi)置對(duì)象,exports是被var創(chuàng)建的局部對(duì)象,module.exports和exports所指向的內(nèi)存地址相同
所有的exports收集到的屬性和方法,都賦值給了Module.exports,最終返回給模塊調(diào)用的是module.exports而不是exports。**

再舉個(gè)例子:

//test.js
exports.test = ()=>{
    console.log("exports 1");
};
module.exports={
    test:function(){
        console.log("module.exports 1");
    },
    testmodule:()=>{
        console.log("module.exports 2")
    }
}
console.log(module.exports);
console.log(exports);

 
 //輸出
{ test: [Function: test], testmodule: [Function: testmodule] }
{ test: [Function] }

//在index.js文件中調(diào)用test2.js
let a=require("./test2");
a.test();
a.testmodule();
//輸出:
module.exports 1
module.exports 2

所有的exports收集到的屬性和方法,都賦值給了Module.exports,當(dāng)直接把函數(shù)和屬性傳給module.exports時(shí),module.exports與exports不想等了,在調(diào)用時(shí)候,exports的屬性和方法會(huì)被忽略,所以最終返回給模塊調(diào)用的是module.exports而不是exports。

2、模塊分類

NodeJs的模塊分為兩類:
一類是原生模塊,例如http,fs,path 等等。node在加載原生模塊的時(shí)候,不需要傳入路徑,NodeJs將原生模塊的代碼編譯到了二進(jìn)制執(zhí)行文件中,加載速度快。
一類是文件模塊,動(dòng)態(tài)加載模塊,
但是NodeJs對(duì)原生模塊和文件模塊都進(jìn)行了緩存,第二次require時(shí),就是執(zhí)行的內(nèi)存中的文件。

3、commonJs模塊加載規(guī)則

index.js調(diào)用math模塊:

let math=require("./math");
let test=math.add(3,3);
console.log(test);

執(zhí)行index.js 輸出:6;

當(dāng)我們執(zhí)行node index.js的時(shí)候,第一語(yǔ)句就是“require("./math");” 加載 math文件。加載math文件這個(gè)動(dòng)作是由原生模塊module的runMain()實(shí)現(xiàn)的。

有沒(méi)有注意到上面寫的是加載math文件,并沒(méi)有明確指出是js文件。
NodeJS加載文件模塊基本流程:
1、根據(jù)名稱按照‘.js’,‘.node‘,’.json‘的順訊依次查找,如果是.node或者.json的文件最好加上擴(kuò)展名,加載速度快。
2、查找到math.js,讀取js內(nèi)容,將使用function進(jìn)行包裝,這樣可以避免污染全局環(huán)境,該函數(shù)的參數(shù)包括require、module、exports等等參數(shù),以mathi.js為例:

(function(exports,require,module,__filename,__dirname){
        let add=(x,y)=>{
            return x+y;
        }
        let sub=(x,y)=>{
            return x-y;
        }

        module.exports={
            add:add,
            sub:sub
        };

 })

require 方法中的文件查找規(guī)則很復(fù)雜底,在網(wǎng)上copy了一個(gè)圖:

更詳細(xì)的加載規(guī)則可以參考:http://www.infoq.com/cn/artic...

4、commonJs模塊的加載機(jī)制:
//lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
//index.js
var mod=require("./lib");
consoe.log(mod.counter);
mod.incCounter();
consoe.log(mod.counter);

輸出:3
     3

commonJS中模塊加載以后,它的內(nèi)部變化不會(huì)影響其內(nèi)部變量,因?yàn)樗鼈儠?huì)被緩存,所以它輸出的是值的拷貝。

CommonJS規(guī)范比較適用服務(wù)器端,如果是瀏覽器就需要異步加載模塊了,所以就有了AMD,CMD解決方案。

AMD(requireJS)

 AMD是"Asynchronous Module Definition"的簡(jiǎn)寫,也就是異步模塊定義。它采用異步方式加載模塊。通過(guò)define方法去定義模塊,require方法去加載模塊。

AMD模塊定義:
define(function(){
    let add=(x,y)=>{
        return x+y;
    }
    let sub=(x,y)=>{
        return x-y;
    }
    
    return {
        add:add,
        sub:sub
    };
});

如果這個(gè)模塊還需要依賴其他模塊,那么define函數(shù)的第一個(gè)參數(shù),必須是一個(gè)數(shù)組,指明該模塊的依賴。

define([tools],function(){
    //…………………………
})
AMD模塊的加載:

require([module], callback);

第一個(gè)參數(shù)[module],是一個(gè)數(shù)組,里面的成員就是要加載的模塊;第二個(gè)參數(shù)callback,則是加載成功之后的回調(diào)函數(shù)。例如加載math.js。

require([math],function(){
    //……………………
})

require()異步加載math,瀏覽器不會(huì)失去響應(yīng);它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后,才會(huì)運(yùn)行,解決了依賴性的問(wèn)題。

CMD(SeaJS)

玉伯提出的CMD規(guī)范,并開(kāi)發(fā)了前端模塊化開(kāi)發(fā)框架SeaJS,不過(guò)在2015年后SeaJS停止了在github上維護(hù),CMD與AMD用法很相似,但是我個(gè)人更喜歡使用SeaJS,雖然在2016年后也被我拋棄啦。

SeaJs使用:

// 所有模塊都通過(guò) define 來(lái)定義
define(function(require, exports, module) {

   // 通過(guò) require 引入依賴
   var $ = require("jquery");
   var Spinning = require("./spinning");

   // 通過(guò) exports 對(duì)外提供接口
   exports.doSomething = ...

   // 或者通過(guò) module.exports 提供整個(gè)接口
   module.exports = ...

});

有關(guān)于SeaJS與 RequireJS 的異同,可以參考:
https://github.com/seajs/seaj...
https://www.douban.com/note/2...

ES6 模塊化

在es6 之前沒(méi)有模塊化的,為了解決問(wèn)題,提出了commonJS,AMD,CMD,現(xiàn)在ES6模塊化汲取了CommonJS 和 AMD 的優(yōu)點(diǎn),簡(jiǎn)潔的語(yǔ)法,異步加載
它完全可以成為瀏覽器和服務(wù)器通用的模塊化解決方案。

ES6中模塊的定義

ES6 新增了兩個(gè)關(guān)鍵字 export 和 import,export 用于把 模塊里的內(nèi)容 暴露 出來(lái), import 用于引入模塊提供的功能。

export命令輸出變量:

//lib.js
let bar=function(){
    console.log("this is bar funciton");
};

let foo=function(){
    console.log("this is foo function");
};

export {bar,foo}

上面的代碼還有另一種寫法:

export let bar=function(){
    console.log("this is bar funciton");
};

export let foo=function(){
    console.log("this is foo function");
};

export 不止可以導(dǎo)出函數(shù),還可以導(dǎo)出對(duì)象,類,字符串等等

const test="aaa";
const obj={
    str:"hello!"
}
export {test,obj};

注:使用export在尾部輸出變量時(shí),一定要加大括號(hào),

ES6中模塊的加載

import 加載模塊:

 //加載 lib.js文件
 import {bar,foo,test,obj} from "./lib"
 
 foo();//this is foo function

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

上面的是逐一指定要加載的方法,我們還可以使用 * 可以整體加載模塊:

import * as lib from "./lib"
lib.foo();

上面的加載模塊的方式需要知道變量名和函數(shù)名,否則是無(wú)法加載的,我們可以使用export default 命令,為模塊指定默認(rèn)輸出。

//lib.js
let foo=function(){
    console.log("this is foo");
} 
export default foo; 

其他文件加載時(shí),可以為該匿名函數(shù)指定任意名字。

import  lib from "lib";

注:export default 命令適用于指定默認(rèn)模塊的輸出,一個(gè)模塊只能有一個(gè)默認(rèn)輸出,所以export default 只能使用一次。

ES6 模塊運(yùn)行機(jī)制

ES6模塊是動(dòng)態(tài)引用,如果使用import從一個(gè)模塊加載變量(即import foo from "foo"),變量不會(huì)被緩存,而是成為一個(gè)指向被加載模塊的引用。等腳本執(zhí)行時(shí),根據(jù)只讀引用,到被加載的那個(gè)模塊中去取值。
舉一個(gè)NodeJS模塊化的例子:

//lib.js
export let counter = 3;
exoprt function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
//index.js
import {counter,incCounter} from "./lib";
consoe.log(mod.counter);
mod.incCounter();
consoe.log(mod.counter);

輸出:3
     4
調(diào)用 incCounter()方法后,lib 模塊里的counter變量值改變了。

參考:
http://www.cnblogs.com/TomXu/...
http://blog.csdn.net/tyro_jav...
http://javascript.ruanyifeng....
http://www.ruanyifeng.com/blo...
https://zhuanlan.zhihu.com/p/...
https://segmentfault.com/a/11...
http://web.jobbole.com/83761/
http://es6.ruanyifeng.com/#do...
http://www.cnblogs.com/lishux...

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

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

相關(guān)文章

  • 王下邀月熊_Chevalier的前端每周清單系列文章索引

    摘要:感謝王下邀月熊分享的前端每周清單,為方便大家閱讀,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清單系列,并以年月為單位進(jìn)行分類,具體內(nèi)容看這里前端每周清單年度總結(jié)與盤點(diǎn)。 感謝 王下邀月熊_Chevalier 分享的前端每周清單,為方便大家閱讀,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清單系列,并以年/月為單位進(jìn)行分類,具...

    2501207950 評(píng)論0 收藏0
  • javascript知識(shí)點(diǎn)

    摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長(zhǎng)后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會(huì)討論安全的類型檢測(cè)惰性載入函數(shù)凍結(jié)對(duì)象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對(duì)寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...

    Karrdy 評(píng)論0 收藏0
  • 前端面試Js

    摘要:作為構(gòu)造函數(shù)使用,綁定到新創(chuàng)建的對(duì)象。內(nèi)部實(shí)現(xiàn)類和類的繼承構(gòu)造函數(shù)構(gòu)造函數(shù)調(diào)用父類構(gòu)造函數(shù)參考請(qǐng)盡可能詳盡的解釋的工作原理的原理簡(jiǎn)單來(lái)說(shuō)通過(guò)對(duì)象來(lái)向服務(wù)器發(fā)異步請(qǐng)求,從服務(wù)器獲得數(shù)據(jù),然后用來(lái)操作而更新頁(yè)面。 1 . 請(qǐng)解釋事件代理 (event delegation) 當(dāng)需要對(duì)很多元素添加事件的時(shí),可以通過(guò)將事件添加到它們的父節(jié)點(diǎn)通過(guò)委托來(lái)觸發(fā)處理函數(shù)。其中利用到了瀏覽器的事件冒泡機(jī)...

    anyway 評(píng)論0 收藏0
  • 前端每周清單半年盤點(diǎn) Node.js

    摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。對(duì)該漏洞的綜合評(píng)級(jí)為高危。目前,相關(guān)利用方式已經(jīng)在互聯(lián)網(wǎng)上公開(kāi),近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開(kāi)發(fā)教程、工程實(shí)踐、深度閱讀、開(kāi)源項(xiàng)目、巔峰人生等欄目。歡...

    kid143 評(píng)論0 收藏0
  • 前端資源系列(4)-前端學(xué)習(xí)資源分享&前端面試資源匯總

    摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒(méi)想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...

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

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

0條評(píng)論

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