无法加载

什么是闭包?

FavoriteLoading收藏

1、概念

各种专业文献的闭包定义都非常抽象,我的理解是: 闭包就是能够读取其他函数内部变量的函数,简单理解就是”定义在一个函数内部的函数”

2、特点

闭包有两个很明显的特点:

1. 函数拥有的外部变量的引用,在函数返回时,该变量仍然处于活跃状态。

2. 闭包作为一个函数返回时,其执行上下文环境不会被销毁,仍处于执行上下文环境中。

在JavaScript中存在一种内部函数,即函数声明和函数表达式可以位于另一个函数的函数体内,在内部函数中可以访问外部函数声明的变量,当这个内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

function fn()
{ var max = 10;
  return function bar(x){ 
    if(x>max){ 
     console.log(x);
    } 
  }; 
}
var f1 = fn();
f1(11);

3、 闭包的用途

1. 结果缓存

在开发过程中,我们可能会遇到这样的场景,假如有一个处理很耗时的函数对象,每次用都会消耗很长时间。我们可以将其处理结果在内存中缓存起来。这样在执行代码时,如果内存中有,则直接返回,如果内存中没有,则调用函数进行计算,更新缓存并返回结果。因为闭包不会释放外部变量的引 用,所以能将外部变量值缓存在内存中。

var cacheBox = (function () {
  //缓存的容器
  var cache = {};
  return {
    searchBox: function (id) {
      //如果在内存中,则直接返回
      if (id in cache) {
        return '查找的结果为:' + cache[id];
      }
      //经过一段很耗时的dealFn()函数处理
      var result = dealFn(id); //更新缓存的结果
      cache[id] = result;
      //返回计算的结果
      return '查找的结果为:' + result;
    },
  };
})(); //处理很耗时的函数
function dealFn(id) {
  console.log('这是一段很耗时的数据库查询操作');
  return id;
} //两次调用cacheBox()函数
console.log(cacheBox.searchBox(1));
console.log(cacheBox.searchBox(1));

2.封装

在JavaScript中提倡的模块化思想是希望将具有一定特征的属性封装到一起,只需要对外暴露对应的函数,并不关心内部实现的逻辑。 例如:我们可以借助数组实现一个栈,只对外暴露出表示入栈和出栈的push()函数和pop函数,以及表示栈长度的size()函数。

var stack = (function () {
  //实现数组模仿栈的实现
  var arr = []; //栈
  return {
    push: function (value) {
      arr.push(value);
    },
    pop: function () {
      return arr.pop();
    },
    size: function () {
      return arr.length;
    },
  };
})();
stack.push('abc');
stack.push('def');
console.log(stack.size());
stack.pop();
console.log(stack.size());

3.定时器问题

定时器setTimeout()函数和for循环在一起使用,总会出现一些意想不到的结果,我们来看一下:

var arr = ['one', 'two', 'three'];
for (var i = 0; i < arr.length; i++) {
  setTimeout(function () {
    console.log(arr[i]);
  }, i * 1000);
}

我们想要间隔一秒输出数组中的数据,但是运行过后却会间隔一秒输出undefined,这是为什么 呢?因为for循环的结束条件i变成3所以arr[3]找不到,结果就是三次undefined。 通过闭包可以解决这个问题:

var arr = ['one', 'two', 'three'];
for (var i = 0; i < arr.length; i++) {
  (function (time) {
    setTimeout(function () {
      console.log(arr[time]);
    }, time * 1000);
  })(i);
}

4.作用域链问题

闭包往往会设计到作用域链的问题,尤其是包含this属性时。

var name = 'outher';
var obj = {
  name: 'inner',
  method: function () {
    return function () {
      return this.name;
    };
  },
};
console.log(obj.method()());

在调用obj.method()函数时,会返回一个匿名函数,而该匿名函数中返回的是this.name,因为引用到了this属性,在匿名函数中,this相当于一个外部变量,所以会形成一个闭包。 在JavaScript中,this指向的永远是函数的调用实体,而匿名函数的实体是全局对象window,因此 输出全局变量name的值是outer。 如果想要输出obj对象自身的name属性,就要改变this的指向,将其指向obj对象本身。

var name = 'outher';
var obj = {
  name: 'inner',
  method: function () {
    //用_this保存obj中的this
    var _this = this;
    return function () {
      return _this.name;
    };
  },
};
console.log(obj.method()());

4、闭包总结

闭包如果使用合理,在一定程度上能够提高代码的执行效率,如果使用不合理,则会造成内存浪 费,性能下降,接下来我们总结一下闭包的优缺点。

闭包的优点

1. 保护函数内变量的安全,实现封装,防止变量流入其他环境发生命名冲突,造成环境污染。

2. 在适当的时候,可以在内存中维护变量并缓存,提高执行效率。

闭包的缺点

1. 消耗内存,通过来说,函数的活动对象会随着执行的上下文环境一起被销毁,但是由于闭包引用的 是外部函数的活动对象,因此这个活动对象无法被销毁,这意味着闭包比普通函数要消耗更多的内 存。

2. 内存泄漏,在IE9之前,如果闭包的作用域链中存在DOM对象,则意味着该DOM对象无法被销毁, 造成内存泄漏。

留下评论

微信:15182814906

QQ:1548902957

邮箱:1548902957@qq.com