番外篇----js闭包
发布在Sass 学习2014年5月10日view:3829
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

闭包:有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式是在函数中定义另一个函数 当某个函数被调用时,会创建一个执行环境以及相应的作用域链,在一个函数内部创建另一个函数时,内部函数会拥有外部函数的活动对象(活动对象是指保存着作用域内所有的变量和函数)

例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;
}

注:以上代码片段非原创,此文章属于读书笔记加自己理解

评论
发表评论
暂无评论
PUBLISHED IN
Sass 学习

学习sass

友情链接 大搜车前端团队博客
我的收藏