详解JavaScript作为命名空间的函数
8.5 作为命名空间的函数
函数作用域,在函数中声明的变量在整个函数体内都是可见的,函数外部是不可见的。
不在任何函数内声明的变量是全局变量,整个JS程序中都是可见的。
JS中无法声明只在一个代码块内可见的变量,基于这个原因,我们常常简单的定义一个函数用作临时的命名空间,在这个命名空间内定义的变量都不会污染到全局命名空间。
比如:假设你写一段JS模块代码,这段代码是共用的,定义了一个存储中间计算结果的变量,问题来了,放到不同程序执行时,无法得知这个变量是否已经创建,若存在这个变量,将会和代码冲突,解决方法当然是将代码放入一个函数内,然后调用这个函数。这样全局变量就变成了函数内的局部变量。
function mymodule(){
//这个模块所使用的所有变量都是局部变量
//而不是污染全局命名空间
}
mymodule();
这样还是太麻烦,可以直接定义一个匿名函数,并在单个表达式中调用它:
(function(){
//模块代码
}());//结束函数并立即调用它
这种定义匿名函数并立即在单个表达式中调用它的写法非常常见,已经成为一种惯用法了。
8.5.1特定场景下返回带补丁的extend()版本
//定义一个拓展函数,用来将第二个以及后续参数复制到第一个参数
//这里我们处理了IE bug:在多数IE版本中,如果o的属性拥有一个不可枚举的同名属性,则
//for/in循环不会枚举对象o的可枚举属性,也就是说将不会正确的处理诸如toString的属性。
//除非我们显式检测它
//将函数的返回值赋值给extend
var extend = (function() {
//修复之前检查是否存在bug
for (var p in {
toString: null
}) {
//一个简单版本的extend()函数
return function extend(o) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var prop in source) {
o[prop] = source[prop];
}
}
return o;
};
}
//返回另一个版本的extend()函数
return function patched_extend(o) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i]; //复制所有的可枚举属性
for (var prop in source) {
o[prop] = source[prop]; //现在检查特殊属性
for (var j = 0; j < protoprops.length; j++) {
prop = protoprops[j];
if (source.hasOwnProperty(prop)) {
o[prop] = source[prop];
}
}
}
}
return o;
};
//需要检查特殊的属性列表
var protoprops = ["toString", "valueOf", "constructor", "hasOwnProperty", "isPrototypeOf",
"propertyIsEnumerable", "toLocaleString"
];
}());