📜  闭包 () - Javascript (1)

📅  最后修改于: 2023-12-03 15:28:49.355000             🧑  作者: Mango

闭包 - Javascript

在Javascript中,闭包是一种非常重要的概念。其实闭包并不难理解,而且学习好闭包可以让你更好地理解 Javascript 的本质。

什么是闭包

闭包就是一个可以访问外部函数作用域中变量的函数。简言之,就是一个函数能够把外部作用域中的变量捆绑到该函数内部,使得这些变量在该函数的生命周期内一直存在。

说起来有点抽象,我们来看一个例子:

function outer() {
  let num = 0;
  function inner() {
    num++;
    console.log(num);
  }
  return inner;
}

let fn1 = outer();
let fn2 = outer();

fn1(); // 1
fn1(); // 2
fn2(); // 1

在这个例子中,outer 函数返回了一个函数 innerinner 函数中调用了外部作用域中的变量 num 。由于 inner 作为闭包,它会一直保存对其父作用域的引用,所以每次调用 fn1() 时,都能够正确地获取到 outer 中定义的 num 变量,并维持 num 的值的连续性。

闭包的用途

如果我们没有学习闭包时,可能会使用全局变量或者函数传递参数来实现某些需求,但是这些做法固然没有问题,但却会让代码不够优雅,而且无法避免变量名污染的问题。

闭包则是解决这些问题的利器。我们可以使用闭包来隐藏实现细节,同时又能够保证变量的私有性。比如在一个定时器回调函数中访问jQuery的对象,使用闭包可以避免全局变量的定义:

function start() {
  let num = 0;
  setInterval(function() {
    num++;
    console.log(num);
    $("#myDiv").html(num);
  }, 1000);
}

start();

在这个例子中,我们使用了闭包来隐藏了变量 num,同时又在定时器回调函数中访问到它。

闭包的坑

闭包对性能的影响是不容忽视的,因为函数的变量会被保存在内存中。在一些情况下,如果没有正确地使用闭包,可能会导致内存泄漏的问题。比如下面这个例子:

function createArray() {
  let arr = [];
  for (let i = 0; i < 100000; i++) {
    arr.push(function() {
      console.log(i);
    });
  }
  return arr;
}

let arr = createArray();

arr[0](); // 100000

在这个例子中,我们使用了循环来创建包含函数的数组。但是由于闭包的引用机制,实际上每个函数都会引用最后一个 i 的值,因此当我们调用 arr[0]() 时,输出的结果是 100000 而非我们期望的 0。这种情况下,我们可以使用立即执行函数来避免这个问题:

function createArray() {
  let arr = [];
  for (let i = 0; i < 100000; i++) {
    (function(k){
      arr.push(function() {
        console.log(k);
      });
    })(i);
  }
  return arr;
}

let arr = createArray();

arr[0](); // 0

这种情况下,我们将循环内的函数改为了立即执行函数,并将 i 作为参数传入,从而避免了闭包引用的问题。

总结

闭包作为 Javascript 的一个重要概念,无论是从实践还是理论上,都应该好好掌握。在实际的开发中,我们可以用闭包来保证变量的私有性、隐藏实现细节,同时又规避了全局变量带来的模块污染等问题。但是在使用闭包时需要注意,一定要正确地引用外部变量,避免可能存在的内存泄漏等问题。