昨天的文章中主要记录了,函数表达式与函数声明的区别

以及在JS中如何安全地使用递归

那么既然要深入地理解JS中的函数,闭包就是一个绕不开的概念

闭包

JS高编一书中对闭包的概念定义如下:

闭包是指有权访问另一个函数作用域中变量的函数

我们来理解这句话,闭包指的是一类函数

这类函数的特点是可以访问另一个函数的作用域

我们知道JS中Es6以下是没有块级作用域的

只有全局作用域,以及函数作用域

一般来讲,函数作用域里面的变量在函数外部是无法访问的

而闭包却可以访问另一个函数作用域,那么说明了什么?

说明闭包说白了就是在函数内部定义或声明的函数

以下面的代码举例

function createComparisonFunction(propertyName){// 用于创建比较函数的函数
return function(object1,object2){// 根据propertyName来比较对象的对应属性的值
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1>value2){
return 1;
}else if(value1<value2){
return -1;
}else{
return 0;
}
}
}

这就是闭包的使用场景之一

我们在内部的函数中访问了外部函数的变量

而当内部匿名函数作为值返回后我们在函数的外部也能访问到函数createComparisonFunction内部的值

这样的结果似乎跟我们之前对JS的认知产生了冲突

我们知道当一个函数执行完毕后,其执行上下文便会被销毁

为其分配的内存也会被垃圾收集器回收

那么为什么闭包依旧可以访问呢?

之前我们讲过JS中的垃圾回收机制

当一个对象不再被引用时才会被垃圾收集器释放内存

而JS中的执行上下文,在ES5被称为活动对象,ES6中似乎被称为变量环境

不管名字是什么,其实就是指的一个保存变量声明等相关信息的对象

虽然 createComparisonFunction 已经执行完毕,但是由于其内部的匿名函数仍旧保存着对这个对象的引用,所以该对象无法被回收

这也是闭包占用内存多的原因

那么闭包的引用的这个对象什么时候会被回收呢?

var compareName = createComparisonFunction('name');
// compareName 保存了对返回的匿名函数的引用 // 一些操作 compareName = null;// 解除对返回的比较函数的引用

也就是当这个闭包不再被引用,闭包的执行上下文,与其外部函数的执行上下文都将一起被回收

闭包与变量

闭包虽然可以访问外部函数的值

但是其作用不是万能的,因为闭包引用的是外部函数的执行上下文

所以闭包只能获得闭包执行时的外部函数执行上下文中变量的最后一个值

function createFunctions(){
var result = new Array();
for (var i=0;i<10;i++){
result.push(function(){return i;})
}
return result;
}

在浏览器中运行结果如下

按照我们上面的结果来看,肯定是不符合我们的预期的

我们或许希望,array 数组中每个对象都返回对应执行时的值

那么我们可以通过JS中的参数传递都是值传递来完成这一点

将之前的函数改写为

function createFunctions(){
var result = new Array();
for (var i=0;i<10;i++){
result.push((function(num){
return function(){return num;}
})(i));
}
return result;
}

使用自执行函数来将每次循环的i值保存到不同的执行上下文中

我们来看看结果

这种方法相当于就是创建了十个执行上下文,每个返回的闭包都引用不同上下文,来实现的

所以十分耗费内存,在实践中不推荐使用

关于this对象

要注意的是,虽然闭包可以访问外部函数的执行上下文

但是并不意味着闭包可以直接访问外部函数的 this 和 arguments对象

因为每个函数在创建时都会自动地取得这两个变量,而不会去获取外部的this

所以如果希望在闭包中访问外部函数的this变量,那么需要在外部函数中创建一个变量来保存 this

内存泄漏

我们知道js的内存是由JS自己回收的

所以我们在获得便利的同时,也增加了内存泄漏的风险

因为这是我们不能控制的

我们只能尽量避免这种情况的发生

而跟闭包有关的主要是在DOM事件中,这里就先不展开讲了,感兴趣的小伙伴可以留言,给我说不定可以开个番外篇

Javascript高级编程学习笔记(24)—— 函数表达式(2)闭包的更多相关文章

  1. Javascript高级编程学习笔记(25)—— 函数表达式(3)模仿块级作用域

    昨天写了闭包 今天就来聊聊块级作用域的事情 在绝大多数编程语言中,都有块级作用域这个概念 什么是块级作用域呢? 前面我们在刚开始讲的时候说过,JS中的大括号(不在赋值运算符的后面)表示代码块 块级作用 ...

  2. Javascript高级编程学习笔记(23)—— 函数表达式(1)递归

    前面的文章中,我在介绍JS中引用类型的时候提过,JS中函数有两种定义方式 第一种是声明函数,即使用function关键字来声明 第二种就是使用函数表达式,将函数以表达式的形式赋值给一个变量,这个变量就 ...

  3. Javascript高级编程学习笔记(26)—— 函数表达式(4)私有变量

    私有变量 严格来讲,JS中没有私有成员的概念,所有对象属性都是公有的. 但是JS中有私有变量的概念 所有在函数中定义的变量都可以认为是私有变量,因为不能在函数外部进行访问 私有变量包括 1.函数参数 ...

  4. 《JavaScript高级程序设计》笔记:函数表达式(七)

    递归 function factorial(num){ if(num<=1){ return 1; }else { return num * arguments.callee(num-1); } ...

  5. Javascript高级编程学习笔记(16)—— 引用类型(5) Function类型

    JS中许多有趣的地方都和函数脱不了联系 那么是什么让JS中的函数这么有趣呢? 我们一起来看看吧 Function类型 在JS中函数实际上就是对象,每个函数都是Function类型的实例,和JS的其他引 ...

  6. Javascript高级编程学习笔记(7)—— 函数

    前几天有事耽搁了,今天继续更新 今天的主要内容是JS中的函数 这一篇主要讲函数的定义等内容,至于变量提升.执行环境.闭包.内存回收等内容在后面讲,高玩们可以不用看下面的正文了. 函数 首先来讲,函数对 ...

  7. JavaScript高级编程学习笔记(第三章之一)

    继续记笔记,JavaScript越来越有意思了. 继续... 第三章:JavaScript基础 ECMAScript语法在很大程度上借鉴了C和其它类似于C的语言,比如Java和Perl. 大小写敏感: ...

  8. Javascript高级编程学习笔记(6)—— 流程控制语句

    话不多说,我们直接开始进入今天的主题 流程控制语句 首先什么是流程控制语句呢? 顾名思义,就是控制流程的语句. 在JS中语句定义了ECMAScript中的主要语法,让我们可以使用一系列的关键字来完成指 ...

  9. Javascript高级编程学习笔记(3)—— JS中的数据类型(1)

    前一段时间由于事情比较多,所以笔记耽搁了一段时间,从这一篇开始我会尽快写完这个系列. 文章中有什么不足之处,还望各位大佬指出. JS中的数据类型 上一篇中我写了有关JS引入的Script标签相关的东西 ...

随机推荐

  1. MSSQL死锁进程查看及关闭

    select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName from sys.dm_tran ...

  2. java 实现udp通讯

    需求:应用A(通常有多个)和应用B(1个)进行 socket通讯,应用A必须知道应用B的ip地址(在应用A的配置文件中写死的),这个时候就必须把应用B的ip设成固定ip(但是某些时候如更换路由后要重新 ...

  3. Python基础与进阶

    1 Python基础与进阶 欢迎来到Python世界 搭建编程环境 变量 | 字符串 | 注释 | 错误消除 他只用一张图,就把Python中的列表拿下了! 使用 If 语句进行条件测试 使用字典更准 ...

  4. get windows auth code

    public static WindowsIdentityInfo GetWindowsIdentityInfo(HttpContext context) { WindowsIdentityInfo ...

  5. C++实现文件内字符数、单词数、行数的统计

    先给出github上的代码链接以及项目需求 1. 项目简介 这个项目的需求可以概括为:对程序设计语言源文件统计字符数.单词数.行数,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处 ...

  6. 【Python】【BugList13】req = requests.get(url=target)报错: (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)')

    [代码] # -*- coding:UTF-8 -*- import requests if __name__ == '__main__': target = 'https://unsplash.co ...

  7. easy-ui treegrid 实现分页 并且添加自定义checkbox

    首先第一点easy-ui  treegrid 对分页没有好的实现, 因为在分页的过程中是按照 根节点来分页的  后台只能先按照 根节点做分页查询  再将子节点关联进去, 这样才能将treegrid 按 ...

  8. ili 一例业务系统框架

    ili即ilinei的简称,像名字一样,是ILINEI团队的内部项目简化而来.2017年金鸡报晓,我们为同行送来了一个简单.快速.轻量级的PHP开源系统,它的任务当然也是唯一的任务,就是提高WEB开发 ...

  9. P2634 [国家集训队]聪聪可可

    淀粉质 第二道点分治的题 关于点分治的一点理解: 所谓点分治,其实就是把要求的问题(一般与路径有关)划分成两种情况 1.路径经过rt(根节点) 2.路径在根节点的子树内 我们只需要处理情况1,因为情况 ...

  10. uni-app 顶部导航点击更换图标

    更换顶部导航的iconfont.ttf图标,先在配置文件配置好按钮: pages.json文件 "buttons": [ { "text": "\ue ...