本文通过具体实例演示了JavaScript中变量冲突的产生原因及解决方案。从使用匿名函数隔离作用域,到利用全局变量通信,再到引入Hash对象和命名空间管理,逐步解决变量覆盖与维护难题。文章最后总结了避免冲突的三大核心策略,帮助开发者编写更健壮、易维护的代码。
本文通过具体实例,详细解析了如何在JavaScript开发中有效避免变量与函数冲突,供相关技术人员参考。
匿名函数隔离作用域
[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,从而引发冲突。为解决此问题,可采用匿名函数将脚本包裹起来,将变量的作用域限制在匿名函数内部。
//功能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
})();
Hash对象管理全局变量
[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]为代码添加必要的注释
以上就是本文的详细内容,希望对大家的学习有所帮助。