在javascript中定義全局變量有2種方式,本質上是等價的,都是向window對象注入屬性或者方法。
// global.js var g_name = "aty"; window.g_age = 25;
當global.js加載的時候,瀏覽器的全局對象window就會多出2個屬性:g_name和g_age。
我們編寫一個js工具類或者是js框架,通常有2種方式:
方式1:dateUtil.js
(function(window) {
var DateUtils = {};
DateUtils.toString = function(){
alert("toString");
};
// 全局變量
window.DateUtils = DateUtils;
})(window);
這種方式是最常用的,比如JQuery、Underscore等框架都是采用這種結構編寫的。
方式2:stringUtil.js
// 全局變量
var StringUtils = {};
StringUtils.toUpperCase = function(input){
alert("toUpperCase");
}
很顯然stringUtil.js和dateUtil.js都不符合AMD規范,現在我們看看如何通過requireJS進行加載。工程目錄結構如下:
index.html main.js libs --dateUtil.js --stringUtil.js
index.html和main.js的實現代碼如下:
<!doctype html>
<html>
<head>
<title>shim</title>
<meta charset="utf-8">
<script data-main="main.js" src="./../requirejs-2.1.15.js"></script>
</head>
<body>
<div id="div1" style="width:200px;height:200px;"></div>
</body>
</html>
requirejs.config({
baseUrl: 'libs'
});
require(["dateUtil","stringUtil"], function(dateUtil,stringUtil) {
alert(dateUtil===undefined);//true
});
運行index.html,通過F12觀察:

很明顯dateUtil.js和stringUtil.js能夠被requireJS正常加載,但是不能獲取到這2個模塊的返回值。我們修改下index.html,給div注冊事件處理函數,並在事件處理函數中調用stringUtil.js提供的方法:
<!doctype html>
<html>
<head>
<title>shim</title>
<meta charset="utf-8">
<script data-main="main.js" src="./../requirejs-2.1.15.js"></script>
<script>
function test()
{
StringUtils.toUpperCase();
}
</script>
</head>
<body>
<div id="div1" style="width:200px;height:200px;" onclick="test();"></div>
</body>
</html>
點擊div1,可以發現test()函數不會報錯。也就是說,requireJS加載不符合AMD規范的js文件,跟我們直接在html通過<script>標簽加載,沒有太大的差別。js文件中引入的全局變量,依然會存在,依然能夠正常使用。
下面我們看下shim參數的使用方式,我們將main.js修改如下:
requirejs.config({
baseUrl: 'libs',
shim:{
dateUtil:{
deps:[],
exports: 'DateUtils'
},
stringUtil:{
deps:[],
exports: 'StringUtils'
}
}
});
require(["dateUtil","stringUtil"], function(dateUtil,stringUtil) {
stringUtil.toUpperCase();
dateUtil.toString();
});
這段代碼可以正常運行,可以看到:shim參數能夠幫助我們以AMD模塊的方式,使用那些不符合AMD規范的模塊。下面接介紹下:deps和exports的含義。exports很好理解,就是模塊的返回值。main.js中exports的值,一定要與dateUtil.js和stringUtil.js中暴露出的全局變量名稱一致。很顯然dateUtil.js和stringUtil.js這2個模塊的返回值,就是暴露出的全局變量window.DateUtils和window.StringUtils,requireJS框架就是將這些全局變量的值返回,作為模塊的返回結果。如果dateUtil.js或stringUtil.js中暴露了多個全局變量,那么exports可以指定其中任何的一個,作為模塊的返回結果。不過一般的框架,都只會使用1個全局變量,這樣沖突的可能性會減少,畢竟全局變量越少越好。
上面我們編寫的dateUtil.js和stringUtil.js,都不依賴於其他js模塊,所以指定的deps是空數組。下面我們編寫的aplugin.js和bplugin.js都依賴於模塊util.js。
//aplugin.js
(function(window,util) {
var a = {};
a.toString = function(){
alert("a="+util.add(1,2));
};
// 全局變量
window.a = a;
})(window,util);
//bplugin.js
var b = {};
b.toString = function(){
alert("b="+util.add(1,2));
}
//util.js
var util = {};
util.add = function(v1,v2){
return v1+v2;
};
main.js代碼如下,只有設置正確的依賴順序,使用的時候才不會出問題。
requirejs.config({
baseUrl: 'libs',
shim:{
dateUtil:{
deps:[],
exports: 'DateUtils'
},
stringUtil:{
deps:[],
exports: 'StringUtils'
},
aplugin:{
deps:["util"],
exports: 'a'
},
bplugin:{
deps:["util"],
exports: 'b'
}
}
});
require(["stringUtil","dateUtil","aplugin","bplugin"], function(string,date) {
//string.toString();
//date.toString();
var aPl = require("aplugin");
var bPl = require("bplugin");
aPl.toString();
bPl.toString();
});
很顯然util.js也不符合AMD規范,如果A模塊依賴於B模塊,A模塊不符合AMD規范(使用的是全局變量),那么B模塊也必須是使用全局變量,否則會報錯。即如果將util.js改成符合AMD規范的寫法,那么aplugin.js和bplugin.js都會因找不到util對象而報錯。
// 符合AMD規范的util.js
define(function(){
function add(v1,v2)
{
return v1+v2;
}
return {"add":add};
});
最后我們看下shim配置參數中init的作用。init可以指定一個函數主要就是用來避免類庫之間的沖突。由於不符合AMD規范的js文件,會使用全局變量。所以當加載多個模塊的時候存在名字沖突的可能。比如JQuery、UnderScore等框架都會提供一個noConflict()函數來避免名字沖突,noConflict()的實現原理可以參考這篇文章。
我們編寫一個不符合AMD規范的模塊conflict.js,使用了全局變量$E,並提供noConflict方法。
(function(window) {
// 保存之前數據
var _$E = window.$E;
var myplugin = {"name":"aty"};
myplugin.noConflict = function(){
window.$E = _$E;
return myplugin;
};
// 向全局對象注冊$E
window.$E = myplugin;
})(window);
將index.html修改如下,在requireJS加載之前,先定義一個全局變量$E。
<!doctype html>
<html>
<head>
<title>shim</title>
<meta charset="utf-8">
<script>
var $E = "before";
</script>
<script data-main="main.js" src="./../requirejs-2.1.15.js"></script>
</head>
<body>
<div id="div1" style="width:200px;height:200px;" onclick="test();"></div>
</body>
</html>
main.js中代碼如下:
requirejs.config({
baseUrl: 'libs',
shim:{
conflict:{
deps:[],
exports: '$E',
init:function(){
return $E.noConflict();
}
}
}
});
require(["conflict"], function(mayConflict) {
alert(mayConflict.name);
alert(window.$E);//before
});
運行index.html,可以發現conflict.js能夠與之前定義的全局變量$E共存,避免了沖突,這就是通過init實現的。如果沒有定義init,可以看到alert(window.$E)打印的值是undefined。
原文:http://blog.csdn.net/aitangyong/article/details/44225859
