Javascript学习日志(三):闭包
说实话,前面一节的原型和原型链在当初学的时候并没有很头疼,对着高级编程第三版撸了几遍就理解透了,闭包这一节真的挺头疼的,很惭愧,看了差不多十来遍吧,还翻看了网上的其他博客和解释文档,五花八门的表达方式,虽然核心思想都一致,但是实在是不能做到自己的理解,后来结合函数作用域链,好不容易有点开窍,趁着热乎劲儿,赶紧写下来,感兴趣的可以参考一下。
闭包:
高级编程上面的解释是指有权访问另一个函数作用域中的变量的函数,(是一个函数);
创建闭包的常见方式,就是在一个函数内部创建另一个函数。
在理解闭包之前,先要清楚一个函数在创建到调用再到结束后的一系列过程:
当一个函数被调用的时候,会先创建一个执行环境以及相应的作用域链,然后用arguments对象和其他命名参数的值来初始化函数的活动对象,但在作用域链中,外部函数的活动对象始终是排在第二,外部函数的外部函数的活动对象排在第三。。。。。。最后一个是全局执行环境下的活动对象。(一圈一圈往外,就像射箭的靶子,中间的红心就是当前的执行环境下的活动对象)
以一个函数为例:
function compare(value1, value2){
return ……
}
var result = compare(5, 10);
在创建函数compare()的时候,会先创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]](就是作用域)属性中,当调用compare()函数的时候,会为函数创建一个执行环境,然后通过复制[[Scope]]中的对象来构建起执行环境的作用域链,此后会将活动对象推到执行环境作用域链的前端。此时的compare()的作用域链上有两个变量对象,第一个是本地变量对象:arguments,value1,value2;第二个则是全局变量对象:result,compare。当函数执行完毕后,局部活动对象就会被销毁,只留下全局执行环境下的变量对象。
但是闭包的情况又有所不同:
如果在函数内部再定义一个函数,则里面的函数会将外部函数的活动对象添加到它的作用域链中,但是它的作用域链的前端还是它自己的活动对象,后面依次是外部函数的活动对象,外部函数的外部函数的活动对象。。。。。直到全局执行环境下的变量对象。
function compare(value1, value2){
return function(name){
…….
}
}
var compareA = compare(1,2);
var result = compareA(“aaaa”);
当匿名函数在compare()中被返回之后,它的作用域链会被初始化,包含compare()的活动对象以及全局变量对象。当compare()执行完毕后,它的活动对象不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。也就是说,compare()函数返回后,它的执行环境的作用域链会被销毁,但是它的活动对象仍然保留在内存中,直到匿名函数被销毁后,它的活动对象才会销毁。拿上面的例子来说明,当执行compare(1,2)的时候,如果没有闭包,则执行完后compare的执行环境就会被销毁,活动对象也不会保留在内存中,内存中只剩下全局变量对象,但是如果函数内部有闭包,此时里面的内部函数返回了,这时候初始化内部函数的作用域链,会保留内部函数外的活动对象(也就是compare的活动对象),还有全局变量对象在内存中,所以这时候,compare的活动对象还是可以访问的,没有被销毁,虽然此时的compare的作用域链已经销毁了。
=====
做一个总结:
当一个函数被创建的时候,同时会产生一个作用域链,这个作用域链是全新的,上面没有什么当前活动对象,只有全局对象,这条作用域链是核心链子,保存在内部”作用域“属性中,一直都在的,当函数被调用的时候,会先创造一个执行环境(关于执行环境,活动对象,变量对象这些名词请参考第一篇随笔),然后复制刚刚内部的“作用域”中的对象来构建一条执行环境的作用域链,这条跟创建函数时所保存的作用域链不是同一条,这条是专门给执行环境准备的,所以当前变量对象,也就是活动对象会被顶到这条作用域链的最前端,此时这条链子上有两个变量对象,最前面的是当前活动对象,后面的是全局环境下定义的变量对象,如果没有闭包,当函数执行完毕之后,当前执行环境就会销毁,同时当前活动对象也会销毁,作用域链上面只剩下全局环境下的变量对象。在第一篇中我们说到如何访问一个变量,标识符解析的时候就是沿着作用域链逐层向上找。所以这时候我们要访问当前变量对象就没办法了。
此时闭包的作用就有了。
在函数内部返回一个新函数,此时新函数被调用,那么此时又会给新函数创建一个新的执行环境,同样当前执行环境下的变量对象(也是活动对象)就会被顶到作用域链的最前端,而刚刚那个外层的函数的执行环境下的变量对象就被挤到了第二个,由于外层的函数已经执行完了,这时候它对应的执行环境已经销毁,但是它的执行环境下的变量对象还在这条作用域链上,在第二个,所以我们依然可以通过这条作用域链来访问到刚刚已经执行过的函数的执行环境下所定义的变量对象,虽然它已经执行过并且被销毁了。
我再做进一步总结:
闭包的作用:能够访问到内部函数的变量对象,
实现方法:在函数内部再返回一个新函数
实现原理:在作用域链上,在要访问的变量对象前面再塞一个新的变量对象,就能保证要访问的变量对象不会销毁并且可以访问到了。(就像串糖葫芦一样,你担心最上面那颗可能会掉,那就在那颗上面再串一颗,就能保证第二颗不掉了)
=====
闭包与变量:
闭包只能取得包含函数(外部函数)中的任何变量的最后一个值。闭包保存的是整个变量对象,而不是某个特殊的变量。
下面是经典案例:
function createFunctions(){
var result = new Array();
for(var i = 0; i<10; i++){
result[i] = function(){
return i
}
}
return result
}
每个函数都会返回10,因为每个函数中的作用域链中都保存着createFunctions()的活动对象,而它的活动对象是i,当createFunctions()执行完毕后,i的值为10,所以每个函数的作用域中的内部i都是10。这是按引用传递,如果想要达到我们期待的结果,就要按值传递。
function createFunctions(){
var result = new Array();
for(var i = 0; i<10; i++){
result[i] = (function(num){
return function(){
return num
}
})(i)
}
return result
}
关于this对象:
匿名函数的执行环境具有全局性,因此其this对象通常指向window。所以闭包中的this通常是全局变量对象下的。
Javascript学习日志(三):闭包的更多相关文章
- JavaScript学习总结(三)——闭包、IIFE、原型、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
- javascript学习日志:前言
javascript学习日志系列的所有博客,主要理论依据是<javascript权威指南>(犀牛书第6版)以及<javascript高级程序设计第三版>(红色书),目前js行业 ...
- JavaScript学习记录三
title: JavaScript学习记录三 toc: true date: 2018-09-14 23:51:22 --<JavaScript高级程序设计(第2版)>学习笔记 要多查阅M ...
- JavaScript学习第三天
今天学习第三天. 凡事都是需要坚持的,坚持下去. 学习内容: 1.document.getElementById(""),document.getElementByTagName( ...
- Javascript学习笔记三——操作DOM(二)
Javascript学习笔记 在我的上一个博客讲了对于DOM的基本操作内容,这篇继续巩固一下对于DOM的更新,插入和删除的操作. 对于HTML解析的DOM树来说,我们肯定会时不时对其进行一些更改,在原 ...
- JavaScript学习笔记(三)——this、原型、javascript面向对象
一.this 在JavaScript中this表示:谁调用它,this就是谁. JavaScript是由对象组成的,一切皆为对象,万物皆为对象.this是一个动态的对象,根据调用的对象不同而发生变化, ...
- JavaScript学习笔记(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
- JavaScript学习总结(三)——this、原型、javascript面向对象
一.this 在JavaScript中this表示:谁调用它,this就是谁. JavaScript是由对象组成的,一切皆为对象,万物皆为对象.this是一个动态的对象,根据调用的对象不同而发生变化, ...
- JavaScript学习总结(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
随机推荐
- 2.跑nodejs文件
1.安装nodejs的环境,这里不介绍了. app.js 'use strict'; //process.argv 接受变量 var command = process.argv[2]; var a ...
- Learn c for the Second day
十六进制对应的二进制码 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 0 ...
- python进阶(6):多态,封装
昨天说了面向对象三大特性的继承,今天来说多态和封装,很多其他程序员说python不支持多态没有多态,并不是python没有多态,而是python处处皆多态.今天的讲课重点会放在封装. 一.多态 1.多 ...
- python enumerate 枚举函数用法
enumerate()说明 enumerate()是python的内置函数 enumerate在字典上是枚举.列举的意思 对于一个可迭代的(iterable)/可遍历的对象(如列表.字符串),enum ...
- [算法题] Remove Element
题目内容 本题来源:LeetCode Given an array and a value, remove all instances of that value in place and retur ...
- 解析spring循环依赖策略
循环依赖 所谓循环依赖就是多个Bean之间依赖关系形成一个闭环,例如A->B->C->...->A 这种情况,当然,最简单的循环依赖就是2个Bean之间互相依赖:A->B ...
- 设计模式(4)--AbstractFactory(抽象工厂模式)--创建型
1.模式定义: 抽象工厂是应对产品族概念的,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类 2.模式特点: 抽象工厂模式为创建一组对象提供了一种解决方案.与工厂方法模式相比,抽象工 ...
- 老司机和你深聊 Kubenertes 资源分配之 Request 和 Limit 解析
欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯云容器服务团队 Kubernetes是一个容器集群管理平台,Kubernetes需要统计整体平台的资源使用情况,合理地将资源分配 ...
- PHPExcel导出excel表格
if(!function_exists('exportExcel')){ //导出excel文件 function exportExcel($datas, $save_method='ht ...
- 从jsp页面到servlet传值的不同方式
1.利用超链接<a></a>来传递参数 例如: <td><a href="/month811/Servlet?id=${student.id}&am ...