当前位置: 首页 > news >正文

JS学习笔记——闭包

1. 什么是闭包

MDN定义:Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, these functions 'remember' the environment in which they were created.

You Don't Know JS: Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.

我所理解的闭包就是,即使外部函数已经运行完毕,内部函数仍能访问外部函数的作用域中的变量。

抓重点: 函数, 作用域。

2. 闭包的运行机制

2.1 词法作用域查找规则

在闭包的使用中,为什么我们能够通过闭包访问外部函数的作用域中的变量?其一,词法作用域的查找规则是“冒泡”的,即向外层一层层查找,直到全局作用域,所以能够访问外部函数的作用域。其二,函数的作用域是定义时所在的作用域,而不是运行时的作用域。

function foo() {
  var a = 1;
  
  function bar() {
      console.log(a);
  }
 
 return bar;
}

var a = 2;
var baz = foo();
baz(); //1

在上面的代码中,由于bar定义在foo的内部,因此能够向外“冒泡”访问foo的作用域。当运行baz时,a的值为1而不是2,也说明了函数的作用域是定义时的作用域,是静态的。

2.2 垃圾回收 + 引用

当函数执行完毕后,引擎的垃圾回收机制会释放不再使用的内存空间。因此,当外部函数执行完毕时,外部函数的内部作用域理应是该被销毁的。然而,由于闭包存在对外部函数作用域的引用,因此此作用域仍然存在,所以内部函数仍能在外部函数执行结束之后访问外部函数定义的变量,此之为“记住”

3. 闭包的应用场景

3.1 私有变量 + 模块

需求:只能通过函数提供的方法访问函数内部的变量——隐藏。只能内部访问——私有。

function bookInfo() {
  var book = {
        name: "You Don't know JS",
      price: 66
    };
        
    function getPrice() {
        console.log(book.price);
    };
    
    function getName() {
        console.log(book.name);
    };
    
    function setPrice(price) {
        book.price = price;
    };
    
    return {
        getPrice,
      getName,
      setPrice
    };
  };

  var book = bookInfo();
  
  book.getPrice(); //66
  book.getName(); //"You Don't know JS"
  book.setPrice(100);
  book.getPrice(); //100

在以上的代码中,bookInfo通过返回一个对象,该对象的值是对内部函数的引用,而不是对变量的引用。因此,实现了函数内部变量是隐藏的(只能通过返回的对象方法访问)且私有的(只有函数内部才能访问)。

在模块中,返回的变量就被称为模块的公共API,模块内部的变量只能通过这些方法去使用。

3.2 偏函数应用

需求:函数需要先接受一些参数,随后再接受另一些参数的时候。
比如,当我计算商品的总价格时,我想先设定商品的单价,随后根据购买数量算出总的商品价格。

  function partialApply(fn, ...fixedArgs) {
    return function (...remainingArgs) {
        return fn.apply(this, fixedArgs.concat(remainingArgs));
    }
  }
  
  function calTotalPrices(price, count) {
      console.log(price * count);
  }
  
  var pay = partialApply(calPrice, 10);
  
  pay(5);

在上面的代码中,pay就是在partialApply的外部访问了partialApply的内部变量(函数参数)。

4. 为什么闭包很重要?

参考资料

  • 征服 JavaScript 面试:什么是闭包?| Eric Elliott

  • You Don't know JavaScript

相关文章:

  • SuperPasterV1.0 测试版发布
  • c语言(01)
  • 第7章 处理串行线路和帧中继连接故障
  • 转载:浅谈 Scala 中下划线的用途
  • buildroot mysql mysql.mk hacking
  • 来介绍一个很好的工具--TodoList
  • Vuejs——(9)组件——props数据传递
  • struts技术的logic标签
  • Centos7下安装mysql5.6需要注意的点
  • 算法之美--3.2.3 KMP算法
  • log4j详解
  • angularjs data-ng-app 和ng-app的区别
  • 微软发布WF教程及大量示例
  • zabbix3.0.4-agent通过shell脚本获取mysql数据库登陆用户
  • 一个n的flex组件(SpringGraph Flex Component)
  • Git初体验
  • java中的hashCode
  • Laravel 实践之路: 数据库迁移与数据填充
  • Otto开发初探——微服务依赖管理新利器
  • SQL 难点解决:记录的引用
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 飞驰在Mesos的涡轮引擎上
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 类orAPI - 收藏集 - 掘金
  • 阿里云服务器如何修改远程端口?
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ###C语言程序设计-----C语言学习(6)#
  • %@ page import=%的用法
  • (a /b)*c的值
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (蓝桥杯每日一题)love
  • (一) storm的集群安装与配置
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)shell调试方法
  • (转)树状数组
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • .NET 5种线程安全集合
  • .NET Micro Framework初体验
  • .NET MVC之AOP
  • .sh 的运行
  • /etc/fstab和/etc/mtab的区别
  • /使用匿名内部类来复写Handler当中的handlerMessage()方法
  • [Android学习笔记]ScrollView的使用
  • [BT]BUUCTF刷题第4天(3.22)
  • [bzoj 3534][Sdoi2014] 重建
  • [C++11 多线程同步] --- 条件变量的那些坑【条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)】
  • [hibernate]基本值类型映射之日期类型
  • [IE9] IE9 Beta崩溃问题解决方案
  • [LeetCode]Max Points on a Line
  • [linux] git lfs install 安装lfs
  • [LLM]大模型八股知识点(一)