当前位置: 首页 > 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)
  • 【mysql】环境安装、服务启动、密码设置
  • 【刷算法】求1+2+3+...+n
  • 11111111
  • Computed property XXX was assigned to but it has no setter
  • JAVA SE 6 GC调优笔记
  • JavaScript函数式编程(一)
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Python_OOP
  • Vue实战(四)登录/注册页的实现
  • vue学习系列(二)vue-cli
  • webgl (原生)基础入门指南【一】
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 如何编写一个可升级的智能合约
  • 如何实现 font-size 的响应式
  • 网页视频流m3u8/ts视频下载
  • 译自由幺半群
  • zabbix3.2监控linux磁盘IO
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (6)STL算法之转换
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (六)激光线扫描-三维重建
  • (七)理解angular中的module和injector,即依赖注入
  • (转)winform之ListView
  • ./configure,make,make install的作用
  • .net MVC中使用angularJs刷新页面数据列表
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET 将多个程序集合并成单一程序集的 4+3 种方法
  • /etc/fstab和/etc/mtab的区别
  • @软考考生,这份软考高分攻略你须知道
  • [acwing周赛复盘] 第 69 场周赛20220917
  • [ANT] 项目中应用ANT
  • [Django 0-1] Core.Checks 模块
  • [Flutter]设置应用包名、名称、版本号、最低支持版本、Icon、启动页以及环境判断、平台判断和打包
  • [FTP]pureftp部署和优化
  • [hdu 1247]Hat’s Words [Trie 图]
  • [hive] sql中distinct的用法和注意事项
  • [iOS]Win8下iTunes无法连接iPhone版本的解决方法