闭包:有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式是在函数中定义另一个函数 当某个函数被调用时,会创建一个执行环境以及相应的作用域链,在一个函数内部创建另一个函数时,内部函数会拥有外部函数的活动对象(活动对象是指保存着作用域内所有的变量和函数)
例1
function compare(value1,value2){
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
var result = compare(5,10);
当执行以上代码时,会先创建arguments,value1,value2 的活动对象,然后在全局环境中会compare()和result 对象
例子2
function createComparisonFunction(propertyName){
return function(object1,object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0 ;
}
}
}
var compareNames = createComparisonFunction("name");
var result = compareNames({name:"Nicholas"},{name:"Greg"});
compareNames = null;
compareNames 会将 createComparisonFunction的活动对象包含在他的作用域链中,在执行以上代码时,compareNames的作用域链会初始化包含createComparisonFunction和全局对象的活动对象 这样compareNames就可以沿着作用域链一级一级的访问作用域链中的变量,当createComparisonFunction返回后其作用域链会被销毁,但是活动对象会保存在内存中 所有 要将compareNames设置为null,以便于内存回收
调用内部函数,逃脱定义它们的内部函数,创建闭包的方法
方法1 定义全局变量
var globalVar;
function outerFn(){
console.log("outer Function");
function innerFn(){
console.log("inner Function");
}
globalVar = innerFn;
}
方法2 利用返回值,可以返回匿名函数或者有名函数
function outerFn(){
console.log("outerFunction");
function innerFn(){
console.log("inner Function");
}
return innerFn;
}
变量作用域 js没有块级作用域的概念,所以在块语句定义的变量,实际上包含在函数中,即使在块级语句中重新定义变量也没用,编译器会自动忽略(重新初始化除外)
function outputNumber(count){
for(var i =0;i<count;i++){
alert(i);
}
alert(i);//5
}
outputNumber(5);
解决方案:将语句放到立即执行语句中
function outputNumber(count){
(function(){
for(var i =0;i<count;i++){
alert(i);
}
})();
}
理解闭包和变量的关系
function createFunctions(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function(){
return i;
}
return result;
}
}
以上运行的结果是result 中保存的都是10,原因是它们引用的都是同一个活动对象,当循环结束时i的值为10,所有保存的都是10 解决方案1 创建另一个匿名函数并返回
function createFunctions(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function(num){
return function(){
return num
}
}(i);
}
return result;
}
console.log( createFunctions());
解决方法2 用jquery 中的$.each 实现,如下实现:函数的参数类似于函数中定义的变量,所以每次循环的value都不同
$(document).ready(function(){
var result = new Array();
$.each([0,1,2,3],function(index,value){
result[index] = value;
})
console.log(result);
});
解决方法3 在for循环内创建一个新函数来分配i;
$(document).ready(function(){
var result = new Array();
for(var i=0;i<10;i++){
(function(value){
result[i] = value;
})(i);
}
console.log(result);
});
解决方法4 jquery的 .on()方法,该方法接受一个对象参数,改参数以event.data的形式传入事件处理程序中
$(document).ready(function(){
var result = new Array();
for(var i = 0 ;i<10;i++){
$element.on("sonmeEvent",{value:i},function(event){ //此段为伪代码
result[i] = event.data.value;
});
}
console.log(result);
});
闭包引起的内存泄露风险---使用闭包时容易出现引用循环 情形1,显示引用,如下例子,innerFn引用了outerVar,outerVar引用了innerFn;由于js的引用计数的垃圾回收机制,内存无法释放
function outerFn(){
var outerVar ={};
function innerFn(){
console.log(outerVar);
}
outerVar.fn = innerFn;
return innerFn;
}
情形2 隐式引用,由于闭包的关系,innerFn拥有outerFn的活动对象outerVar,也就是说outerVar位于innerFn的封闭环境中,outerVar被innerFn所引用,于是又出现了循环引用
function outerFn(){
var outerVar ={};
function innerFn(){
console.log("hello");
}
outerVar.fn = innerFn;
return innerFn;
}
注:以上代码片段非原创,此文章属于读书笔记加自己理解