JavaScript教程

作用域和闭包

Preview
  • JavaScript作用域和闭包
  • 作用域
  • 全局作用域
  • 函数作用域
  • 闭包
  • 闭包的应用
  • 闭包的注意点
  • 内存泄漏
  • 作用域链
  • 总结

JavaScript作用域和闭包

作用域

作用域是指变量、函数的可访问范围。在 JavaScript 中,作用域分为全局作用域和函数作用域。

全局作用域

全局作用域是指在函数外部定义的变量和函数,它们可以被整个程序访问。全局变量和函数可以被局部作用域中的代码访问。

var globalVariable = "I am a global variable";

function globalFunction() {
  console.log("I am a global function");
}

console.log(globalVariable); // 输出 "I am a global variable"
globalFunction(); // 输出 "I am a global function"

函数作用域

函数作用域是指在函数内部定义的变量和函数,它们只能在函数内部访问。函数作用域可以嵌套,内部函数可以访问外部函数的变量和函数。

function outerFunction() {
  var outerVariable = "I am an outer variable";
  
  function innerFunction() {
    var innerVariable = "I am an inner variable";
    console.log(outerVariable); // 输出 "I am an outer variable"
  }
  
  innerFunction();
}

outerFunction();

闭包

闭包是指在函数内部创建的一个函数,它可以访问函数外部的变量和函数。闭包可以让函数内部的变量和函数在函数执行完毕后仍然存在,不会被垃圾回收机制回收。

function outerFunction() {
  var outerVariable = "I am an outer variable";
  
  function innerFunction() {
    console.log(outerVariable); // 输出 "I am an outer variable"
  }
  
  return innerFunction;
}

var closureFunction = outerFunction();
closureFunction(); // 输出 "I am an outer variable"

在上面的例子中,outerFunction 返回了 innerFunctionclosureFunction 接收了 outerFunction 的返回值。当 closureFunction 被调用时,它仍然可以访问 outerVariable,因为它是一个闭包,可以访问外部函数的变量和函数。

闭包的应用

闭包常用于封装变量和函数,以便于控制变量和函数的访问权限。闭包还可以用于实现函数记忆和函数柯里化等高级技术。

function counter() {
  var count = 0;
  
  return function() {
    count++;
    console.log(count);
  }
}

var increment = counter();
increment(); // 输出 1
increment(); // 输出 2
increment(); // 输出 3

在上面的例子中,counter 返回了一个闭包,它可以访问 count 变量。每次调用闭包时,它都会增加 count 的值并输出。这样就可以实现一个简单的计数器。

闭包的注意点

虽然闭包可以带来很多好处,但是也需要注意一些问题。

内存泄漏

由于闭包会使得函数内部的变量和函数在函数执行完毕后仍然存在,如果闭包一直存在,这些变量和函数也会一直存在,可能会导致内存泄漏。

function outerFunction() {
  var array = new Array(10000).fill(0);
  
  return function() {
    console.log(array);
  }
}

var closureFunction = outerFunction();
closureFunction(); // 输出一个长度为 10000 的数组

// 如果不再需要 closureFunction 了,可以手动解除引用
closureFunction = null;

在上面的例子中,outerFunction 返回了一个闭包,它可以访问 array 变量。由于 array 的长度为 10000,如果闭包一直存在,这个数组也会一直存在,可能会导致内存泄漏。如果不再需要闭包了,可以手动解除引用,以便让垃圾回收机制回收内存。

作用域链

闭包的作用域链包括外部函数的作用域和全局作用域,如果闭包访问了一个不存在的变量或函数,它会一直向上查找作用域链,直到找到为止。由于作用域链的长度可能会很长,这可能会影响闭包的性能。

function outerFunction() {
  var array = new Array(10000).fill(0);
  
  return function() {
    console.log(array.length); // 输出 10000
    console.log(undefinedVariable); // 报错,undefinedVariable 未定义
  }
}

var closureFunction = outerFunction();
closureFunction(); // 报错

在上面的例子中,闭包访问了一个未定义的变量 undefinedVariable,由于它不存在,闭包会一直向上查找作用域链,直到找到全局作用域,仍然找不到,最终报错。

总结

JavaScript 的作用域分为全局作用域和函数作用域,闭包可以访问外部函数的变量和函数,常用于封装变量和函数,以便于控制变量和函数的访问权限。但是闭包也需要注意内存泄漏和作用域链的问题,需要适当使用和手动解除引用,以便让垃圾回收机制回收内存。