前端基础知识:闭包就是指有权访问另一个函数作用域中的变量的函数

2021-02-0209:48:31WEB前端开发Comments1,395 views字数 2431阅读模式

闭包就是指有权访问另一个函数作用域中的变量的函数。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

官方解释:闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。(词法作用域)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

通俗解释:闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

当某个函数被掉用的时候,会创建一个执行环境及相应的作用域链。然后使用arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位...直至作为作用域链终点的全局执行环境。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

作用域链本质上是一个指向变量对象的指针列表,他只引用但不实际包含变量对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相同名字的变量,一般来讲,当函数执行完毕,局部活动对象就会被销毁,内存中仅保存全部作用域的活动对象。但是,闭包不同。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

创建闭包: 在一个函数内部创建另一个函数文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

function add() {
  let a = 1;
  let b = 3;
  function closure() {
     b++;
     return a + b;
  }
  return closure;
}
// 闭包的作用域链包含着它自己的作用域,以及包含它的函数的作用域和全局作用域。

生命周期文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

当闭包中的函数closureadd中返回后,它的作用域链被初始化为包含add函数的活动对象和全局变量对象。这样closure就可以访问在add中定义的所有变量。更重要的是,add函数在执行完毕后,也不会销毁,因为closure函数的作用域链仍然在引用这个活动对象。换句话说,当add返回后,其执行环境的作用域链被销毁,但它的活动对象仍然在内存中,直至closure被销毁。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

function add(x) {
  function closure(y) {
     return x + y;
  }
  return closure;
}

let add2 = add(2);
let add5 = add(5);
// add2 和 add5 共享相同的函数定义,但是保存了不同的环境
// 在add2的环境中,x为5。而在add5中,x则为10
console.log(add2(3)); // 5
console.log(add5(10)); // 15

// 释放闭包的引用
add2 = null;
add5 = null;

闭包中的this对象文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

var name = 'window';
var obj = {
  name: 'object',
  getName: () => {
    return () => {
      return this.name;
    }
  }
}
console.log(obj.getName()()); // window

obj.getName()()是在全局作用域中调用了匿名函数,this指向了window。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

函数名与函数功能是分割开的,不要认为函数在哪里,其内部的this就指向哪里。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

window才是匿名函数功能执行的环境。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

使用注意点文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

1)由于闭包会让包含函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

使用文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

  1. 模仿块级作用域
  2. 私有变量
  3. 模块模式

在循环中创建闭包:一个常见错误文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

function show(i) {
  console.log(i);
}

function showCallback(i) {
  return () => {
    show(i);
  };
}

// 测试1【3,3,3】
const testFunc1 = () => {
  // var i;
  for (var i = 0; i < 3; i++) {
    setTimeout(() => show(i), 300);
  }
}

// 测试2 【0,1,2】
const testFunc2 = () => {
  for (var i = 0; i < 3; i++) {
    setTimeout(showCallback(i), 300);
  }
}

// 测试3【0,1, 2】 闭包,立即执行函数
// 在闭包函数内部形成了局部作用域,每循环一次,形成一个自己的局部作用域
const testFunc3 = () => {
  for (var i = 0; i < 3; i++) {
    (() => {
       setTimeout(() => show(i), 300);
    })(i);
  }
}

// 测试4【0,1, 2】let
const testFunc4 = () => {
  for (let i = 0; i < 3; i++) {
    setTimeout(() => show(i), 300);
  }
}

setTimeout()函数回调属于异步任务,会出现在宏任务队列中,被压到了任务队列的最后,在这段代码应该是for循环这个同步任务执行完成后才会轮到它文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

测试1错误原因:赋值给 setTimeout 的是闭包。这些闭包是由他们的函数定义和在 testFunc1 作用域中捕获的环境所组成的。这三个闭包在循环中被创建,但他们共享了同一个词法作用域,在这个作用域中存在一个变量i。这是因为变量i使用var进行声明,由于变量提升,所以具有函数作用域。当onfocus的回调执行时,i的值被决定。由于循环在事件触发之前早已执行完毕,变量对象i(被三个闭包所共享)已经指向了i的最后一个值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

测试2正确原因: 所有的回调不再共享同一个环境, showCallback 函数为每一个回调创建一个新的词法环境。在这些环境中,i 指向数组中对应的下标。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

测试4正确原因:JS中的for循环体比较特殊,每次执行都是一个全新的独立的块作用域,用let声明的变量传入到 for循环体的作用域后,不会发生改变,不受外界的影响。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20915.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/20915.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定