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

以及在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. 数据如何输入输出_Spark

    1)输入:在Spark程序运行中,数据从外部数据空间(如分布式存储:textFile读取HDFS等,parallelize方法输入Scala集合或数据)输入Spark,数据进入Spark运行时数据空间 ...

  2. “无法将“Enable-Migrations”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。”的一种解决方式

    通过以下几个步骤解决: 1.install-package entityFramework: 2.更新 nuget: 3.更新 visual studio: 我是通过第三个步骤解决的.

  3. mysql执行sql语句过程

    开发人员基本都知道,我们的数据存在数据库中(目前最多的是mysql和oracle,由于作者更擅长mysql,所以这里默认数据库为mysql),服务器通过sql语句将查询数据的请求传入到mysql数据库 ...

  4. Lonsdor K518ISE programs 2005 Ford Focus key in two minutes

    A quick demonstration of Lonsdor K518ISE programming key for 2005 Ford Focus in two minutes. And for ...

  5. Eclipse 新建 Maven web 项目

    File --> New --> Maven Project --> 选择存放路径 --> 选择骨架 --> 输入Maven坐标 --> 初步成型,下面还要配置 1 ...

  6. [规则原则定理]规则原则定理章4 HTTP&RPC

    rpc是远端过程调用,其调用协议通常包含传输协议和序列化协议. 传输协议包含: 如著名的 [gRPC](grpc / grpc.io) 使用的 http2 协议,也有如dubbo一类的自定义报文的tc ...

  7. sparse_matrix

    (1)ndarray 与 scipy.sparse.csr.csr_matrix 的互转 import numpy as npfrom scipy import sparse 1.1 ndarry 转 ...

  8. Trachtenberg(特拉亨伯格)速算系统

    二战期间,俄国的数学家Jakow Trachtenberg(1888-1953)被关进纳粹集中营,在狱中,他开发出了一套心算算法,这套算法后来被命名为Trachtenberg(特拉亨伯格)速算系统. ...

  9. 第二阶段第七次spring会议

    昨天我将尝试对软件进行添加搜索引擎的界面. private void linkLabel1_LinkClicked_1(object sender, LinkLabelLinkClickedEvent ...

  10. java36

    1.Main public static void main(String [ ] args) public:被jvm调用的方法,权限要足够大 static:被jvm调用的方法,不需要创建对象,直接用 ...