摘要:所以经常看到的说闭包就是绑定了上下文环境的函数。我更偏向于闭包是一个函数和声明该函数的词法环境的组合。里面的闭包先上一个闭包该例子的解释上面的代码,在函数里面定义的函数和这个函数声明的词法环境就形成了一个闭包。
闭包是什么
第一种说法:闭包创建一个词法作用域,这个作用域里面的变量被引用之后可以在这个词法作用域外面被自由访问,是一个函数和声明该函数的词法环境的组合
第二种说法:闭包就是引用了自由变量的函数,这个自由变量与函数一同存在,即使脱离了创建它的环境。所以经常看到的说闭包就是绑定了上下文环境的函数。
我更偏向于闭包是一个函数和声明该函数的词法环境的组合。
JS里面的闭包 先上一个闭包function sayHello(name){ let str = "Hello,${name}"; function say(){ console.log(str); } return say; } let myHello = sayHello("abby"); myHello();该例子的解释
上面的代码,在sayHello函数里面定义的say函数和这个函数声明的词法环境就形成了一个闭包。say函数引用了sayHello函数里面定义的一个变量str,并且sayHello函数将say这个函数return了出去,这样,在sayHello函数的外面也能访问到它词法作用域里面的变量str,最后就像say这个函数和str这个变量绑定了一样
为什么在外部还能访问到变量str呢?在一些语言中,一般认为函数的局部变量只在函数的执行期间可以访问
当上段代码在执行到let myHello = sayHello("abby");这段代码的时候,按理会销毁掉sayHello这个函数的执行环境,但是在这里却没有,因为,sayHello这个函数返回的是一个函数,这个函数里面的str引用了外部的变量str,如果销毁了sayHello的执行环境就会找不到了,所以,sayHello的执行环境会一直在内存中,所以也就会有闭包会增加内存开销的说法
体会在JavaScript语言中,只有函数内部的子函数才能读取内部变量,可以把闭包简单理解成“定义在一个函数内部的函数”
闭包就是将函数内部和函数外部连接起来的一座桥梁
闭包的用处 1、读取函数内部的变量 2、让这些变量始终保持在内存中function createIncrementor(start) { return function () { return start++; }; } var inc = createIncrementor(5); inc() // 5 inc() // 6 inc() // 7
start是函数createIncrementor的内部变量,通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算,闭包inc使得函数createIncrementor的内部环境一直存在,因为inc始终存在内存中,而inc的存在依赖于createIncrementor,因此该函数不会在调用结束后,被垃圾回收机制回收
3、封装对象的私有属性和私有方法function Person(name) { var _age; function setAge(n) { _age = n; } function getAge() { return _age; } return { name: name, getAge: getAge, setAge: setAge }; } var p1 = Person("xiaoming"); p1.setAge(25); pa.getAge(); //25
函数Person的内部变量_age,通过闭包getAge和setAge,变成了返回对象p1的私有变量,外层函数每次运行,都会产生一个新的闭包,而这个闭包又会保留外层函数的内部变量,内存也就消耗较多
在举几个例子1、常见的闭包都是return出来一个函数,但并不是说明,闭包一定需要return一个函数,return一个函数也只是为了能在作用域范围之外访问一个变量
let say; function sayHello(name){ let str = "Hello,${name}"; say = function(){ console.log(str); } } let myHello = sayHello("abby"); say();
2、同一个调用函数生成同一个闭包环境,在里面声明的所有函数同时具有这个环境里面的变量的引用
let get,up,down function setUp(){ let number = 20; get = function(){ console.log(number); } up = function(){ number += 3; } down = function(){ number -= 2; } } setUp(); get(); up(); down(); get();
3、每一个调用函数都会创建不同的闭包环境,里面的变量互不影响
function newClosure(){ let array = [1,2]; return function(num){ array.push(num); console.log("array:${array}"); } } let myClosure = newClosure(); let yourClosure = newClosure(); myClosure(3); yourClosure(4); myClosure(5);
4、在循环里面创建闭包
function newClosure(){ for(var i=0;i<5;i++){ setTimeout(function(){ console.log(i); }); } } newClosure();//5个5
改进方法一:创建一个新的闭包对象,这样每个闭包对象里面的变量就互不影响
function log(i){ return function(){ console.log(i); } } function newClosure(){ for(var i=0;i<5;i++){ setTimeout(log(i)); } } newClosure();
每次log(i)都会创建不同的闭包对象,所有的回调函数不会指向同一个环境
改进方法二:使用自执行函数,外部的匿名函数会立即执行,并且把i作为它的参数,此时函数内变量e就拥有了i的一个拷贝。当传递给setTimeout的匿名函数执行时,它就拥有了对e的引用,而这个值是不会被循环改变的
function newClosure(){ for(var i=0;i<5;i++){ (function(e){ setTimeout(function(){ console.log(e); }); })(i) } } newClosure();
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/108005.html
摘要:一前言本文章将以报表下载为例,给大家介绍三种文件下载的方式。通过二进制数据流的方式下载这种方式是我目前采用的方式,用于处理报表下载。缺点对于数据量不大的文件,这种方式是可行的。 一、前言 本文章将以excel报表下载为例,给大家介绍三种文件下载的方式。 原文地址:简谈文件下载的三种方式 | Rychou 二、正文 1. 通过服务器文件地址下载 这是最常见的文件下载方式,大多数网站的音频...
摘要:一前言本文章将以报表下载为例,给大家介绍三种文件下载的方式。通过二进制数据流的方式下载这种方式是我目前采用的方式,用于处理报表下载。缺点对于数据量不大的文件,这种方式是可行的。 一、前言 本文章将以excel报表下载为例,给大家介绍三种文件下载的方式。 原文地址:简谈文件下载的三种方式 | Rychou 二、正文 1. 通过服务器文件地址下载 这是最常见的文件下载方式,大多数网站的音频...
摘要:常量接口是对接口的一种不良使用。如果这些常量最好被看作是枚举类型成员,那就应该用枚举类型来导出。因为客户端既不能创建枚举类型的实例,也不能对它进行扩展,因此很可能没有实例,而只有声明过的枚举常量。换句话说,枚举类型是实例受控的。 问题 我们偶尔能在项目中看到如下风格的代码: public class ResponseCode { public static final int ...
摘要:而用关键字调用构造器,总是会创建一个新的对象,无论内容是否相同。中对象的哈希码被频繁地使用比如在等容器中。字符串不变性保证了码的唯一性因此可以放心地进行缓存。对于所有包含方式新建对象包括的连接表达式,它所产生的新对象都不会被加入字符串池中。 前言 前阵子和同事在吃饭时聊起Java的String,觉得自己之前的笔记写的略显零散。故此又重新整理了一下。 String在Java中算是一个有意...
阅读 3342·2021-11-22 09:34
阅读 2835·2021-10-09 09:43
阅读 1352·2021-09-24 09:47
阅读 2182·2019-08-30 12:53
阅读 970·2019-08-29 14:00
阅读 3337·2019-08-29 13:17
阅读 2248·2019-08-28 18:00
阅读 1264·2019-08-26 12:00