模塊化(翻譯)

JS模塊:初學者指南

如果你是一個學習js的新手,一些專業(yè)的術(shù)語:模塊化打包與模塊化載入,webpack與browserify,AMD和CommonJS.這些問題會讓人很迷惑.

js模塊化系統(tǒng)可能比較難以理解,但是這些對于網(wǎng)絡(luò)開發(fā)者確實必須的.

在這片文章中,我會盡量用平實的語言來解釋這些抽象的詞匯,希望對你有所幫助!

第一部分:能有人給我解釋一下什么是模塊化?

好的作者會把他們的書分為章節(jié)和段落,而好的碼農(nóng)會把他們的程序模塊化.

就像一片文章的章節(jié),模塊就是一組單詞.

對于一個好的模塊,是具有完備的,不同的功能的,他們可以被拖拽,移除,添加而不會使系統(tǒng)分裂.

為什么使用模塊

針對不規(guī)則的,相互依賴的代碼使用模塊化會有很多的好處,我認為最重要的點:

  1. 可維護性:定義了一個完備的模塊,一個好的模塊旨在于減少各個部分的依賴關(guān)系,他可以獨立的成長和發(fā)展.更新一個單獨的模塊兒會更容易,當其已經(jīng)從其他代碼解耦后,

    回到書的例子,如果你想要更新書的一個章節(jié),如果一個小的章節(jié)變化需要你去更改相關(guān)的每個章節(jié),name這將是一個噩夢,但是,如果你以模塊化的方式去寫每個章節(jié)會改善而不影響其他的章節(jié)

  2. 命名空間:在JavaScript中,外層函數(shù)作用域外的變量是全局的(意味著每個人可以訪問),由于這一點就很容易造成命名污染:完全不相關(guān)的代碼共享使用了全局變量.

    在不相關(guān)的代碼之間共享全局變量在開發(fā)當中是萬萬不適用的.

    在之后我們將看到模塊化允許我們通過創(chuàng)建變量的私人空間避免命名污染.

  3. 可重用性:這里是事實:我們都會復制我們之前所寫的代碼到新的項目當中,舉個例子,我們假設(shè)你復制了你之前所寫的所有的工具方法到你當前的項目.

    這都是很好的,但是如果你找到了一個更好的方法去更新這些代碼,你得去回顧并且熟記跟新他,

    顯然這個是非常浪費時間的,難道沒有更容易的方法--等等--一個模塊我們可以不斷的重復使用嗎?

如何合并模塊

有很多方法去合并模塊到你的代碼當中,來大概瀏覽一下:

模塊化模型

模塊化模型常被用作模擬類的概念,我們可以存儲公共和私用的方法和變量在一個單獨的項目當中,就像在Java和python當中使用class一樣,允許我們創(chuàng)建一個們想要展示的公共的API方法,仍然可以封裝私人變量和方法在一個閉包的作用域.

有幾種方法去實現(xiàn)模塊化模型,在第一個例子當中,我將要用一個匿名的閉包,這將幫助我們達成我們的目標通過放置我們的到嗎在匿名函數(shù)當中(記住:在JavaScript當中,函數(shù)是唯一的方法創(chuàng)建新的作用域)

例子1:匿名閉包

(
  function(){
    //保存這些變量在這個閉包的作用域當中
    var myGrades = [ 93,95,88,0,55,91];
    var average  = function(){
      var totle = myGrades.reduce(function(acu,item){
        return acu + item;
      },0);
      return 'Your average gride is '+totle/myGrades.length+',';
    }
    var failing = function(){
      var failingGrade = myGrades.filter(function(item){
        return item<70;
      });
      return 'You failed'+failingGrade.length+'times.';
    }
    console.log(failing());
  }());
//You failed 2 times.

通過這個構(gòu)造器,我們的匿名函數(shù)有了他自己的運行環(huán)境or閉包,然后立即執(zhí)行.我們隱藏了變量從全局的命名空間.

這種方法最好的一點是你可以使用本地的變量inside這個函數(shù)不會偶然的復寫全局變量,但是仍然可以訪問全局變量,就像:

var global = 'Hello,I am a global variable :)';
(function(){
  var myGrades = [93,95,98,0,55,91];
  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item}, 0);
    
    return 'Your average grade is ' + total / myGrades.length + '.';
  }

  var failing = function(){
    var failingGrades = myGrades.filter(function(item) {
      return item < 70;});
      
    return 'You failed ' + failingGrades.length + ' times.';
  }

  console.log(failing());
  console.log(global);
}())
// 'You failed 2 times.'
// 'Hello, I am a global variable :)'

記得匿名函數(shù)的括號是必須的,因為語句開始的關(guān)鍵詞function重視被認為是函數(shù)聲明,因此,包裹的圓括號創(chuàng)建了一個函數(shù)表達式.

例子2:全局導入

另一個比較流行的方法使用庫就像jq的全局導入,和我們剛剛看到的匿名閉包是一樣的,以參數(shù)的形式傳入全局:

(function(globalVariable){
  var privateFunction = function(){
    console.log('shhhh,this is private')
  }
  globalVariable.each = function(collection,iterator){
    if(Array.isArray(collection)){
      for(var i=0;i< collection.length;i++){
        iterator(collection[i],i,collection);
      }
    }else{
      for(var key in collection){
        iterator(collection[key],key,collection);
      }
    }
  };
  globalVariable.filter = function(collection,test){
    var filtered = [];
    globalVariable.each(collection,function(item){
      if(test(item)){
        filter.push(item);
      }
    });
    return filtered;
  };
  globalVariable.map = function(collection,iterator){
    var mapped = [];
    globalUtils.each(collection,function(value,key,collection){
      mapped.push(iterator(value))
    });
    return mapped;
  };
  globalVariable.reduce = function(collection,iterator,accumulator){
    var startingValueMissing = accumulator === undefined;
    globalVariable.each(collection,function(item){
      if(startingValueMissing){
        accumulator = item;
        startingValueMissing = false;
      }else{
        accumulator = iterator(accumulator,item);
      }
    });
    return accumulator;
  }
  
}(globalVariable))

在這個例子中,globalVariable是唯一一個全局變量,這種方法的好處在于可以先聲明全局變量,使他更容易被辨認.

例子3:對象接口

另外一種方法是創(chuàng)建一個模塊使用自備的對象接口,下面這樣子:

var myGradesCalculate = (function(){
  var myGrades = [93, 95, 88, 0, 55, 91];
  return {
    average:function(){
      var total = myGrades.reduce(function(acc,item){
        return acc + item;
      },0);
      return 'Your average grade is'+total/myGrade.length+'.';
    },
    failing:function(){
      var failingGrades = myGrades.filter(function(item){
        return item<70;
      });
      return 'You"ve failed'+failingGrades.length + 'times.'
    }
  }
})();
myGradesCalculate.failing();
myGradesCalculate.average();

正如你所看到的,這種方法讓我們決定了什么變量/方法是我們想要保持private,什么方法使我們想要返回并暴露的.

例子4:顯示模塊模型

這個和以上的方法是非常的相似的,除了他確信讓所有的方法和變量保持私有直到explicitly exposed:

var myGradesCalculate = (function(){
  var myGrades = [93, 95, 88, 0, 55, 91];
  var average = function(){
    var total = myGrades.reduce(function(acc,item){
      return acc+item;
    },0);
    return 'Your average grade is '+total/myGrades.length+'.';
  };
  var failing = function(){
    var failingGrades = myGrades.filter(function(item){
      return item<70;
    });
    return 'you failed'+failingGrades.length+'times.';
  };
  return {
    average:average,
    failing:failing
  }
})();
myGradesCalculate.failing();
myGradesCalculate.average();

看上去有很多東西需要掌握,但是這只是冰山一角.

CommonJS 和AMD

以上的方法都有一個共同點:一個全局變量包裹他的代碼在函數(shù)內(nèi),因此創(chuàng)建私人命名空間為他自己使用一個封閉的作用域.

然而每個方法都按自己的方式是生效,他們有自己的downsides.

首先,作為一個開發(fā)者,你需要直到你的文件載入的順序和依賴,例如:你正在使用backbone到你的項目,所以你在你的文件中包含了backbone的源碼.

但是,由于backbone對underscoreJS有很深的依賴,所以backbone的script的標簽不能放在underscore的script的標簽的前面.

作為一個開發(fā)者,管理依賴并把這些弄正確也是一個頭疼的問題.

另一個缺憾就是他們?nèi)匀粫鹈臻g的沖突,比如萬一你的兩個模塊有相同的名字怎么辦?或者說你有同一個模塊的兩個不同的版本,但是你兩個都需要?

所以,你會疑惑:我們可以設(shè)置一種方式去請求模塊的接口而不是直接通過全局作用域?

幸運的是,答案是yes,

有兩種比較流行的方式:commonJS和AMD

CommonJS

AMD

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容