一、作用域
js中函数的作用域就是一个函数可访问的变量范围,以一对大括号为一个圈,如下图:
函数B可以访问A中的变量,C也可以访问A中的变量,但B与C之间无法相互访问内部的变量(当然你也可以通过一些特殊的方式实现,如函数相互调用通过参数传递内部变量等等…),A中也无法访问B、C中的参数(别把A当做js文件,它也可以是一个函数,js中是允许在函数中定义函数的哦)。
二、闭包
闭包其实就是在函数内部定义另一个函数以实现函数之间变量的传递(就是把C放到B里去,此时C及C能访问到的变量就形成了一个闭包),这么做的目的是什么呢?它能减少全局变量的创建、函数间参数值的传递,并且具备更好的封装性,举个例子:
function getAdd(base){
var sum = base;
return function(arg){
return sum+arg;
}
}
var add= getAdd(10);
console.log(add(5));
console.log(add(3));
这里定义了一个getAdd方法,他需要传入一个基数,会返回一个函数,调用后获得add方法,第一次打印15,第二次打印13,而普通的方法要么需要定义全局变量,要么每次传入两个参数,在逻辑复杂,全局变量十分多的情况下,可以采用这种方式以减小变量命名冲突的情况(毕竟冲突了编译器也不会报错,很难找到错误的地方)。闭包还有一些常用用法,如匿名函数自调用,将匿名函数中的对象作为属性添加到window对象上:
(function(){
var a = 10;
window.add = add;
function add(arg){
return a+arg;
}
})();
console.log(add(5));
这里有一个直接被调用的匿名函数,其内部定义了一个add函数并作为属性添加到了window对象
上(方法也是可以作为属性添加到对象上的,相当于在对象内定义了一个函数,js是能在运行时动态为一个对象添加方法的)所以在最后一行可以直接调用add方法,当然你也可以在匿名函数调用前创建一个对象:
var obj = {};
此时该对象一定要要一个值,哪怕是“{}”空的(毕竟undefined可不是一个添加属性的对象),然后在匿名函数中将window改为obj就行了,调用时这样调用:
console.log(obj.add(5));
小案例:编写一个可以统计创造出的对象数量的构造器
(function(){
var count = 0;
function _getCount(){
return count;
}
function Person(_name,_age){
++count;
this.name = _name;
this.age = _age;
this.getCount = _getCount;
}
window.Person = Person;
})();
var p = new Person();
console.log(p.getCount());//1
new Person();
new Person();
console.log(p.getCount());//3
这里将本应该是全局变量的count通过闭包进行了封装,通过这样的方式创建全局函数比在对象定义中添加函数效率更高,因为所有对象使用的都是同一个函数。