Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入学习 JavaScript——闭包

深入学习 JavaScript——闭包

作者头像
李振
发布于 2021-11-26 06:53:12
发布于 2021-11-26 06:53:12
26800
代码可运行
举报
文章被收录于专栏:乱码李乱码李
运行总次数:0
代码可运行

什么是闭包(Closure)

“函数挂载父环境的时机,如果是定义时就是闭包,如果是执行时就不是闭包。”——听一位大神同事讲的。

“闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。”——MDN

刚学JavaScript的时候看了这些定义后我就哭了,要想理解闭包还是要看例子。

举个栗子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function foo() {
    let a = 1;
    function inner () {
        console.log(a++);
    };
    return inner;
}

let fun = foo();
fun(); //1
fun(); //2
fun = null; //a被垃圾回收

函数 foo 返回一个内部函数 inner,所以“let fun = foo()”的结果应该是“fun = inner” 也就是 “fun = function (){console.log(a++)};”

那么当执行 fun() 的时候 a=?,显然在 fun 的外部环境中是没有 a 的定义的,于是就向 inner 函数定义时候的父环境中找 a,果然在 foo 函数中找到了。这样就可以理解上面给出的第一个闭包的定义了:一个函数在执行的时候,如果能拿到定义时候父环境的值,这样就是闭包,反之则不是闭包。

那闭包究竟是一个什么东西呢?我们可以把闭包理解成 “函数 + 函数创建时的环境”的组合,比如上面的 inner 函数 + 变量a 就是一个闭包。

闭包的用途

通过使用闭包,我们可以做很多事情。

  1. JavaScript面向对象
  2. 提升代码效率
  3. 编写更优雅的代码

匿名自执行函数(立即执行函数表达式)

匿名自执行函数有两个作用:

  1. 不污染全局变量
  2. 函数执行完立刻释放垃圾回收

比如我上面栗子中创建的函数 foo 会自动绑定到全局变量中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
window.foo()(); //1

这样我们每次创建一个函数都必须要使用 const/let/var 去声明一个变量等于函数,不然全局对象的属性会越来越多,从而影响访问速度(因为变量的取值是需要从原型链上遍历的),而且可能会导致变量冲突。

结果缓存

结果缓存是闭包能显著提高程序效率的一个用途。假如有一个处理过程很耗时的函数对象,我们可以将每次处理的结果缓存起来,当再次调用这个函数的时候,就先从缓存中查找。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const cacheSearch = (function() {
    var cache = {};

    function search(key) {
        if (key in cache) {
            return cache[key];
        } else {
            cache[key] = `Hello ${key}`; //假如这是一步比较复杂的计算
            return cache[key];
        }
    }
    return search;
})();

封装

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const foo = (function() {
    let name = 'name'; // “闭包”内的函数可以访问 name 变量,而 name 变量对于外部却是隐藏的
    return {
        getName: function() { // 通过定义的接口来访问 name
            return name;
        },
        setName: function(new_name) { // 通过定义的接口来修改 name
            name = new_name;
        }
    };
}());

foo.getName(); // 得到 'name'
foo.setName('newName'); // 通过函数接口,我们访问并修改了 name 变量
foo.getName(); // 得到 'newName'
foo.name; // Type error,访问不能

实现类和继承

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Person() {
  let name = 'God';

  return {
    getName: function() {
      return name;
    },
    setName: function(newName) {
      name = newName;
    }
  }
};

let Student = function() {};
//继承自Person
Student.prototype = new Person();
//添加私有方法
Student.prototype.Say = function(name) {
  console.log(`Hello ${name}`);
};
let leo = new Student();
leo.setName('Leo');
leo.Say('World');
console.log(leo.getName());

这里的 Person 是一个函数,由于 JavaScript “没有” class 的概念(有 class 关键字) ,所以在 JavaScript 中,new 后面跟的是构造函数。 上面的代码里面定义了 Student 继承自 Person,所以拥有 getName 方法,然后通过prototype添加自己的方法。

经典题目

实现每隔一秒输出一个递增的数字(0 到 5)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (var i = 0; i < 5; i++) {
    setTimeout(() => {
        console.log(i)
    }, i * 1000)
}

上面这种写法想必大家都知道结果是什么,那就是每隔一秒输出一个5

使用闭包实现输出数字为 0 到 5

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (var i = 0; i < 5; i++) {
    ((a) => {
        setTimeout(() => {
            console.log(a)
        }, a * 1000)
    })(i)
}

还有一种使用闭包的方式是使用 Array 的 forEach 循环,forEach 里的执行函数也行成了一个闭包

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[0, 1, 2, 3, 4].forEach((i) => {
    setTimeout(() => {
        console.log(i)
    }, i * 1000)
})

当然使用 ES6 的 let 才是最好的选择

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (var i = 0; i < 5; i++) {
    setTimeout(() => {
        console.log(i)
    }, i * 1000)
}

闭包总结

闭包三个特性:

  1. 函数嵌套函数
  2. 函数内部可以引用外部的参数和变量
  3. 参数和变量不会被垃圾回收机制回收

闭包的优点:

  1. 希望一个变量长期驻扎在内存中
  2. 避免全局变量的污染
  3. 私有成员的存在

闭包的缺点:

  1. 闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
深入理解JavaScript闭包之闭包的使用场景
本篇文章是上一篇 深入理解JavaScript闭包之什么是闭包文章的下篇,闭包的使用场景。
木子星兮
2020/08/04
1.3K0
javascript中的闭包这一篇就够了
两段代码,在第二段代码中,函数A内的匿名函数可以访问到函数A中的局部变量这就是闭包的基本使用。
coder_koala
2019/07/30
6590
javascript 基础_JavaScript高级编程
1.分类: -基本类型 -String:任意字符串 -Number:任意的数字 -boolean: true/false -undefined:未定义 -null:空
全栈程序员站长
2022/09/24
1.6K0
javascript 基础_JavaScript高级编程
细说JavaScript闭包
JavaScript 闭包难点剖析一、作用域基本介绍ES6之前只有全局作用域与函数作用域两种,ES6出现之后,新增了块级作用域1.全局作用域在JavaScript中,全局变量是挂载在window对象下的变量,所以在网页中的任何位置你都可以使用并且访问到这个全局变量当我们定义很多全局变量的时候,会容易引起变量命名的冲突,所以在定义变量的时候应该注意作用域的问题var globalName = 'global'function getName() { console.log(globalName) // gl
hellocoder2029
2022/10/21
2280
【JavaScript】 进阶教程 施工中~
每个函数都有一个prototype属性。它默认指向一个Object空对象(即成为:原型对象)
杨丝儿
2022/02/24
1.4K0
【JavaScript】 进阶教程 施工中~
javaScript 递归 闭包 私有变量
递归   递归的概念     在程序中函数直接或者间接调用自己。      跳出结构,有了跳出才有结果。    递归的思想      递归的调用,最终还是要转换为自己这个函数。    应用    function sum(n){   if(n ==1) return 1;   return sum(n-1) + n   }   sum(100)   var fib = function (n){   if(n <=2){   return 1;   }
用户1197315
2019/12/11
5750
【JS】重温基础:闭包
这里先要了解一个概念,词法作用域:它是静态的作用域,是书写变量和块作用域的作用域**。
用户1462769
2019/09/19
1.9K0
全面理解Javascript闭包和闭包的几种写法及用途
好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了。好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一些实用的东西,主要将闭包的写法、用法和用途。
挨踢小子部落阁
2019/08/06
5980
闭包面试回答_ajax面试题
👸🏾:写在前面:在学习了闭包之后,试着做做这些题。其实是一种很棒地检验自己学习成果的手段。我当时反反复复,学了但好像又没完全学,遇到题还是一头雾水,到现在可以捋得很清楚也经历了蛮久的。而且从this,执行上下文,作用域一直走过来,这些题目涉及的内容也相对全面,加油喽。👍🏾 ​​​​​​​👉【JS基础整理—No.06】闭包_Chailo的博客-CSDN博客 //🍕 闭包面试题1 function show(){ f = function fn(){ console.log(x); } v
全栈程序员站长
2022/09/27
1970
详解JavaScript中闭包(Closure)概念
闭包(Closure)是 JavaScript 中一个非常重要的概念,它允许函数访问其词法作用域(lexical scope)中的变量,即使这个函数在其词法作用域之外执行。理解闭包有助于编写更高效、模块化的代码,并且在处理异步操作、回调函数和数据封装时非常有用。
jack.yang
2025/04/05
810
JS_基础知识点精讲
今天,我们继续「前端面试」的知识点。我们来谈谈关于「JavaScript」的相关知识点和具体的算法。
前端柒八九
2022/12/19
1.2K0
JS_基础知识点精讲
中高级前端高频面试题分享
代码比较简单,我们只是在setTimeout的方法里面又调用了一次setTimeout,就可以达到间歇调用的目的。
前端迷
2019/05/28
8660
深入理解JavaScript闭包之什么是闭包
在看本篇文章之前,可以先看一下之前的文章 深入理解JavaScript 执行上下文 和 深入理解JavaScript作用域,理解执行上下文和作用域对理解闭包有很大的帮助。
木子星兮
2020/07/27
8730
兄台:JS闭包了解一下
同时, 在 JS 中,对象的值可以是「任意类型」的数据。(在JS篇之数据类型那些事儿简单的介绍了下基本数据类型分类和判断数据类型的几种方式和原理,想了解具体细节,可移步指定文档)
前端柒八九
2022/08/25
7710
兄台:JS闭包了解一下
滴滴前端面试题(边面边更)_2023-02-24
柯里化(currying) 指的是将一个多参数的函数拆分成一系列函数,每个拆分后的函数都只接受一个参数。
java_js0166293
2023/02/24
1.2K0
什么是闭包?闭包的用途是什么?
所谓块级作用域就是指在循环中定义的变量,一旦循环结束,变量也随之销毁,它的作用范围只在这一小块。而在JavaScript中没有这样的块级作用域,由于JavaScript不会告诉你变量是否已经被声明,所以容易造成命名冲突,如果在全局环境定义的变量,就会污染全局环境,因此可以利用闭包的特性来模仿块级作用域。
青梅煮码
2023/01/16
1.9K0
2020回顾-个人web分享JavaScript面试题附加回答
对于在JavaScript中的字符串,对象,数组是没有固定大小的,只有当对他们进行动态分配存储时,解释器就会分配内存来存储这些数据,当JavaScript的解释器消耗完系统中所有可用的内存时,就会造成系统崩溃。
达达前端
2021/01/05
1.7K0
JavaScript学习总结(三)——闭包、IIFE、原型、函数与对象
一、闭包(Closure) 1.1、闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9;方法:找到所有的div,
张果
2018/01/04
1.6K0
JavaScript学习总结(三)——闭包、IIFE、原型、函数与对象
JavaScript 设计模式学习第三篇- 闭包与高阶函数
JavaScript 的函数也是对象,可以有属性,可以赋值给一个变量,可以放在数组里作为元素,可以作为其他对象的属性,什么都可以做,别的对象能做的它能做,别的对象不能做的它也能做,这不就是一等公民的地位嘛。 — 程墨 Morgan
越陌度阡
2020/11/26
3580
JavaScript闭包
init() 创建了一个局部变量 name 和一个名为 displayName() 的函数。displayName() 是定义在 init() 里的内部函数,并且仅在 init() 函数体内可用。displayName() 没有自己的局部变量。然而,因为它可以访问到外部函数的变量,所以 displayName() 可以使用父函数 init() 中声明的变量 name 。
闲花手札
2021/11/15
6320
相关推荐
深入理解JavaScript闭包之闭包的使用场景
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验