如何避免javascript中的衝突

小火柴的藍色理想發表於2015-09-10


[1]工程師甲編寫功能A

var a = 1;
var b = 2;
alert(a+b);//3

 

[2]工程師乙新增新功能B

var a = 2;
var b = 1;
alert(a-b);//1

 

[3]上一步中,工程師乙在不知情的情況下,定義了同名變數a,產生衝突。於是使用匿名函式將指令碼包起來,讓變數作用域控制在匿名函式之內。

//功能A
(function(){
  var a = 1;
  var b = 2;
  alert(a+b);//3
})();
//功能B
(function(){
  var a = 2;
  var b = 1;
  alert(a-b);//1
})();

 

[4]此時有了新需求,網頁中加入功能C,且需要用到功能A中的變數b。於是在window作用域下定義一個全域性變數,把它作為一個橋樑,完成各匿名函式之間的通訊

//全域性變數
var str;
//功能A
(function(){
  var a = 1;
  //將b的值賦給str
  var b = str = 2;
  alert(a+b);//3
})();
//功能B
(function(){
  var a = 2;
  var b = 1;
  alert(a-b);//1
})();
//功能C
(function(){
  //將str的值賦給b
  var b = str;
  alert(b);//2
})();

 

[5]但如果功能C還需要功能A中的變數a呢,這時就需要再定義一個全域性變數

//全域性變數
var str,str1;
//功能A
(function(){
  //將a的值賦給str1
  var a = str1 = 1;
  //將b的值賦給str
  var b = str = 2;
  alert(a+b);//3
})();
//功能B
(function(){
  var a = 2;
  var b = 1;
  alert(a-b);//1
})();
//功能C
(function(){
  //將str1的值賦給a
  var a = str1;
  //將str的值賦給b
  var b = str;
  alert(a*b);//2
})();

 

[6]但隨著匿名函式之間需要通訊的變數越多,需要的全域性變數也就越多。因此需要嚴格控制全域性變數的數量,使用hash物件作為全域性變數,可以將需要的變數都作為物件的屬性,可以保證全域性變數的個數足夠少,同時擴充性非常好

//全域性變數
var GLOBAL = {};
//功能A
(function(){
  //將a的值賦給GLOBAL.str1
  var a = GLOBAL.str1 = 1;
  //將b的值賦給GLOBAL.str
  var b = GLOBAL.str = 2;
  alert(a+b);//3
})();
//功能B
(function(){
  var a = 2;
  var b = 1;
  alert(a-b);//1
})();
//功能C
(function(){
  //將GLOBAL.str1的值賦給a
  var a = GLOBAL.str1;
  //將GLOBAL.str的值賦給b
  var b = GLOBAL.str;
  alert(a*b);//2
})();

 

[7]但如果新增功能D,功能D需要和功能B通訊,並使用功能B指令碼中的變數a,開發功能D的是工程師丁

//全域性變數
var GLOBAL = {};
//功能A
(function(){
  //將a的值賦給GLOBAL.str1
  var a = GLOBAL.str1 = 1;
  //將b的值賦給GLOBAL.str
  var b = GLOBAL.str = 2;
  alert(a+b);//3
})();
//功能B
(function(){
  //將a的值賦給GLOBAL.str1
  var a = GLOBAL.str1 = 2;
  var b = 1;
  alert(a-b);//1
})();
//功能C
(function(){
  //將GLOBAL.str1的值賦給a
  var a = GLOBAL.str1;
  //將GLOBAL.str的值賦給b
  var b = GLOBAL.str;
  alert(a*b);//2
})();
//功能D
(function(){
  //將GLOBAL.str1的值賦給a
  var a = GLOBAL.str1;
  alert(a*2);//4
})();

 

[8]由於工程師丁只關心自己的匿名函式和功能B的匿名函式,使用GLOBAL.str卻無意中覆蓋了功能A中設定的同名變數,導致功能C出錯。於是使用名稱空間來解決這個問題,在不同的匿名函式下,根據功能宣告一個不同的名稱空間,然後每個匿名函式中的GLOBAL物件的屬性都不要直接掛在GLOBAL物件上,而是掛在此匿名函式的名稱空間下

//全域性變數
var GLOBAL = {};
//功能A
(function(){
  GLOBAL.A = {};
  //將a的值賦給GLOBAL.A.str1
  var a = GLOBAL.A.str1 = 1;
  //將b的值賦給GLOBAL.A.str
  var b = GLOBAL.A.str = 2;
  alert(a+b);//3
})();
//功能B
(function(){
  GLOBAL.B = {};
  //將a的值賦給GLOBAL.B.str1
  var a = GLOBAL.B.str1 = 2;
  var b = 1;
  alert(a-b);//1
})();
//功能C
(function(){
  //將GLOBAL.A.str1的值賦給a
  var a = GLOBAL.A.str1;
  //將GLOBAL.A.str的值賦給b
  var b = GLOBAL.A.str;
  alert(a*b);//2
})();
//功能D
(function(){
  //將GLOBAL.B.str1的值賦給a
  var a = GLOBAL.B.str1;
  alert(a*2);//4
})();

 

[9]如果同一個匿名函式中的程式非常複雜,變數名很多,名稱空間還可以進一步擴充,生成二級名稱空間

//以功能A為例
(function(){
  var a = 1, b = 2;
  GLOBAL.A = {};
  GLOBAL.A.CAT = {};
  GLOBAL.A.DOG = {};
  GLOBAL.A.CAT.name = 'mimi';
  GLOBAL.A.DOG.name = 'xiaobai';
  GLOBAL.A.CAT.move = function(){};
  GLOBAL.A.str1 = a;
  GLOBAL.B.str = b;    
})();

 

[10]因為生成名稱空間是個非常常用的功能,進一步將生成名稱空間的功能定義成一個函式,方便呼叫,完整版本改寫後的程式碼如下

var GLOBAL = {};
GLOBAL.namespace = function(str){
  var arr = str.split('.');
  var o = GLOBAL;
  var start = 0;
  if(arr[0] == 'GLOBAL'){
    start = 1;
  }else{
    start = 0;
  }
  for(var i = start; i < arr.length; i++){
    o[arr[i]] = o[arr[i]] || {};
    o = o[arr[i]];
  }
};
//功能A
(function(){
  var a = 1;
  var b = 2;
  GLOBAL.namespace('A.CAT');
  GLOBAL.namespace('A.DOG');
  GLOBAL.A.CAT.name = 'mimi';
  GLOBAL.A.DOG.name = 'xiaobai';
  GLOBAL.A.CAT.move = function(){};
  GLOBAL.A.str1 = a;
  GLOBAL.A.str = b;    
  alert(a+b);//3
})();
//功能B
(function(){
  var a = 2;
  var b = 1;
  GLOBAL.namespace('B');
  GLOBAL.B.str1 = a;
  alert(a-b);//1
})();
//功能C
(function(){
  var a = GLOBAL.A.str1;
  var b = GLOBAL.A.str;
  alert(a*b);//2
})();
//功能D
(function(){
  var a = GLOBAL.B.str1;
  alert(a*2);//4
})();

 

[11]程式碼的衝突問題已經解決了,但可維護性並不強。比如,現在需要讓工程師甲去修改功能B。因為工程師甲寫的指令碼是關於功能A的,他並不知道功能B的指令碼情況。為了改善這種局面,需要給程式碼新增適當的註釋。

var GLOBAL = {};
GLOBAL.namespace = function(str){
  var arr = str.split('.');
  var o = GLOBAL;
  var start = 0;
  if(arr[0] == 'GLOBAL'){
    start = 1;
  }else{
    start = 0;
  }
  for(var i = start; i < arr.length; i++){
    o[arr[i]] = o[arr[i]] || {};
    o = o[arr[i]];
  }
};
/*
* @method 功能A:實現加法運算
* @author 工程師甲
* @connect 1234567
* @time 2015-01-01
*/

(function(){
  var a = 1;
  var b = 2;
  GLOBAL.namespace('A.CAT');
  GLOBAL.namespace('A.DOG');
  GLOBAL.A.CAT.name = 'mimi';
  GLOBAL.A.DOG.name = 'xiaobai';
  GLOBAL.A.CAT.move = function(){};
  GLOBAL.A.str1 = a;
  GLOBAL.A.str = b;    
  alert(a+b);//3
})();
/*
* @method 功能B:實現減法運算
* @author 工程師乙
* @connect 1234567
* @time 2015-01-01
*/
(function(){
  var a = 2;
  var b = 1;
  GLOBAL.namespace('B');
  GLOBAL.B.str1 = a;
  alert(a-b);//1
})();
/*
* @method 功能C:實現乘法運算
* @author 工程師丙
* @connect 1234567
* @time 2015-01-01
*/
(function(){
  var a = GLOBAL.A.str1;
  var b = GLOBAL.A.str;
  alert(a*b);//2
})();
/*
* @method 功能D:實現乘2運算
* @author 工程師丁
* @connect 1234567
* @time 2015-01-01
*/
(function(){
  var a = GLOBAL.B.str1;
  alert(a*2);//4
})();

 

讓javascript不再衝突,需要

  [1]避免全域性變數的泛濫

  [2]合理使用名稱空間

  [3]為程式碼新增必要的註釋

相關文章