闭包该怎么理解呢?

前言

怀着一颗忐忑的心来写这篇文章,由于今天写的是闭包,关于闭包的理解可能你们都很是理解了,也怕写的很差,误导了部分的初学者,但愿下面内容有任何写的很差的地方,欢迎你们积极的指出来,真正的进步就是你们共同进步。也可以让本身成长起来,这也是我写坚持写博客的缘由之一。chrome

好了,废话闲说,咱们进入咱们的正题:闭包浏览器

function Car () {
  let wheel = 4;
  function drive () {
    console.log(wheel + ' wheel run');
  }
  return drive;
}

let drive = new Car();
drive(); // 4 wheel run
复制代码

你们确定都写过相似的代码,这段代码使用了闭包;那么到底闭包是什么?如何分析出闭包为何产生和闭包到底在哪里?缓存

什么是闭包?

闭包是由函数以及建立该函数的词法环境组合而成,闭包具有的几点要素bash

  1. 闭包必定是个函数对象
  2. 闭包和词法做用域、做用域链、垃圾回收机制息息相关
  3. 当函数必定是在其定义的做用域外进行访问时,才产生闭包
  4. 闭包是由该函数和其上层执行的上下文共同构成

为了让你们清晰的看到,咱们借助chrome的调试工具看下闭包产生的过程。 闭包


 当js执行到 let drive = new Car();代码,在执行car()时,此时的Call Stack只有全局上下文。函数


 接下来执行car(); 咱们能够看到,此时的drive进入了Call Stack,而且Closure(Car) 造成了。工具

经过调试,咱们看到当函数car执行时,闭包才产生,而封闭的空间Car时闭包。性能

闭包是js中的一个重要的存在,它能让不少不可能实现成为可能,可是闭包虽好,可是也不能乱用,有的时候也会拔苗助长。ui

闭包致使的内存泄漏

JS分配给浏览器的可用内存数量一般比分配给桌面应用程序的少,主要是防止js的网页所有讲系统内存耗尽。咱们要想让页面具有更好的性能就必须保证页面占用最少的内存,也就是说,咱们应该保证执行的代码只保存有用的数据,一旦数据再也不有用,咱们就该进行垃圾回收,释放内存。闭包是阻止垃圾回收的,所以变量永远存在内存中。当变量再也不被使用时,会形成内存泄漏,会影响页面的性能。spa

所以当变量对象再也不使用的时候,咱们应该将其释放掉。

function Car () {
  let wheel = 4;
  function drive () {
    console.log(wheel + ' wheel run');
  }
  return drive;
}

let drive = new Car();
drive(); // 4 wheel run
drive = null; // 在drive再也不使用,将其指向对象释放
复制代码

闭包的应用方法

  1. 模块
var module = (function (window, undefined) {
  let name = '王小端Coder';
  function getName () {
    return name;
  }
  return {
    name,
    getName
  }
})(window);

console.log(module.name); // 王小端Coder
console.log(module.getName()); // 王小端Coder
复制代码

一个模块具备私有属性、私有方法和公有属性、公有方法,而闭包能很好的将模块的公有属性、方法暴露出来。return关键字将对象引用赋值给module,从而使用的闭包。

  • 延时器、计时器
for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000 * i);
}
复制代码

在上面这个例子中咱们实际上想依次输出 0,1,2,3,4;而不是如今的输出5个5。下面咱们进行改造。

for (var i = 0;i < 5; i++) {
  (j => {
    setTimeout(() => {
      console.log(j);
    }, 1000 * j);
  })(i)
}
复制代码

在setTimeout中应用了闭包,使其内部可以记住每次循环所在的词法做用域和做用域链。因为setTimeout中的回调函数会在当前任务队列的尾部进行执行,所以上面第一个例子中每次循环中的setTimeout回调函数记住的i的值是for循环做用域中的值,此时都是5,而第二个例子记住的i的数为setTimeout的父级做用域自执行函数中的j的值,依次为0,1,2,3,4。

  • 数据缓存
var fn = function () {
  var sum = 0;
  for (var i = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }
  return sum;
}
复制代码

这个函数是返回传递参数之和,若是每次传递参数一致。结果返回都是一致的,就会形成浪费。若是改造这个函数,提升函数的性能。

var fn = (function () {
  var cache = {};
  return function () {
    var str = JSON.stringify(arguments);
    if (cache[str]) {
      return cache[str];
    } else {
      var sum = 0;
      for (var i = 0; i < arguments.length; i++) {
        sum += arguments[i];
      }
      return cache[str] = sum;
    }
  }
})();
复制代码

上面的示例将计算后的结果缓存到局部变量cache当中,在调用这个函数时,先在缓存中查找,若是找不到,则进行计算,而后将结果放到缓存中并返回,若是找到了,直接返回查找到的值。

若有对闭包的理解不正确的地方欢迎你们指正。谢谢!

相关文章
相关标签/搜索