一次就看懂什么是JS闭包

定义

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

解释

在JS中闭包是指函数内部可以访问函数外部的一种行为,也包括代码块内部访问外部变量的情况。

原理

会出现闭包这种情况的原因是因为JS在函数或者代码块运行的时候,会有一个隐藏的词法环境变量。该变量主要包含2部分:

  1. 环境记录(Environment Record)—— 一个把所有局部变量作为其属性(包括一些额外信息,比如 this 值)的对象。
  2. **外部词法环境(outer lexical environment)**的引用 —— 通常是嵌套当前代码(当前花括号之外)之外代码的词法环境。
    这个对象会记录我们运行时的环境情况,比如:
let phrase = 'Hello';
function sayName (name) {
  console.log(`${phrase}${name}`);
}
sayName('John');

我们在调用sayName函数时,会创建出sayName函数的词法变量对象,首先会保存自己函数内部的局部变量信息,但是sayName中没有局部变量,其次会保存指向外部词法环境变量的引用,这样当我们在运行时,首先在内部词法环境变量中寻找phrase变量,如果找不到就顺着外部词法环境变量的引用去寻找,知道找到为止。这样从函数内部访问外部变量的行为,就称为闭包。

Tips:

  1. 一次调用 —— 一个词法环境
    请记住,每次函数运行会都会创建一个新的函数词法环境。如果一个函数被调用多次,那么每次调用也都会此创建一个拥有指定局部变量和参数的词法环境。
  2. 词法环境是一个规范对象
    『词法环境』是一个规范对象。我们不能在代码中获取或直接操作该对象。但 JavaScript 引擎同样可以优化它,比如清除未被使用的变量以节省内存和执行其他内部技巧等,但显性行为应该是和上述的无差。
  3. new Function例外
    当使用new Function创建函数时,外部词法环境变量引用指向的是全局环境,并不指向函数创建位置的上一层词法环境变量。

总结

函数保存其外部的变量并且能够访问它们称之为闭包。在某些语言中,是没有闭包的,或是以一种特别方式来实现。但正如上面所说的,在 JavaScript 中函数都是天生的闭包(只有一个例外,请参考 “new Function” 语法)。
也就是说,他们会通过隐藏的 [[Environment]] 属性记住创建它们的位置,所以它们都可以访问外部变量。
在面试时,前端通常都会被问到『什么是闭包』,正确的答案应该是闭包的定义并且解释 JavaScript 中所有函数都是闭包,以及可能的关于 [[Environment]] 属性和词法环境原理的技术细节。

应用

  1. 使用闭包封装私有变量
function User () {
  let name = 'John';
  return {
    setName: (userName) => {
      name = userName;
    },
    getName: () => {
      return name;
    }
  };
}
const user = new User();
console.log(user.getName());
user.setName('Bob');
console.log(user.getName());
  1. 使用闭包缓存
const cacheCount = (() => {
  const cache = {};
  return {
    getCache: (key) => {
      if (key in cache) {
        return cache[key];
      }
      const newValue = `${key}-value`;
      cache[key] = newValue;
      return newValue;
    }
  };
})();
console.log(cacheCount.getCache('key'));

详细闭包学习,参考以下链接: https://zh.javascript.info/closure

推荐JS学习地址: https://zh.javascript.info/

你可能感兴趣的:(JavaScript,原理学习)