深入理解javascript闭包系列第五篇——闭包的10中形式

前面的话

根据闭包的定义,我们知道,无论通过何种手段,只要将内部函数传递到所在的词法作用域以外,它都会持有对原始作用域的引用,无论在何处执行这个函数都会使用闭包。接下来,我们将详细介绍闭包的10种形式。

一、返回值

最常用的一种形式是函数作为返回值返回。

var F = function(){
    var b = 'local';
    var N = function(){
        return b;
    }
    return N;
}
console.log(F()());

二、函数赋值

一种变形的形式是将内部函数赋值给一个外部变量。

var inner;
var F = function(){
    var b = 'local';
    var N = function(){
        return b;
    };
    inner = N;
};
F(); //相当于调用F函数时,为inner变量绑定了一个内部函数引用
console.log(inner());

三、函数参数

闭包可以通过函数参数传递函数的形式来实现。

//外部定义了一个参数执行函数
var Inner = function(fn){
    fn();
}
var F = function(){
    var b = 'local';
    var N = function(){
        return b;
    }
    Inner(N); //调用这个函数时,执行传入的参数函数
}
F();

四、IIFE

由前面的示例代码可知,函数F()都是声明后立即被调用的,因此可以使用IIFE来代替。但要注意的是,这里的Inner()只能使用函数声明语句的形式,而不能使用函数表达式。(不是很理解)

function Inner(fn){
    console.log(fn());
}

(function(){
    var b = 'local';
    var N = function(){
        return b;
    }
    Inner(N);
})();

五、循环赋值

在闭包问题上,最常见的一个错误就是循环赋值的错误。

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//2

正确的写法

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = (function fn(j){
            return function test(){
                return j;
            }
        })(i);
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//0

六、g(s)etter

我们通过提供getter()和setter()函数来将要操作的变量保存在函数内部,防止其暴露在外部。

var getValue,setValue;
(function(){
    var secret = 0;
    getValue = function(){
        return secret;
    }
    setValue = function(v){
        if(typeof v === 'number'){
            secret = v;
        }
    }
})();
console.log(getValue());//0
setValue(1);
console.log(getValue());//1

七、迭代器

我们经常使用闭包来实现一个累加器

var add = (function(){
    var counter = 0;
    return function() {
        return ++counter;
    }
})();
//上面这个立即执行函数执行完毕时,add现在指向这个匿名函数。
//所以调用这个add,实际上等同于调用匿名函数。
console.log(add()); //1
console.log(add()); //2

类似的,可以使用闭包很方便的实现一个迭代器

function setup(x) {
    var i = 0;
    return function() {
        return x[i++];
    }
}

var next = setup(["a","b","c"]);
//传进去一个数组,然后读取数组中的每一项

console.log(next()); //"a"
console.log(next()); //"b"
console.log(next()); //"c"

八、区分首次

var firstLoad = (function) {
    var _list = [];
    return function(id) {
        if(_list.indexOf(id) >= 0) {
            return false;
        } else {
            _list.push(id);
            return true;
        }
    }
})()

//这个是通过是否是第一次添加,然后对传入的值进行操作,第一次添加就push进数组,并返回true。

九、缓存机制

通过闭包加入缓存机制,使得相同的参数不用重复计算,来提高函数的性能。
未加入缓存机制时的代码

var mult = function() {
    var a = 1;
    for(var i=0; i<arguments.length; i++) {
        a = a * arguments[i]
    }
    return a;
}

加入缓存机制后的代码

var mult = function(){
  var cache = {};
  var calculate = function(){
    var a = 1;
    for(var i = 0,len = arguments.length; i<len; i++){
      a = a * arguments[i];
    }
    return a;
  };
  return function(){
    var args = Array.prototype.join.call(arguments,',');
    if(args in cache){
      return cache[args];
    }

    return cache[args] = calculate.apply(null,arguments);
  }
}()

十、img对象

img对象经常用于数据上报

var report = function(src) {
    var img = new Image();
    img.src = src;
}

report("https://github.com/AFeng521web/photos")

但是,在一些低版本的浏览器中,使用report函数进行数据上报会丢失30%左右的数据包,也就是说,report函数不是每一次都成功的发起了HTTP请求。

原因是img是report函数中的局部变量,当report函数调用结束后,img局部变量会立即被销毁,而此时或许还没来的及发出HTTP请求。所以这次请求就会丢失掉。

现在把img变量用闭包包裹起来,解决请求丢失的问题。

var report = (function() {
    var imgs = [];
    return function(src) {
        var img = new Image();
        imgs.push(img);
        img.src = src;
    }
})()

report("https://github.com/AFeng521web/photos")

这里使用了闭包,当report函数执行完毕时,其执行环境会被销毁,但由于内层函数引用了它其中的变量,产生闭包,所以其变量对象一直存在于内存中。从而很好的解决了数据丢失的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值