對JS學習和使用的深入,漸漸的發現兩個問題:
(1)我之前寫的JS代碼重用性很低
(2)功能分散,舉個栗子,我要獲得當前日期,也要取得當前日期加1也就是明天的日期,之前我作為兩個獨立互不相干的函數去寫,但現在想想,其實他們都可以歸為一個日期對象的兩個方法,把它們綁定在一個物件上,作為它的兩個方法,不是更好麼?
1. 首先,我們要明白為什麼要用模組化?
功能都是為了解決需求的。模組化可以帶來的優點有以下幾點:
(1)可維護性。舉個例子,如果我們把未使用模組化的程式碼比喻為油和水混合在了一起,模組化之後的程式碼就好像油和水的分層,油就是油,水就是水,這樣的程式碼層次清晰,功能分明。似乎用油和水必然分層的現象來指JS模組化的大勢所趨也很合適。
(2)命名空間。這裡需要談到JS的作用域。又涉及到了作用域鏈。如果對作用域鏈不熟悉的同學可以移步我的另一篇文章「理解JavaScript中的作用域鏈」。 JS中是靠函數來區分作用域的。每個函數都有一個作用域鏈。如果我們把所有的程式碼都揉在一起,程式碼行數少還行,多了就難免會造成「命名空間污染」。
(3)可重複用性。當我們明白了命名空間,借助命名空間我們就可以實現對模組程式碼的封裝,這樣我們就可以在任何我們需要這個功能的時候直接去引用這個功能模組。
接下來,用我的JS程式碼之路示範如何讓程式碼模組化:
(1)原始時代:把所有的要用到的JS程式碼都堆砌在該頁面的一對<script>標籤中。 </script>
function f1(){ //函数体 } function f2(){ //函数体 }
這樣寫的缺點:程式碼基本上沒有什麼複用性可以,應該還會存在和頁面隅合度太高的問題。還需要去考慮各種作用域的問題。
(2)古時代:想法就是把模組寫成一個物件。例如我們要寫一個能控制頁面音樂播放,停止,下一首,上一首的功能。就可以封裝一個musicPlayer對象
// 将基本的方法封装起来 var musicPlayer = { var musicDom = null, //播放器对象 var musicList = [], //存放歌曲列表 // 初始化音乐播放器 var init = function(){ }, // 添加一首歌曲 var add = function(src){ }, // 根据数组下标决定播放哪一首,索引index从0开始 var play = function(index){ }, // 暂停播放 var stop = function(){ }, // 下一首 var next = function(){ }, // 上一首 var prev = function(){ } };
這時候,就已經可以稱之為一個模組了,在全局作用域中,我們只向window對像上綁定了一個musicPlayer對象,之後我們就可以使用musicPlayer加'. '的形式來呼叫裡面的方法。如「musicPlayer.init();」
這種方式也有一個缺點,就是我們不能去控制我們想要暴露的內容,並且在外部可以改寫musicPlayer物件的內部方法和變數。
(3)現代。包含IIFE(立即執行函數),放大模式,寬放大模式,輸入全域變數
除了IIFE,其他的三種我之前都沒有接觸過,在這裡簡單談談我的理解。
- IIFE(Immediately-Invoked Function Expression)
// 创建一个立即执行的匿名函数 // 该函数返回一个对象,包含你要暴露的属性 // 如下代码如果不使用立即执行函数,就会多一个属性i // 如果有了属性i,我们就能调用counter.i改变i的值 // 对我们来说这种不确定的因素越少越好 var counter = (function(){ var i = 0; return { get: function(){ return i; }, set: function( val ){ i = val; }, increment: function() { return ++i; } }; }()); // counter其实是一个对象 counter.get(); // 0 counter.set( 3 ); counter.increment(); // 4 counter.increment(); // 5 counter.i; // undefined i并不是counter的属性 i; // ReferenceError: i is not defined (函数内部的是局部变量)
從以上的程式碼可以看出,counter中其中並沒有i這個屬性,它只有return 中暴露出來的內容。這樣我們就對i實現了私有。
- 放大模式
我對放大模式的理解就是把原函數當作參數傳遞到IIFE中,然後給原函數添加新的擴展方法,把擴展後的函數返回。就實現了對原函數的「放大」。
var module1 = (function (mod){ mod.m3 = function () { //... }; return mod; })(module1);
在這個例子中,就為module1增加了一個新的方法m3並回傳。
- 寬放大模式
寬放大模式就是在放大模式的基礎上新增了一個特性:IIFE的參數可以是空物件。
var module1 = ( function (mod){ //... return mod; })(window.module1 || {});
IIFE傳入的參數:如果window.module1有定義,就傳入該參數,如果為undefined就傳入一個空物件。
- 輸入全域變數
如果我們要在IIFE內使用全域變量,最好把全域變數透過參數傳遞進去。
var module1 = (function ($, YAHOO) { //... })(jQuery, YAHOO);
如上所示的程式碼將jQuery和YUI兩個函式庫的全域變數當作參數傳入了module1。