前端进阶(第二期)- 作用域闭包笔记
原文地址:
2-1 从作用域链谈闭包
2-2 JavaScript深入之闭包
2-3 深入javascript——作用域和闭包
JavaScript Closures Explained by Mailing a Package
2-1 从作用域链谈闭包
原文地址
闭包的概念
闭包是指有权访问另外一个函数作用域中的变量的函数 --- 红宝书
关键于两点:
- 是一个函数
- 能访问另外一个函数的作用域中的变量
晦涩难懂有木有? 个人感觉文中的例子描述的非常好,特此摘录:
function getName() {var name = "美女的名字";console.log(name); //"美女的名字"
}
function displayName() {console.log(name); //报错
}
复制代码
外部访问不到函数作用域中的变量。但是为了得到美女的名字,不死心的单身汪把代码作了一些修改便得逞了,如下:
function getName() {var name = "美女的名字";function displayName() {console.log(name); }return displayName;
}
var 美女 = getName();
美女() //"美女的名字"
复制代码
这时的‘美女’是一个闭包了,单身汪想怎么玩就怎么玩了(邪恶脸--)。
- 闭包可以访问当前函数以外的变量
- 即使外部函数已经返回,闭包仍能访问外部函数定义的变量
- 闭包可以更换外部变量的值
作用域链
---为什么闭包就能访问外部函数的变量呢?
复制代码
首先回顾下上期学习的 --- 执行上下文
Javascript中有一个执行上下文(execution context)的概念,它定义了变量或函数有权访问的其它数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
作用域链:当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链。
作用域链 和 原型继承 查找时的区别:如果去查找一个普通对象的属性,但是在当前对象和其原型中都找不到时,会返回undefined;但查找的属性在作用域链中不存在的话就会抛出ReferenceError。
那些图例还是没怎么搞懂,我需要回去再翻翻我的红宝书!
闭包的作用需要在看一下这一篇文章
2-2 JavaScript深入之闭包
原文地址
MDN 对闭包的定义为:
闭包是指那些能够访问自由变量的函数。
什么是自由变量?
自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。
ECMAScript中,闭包指的是:
从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
从实践角度:以下函数才算是闭包: 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回) 在代码中引用了自由变量
举个例子:
var scope = "global scope";
function checkscope(){var scope = "local scope";function f(){return scope;}return f;
}var foo = checkscope();
foo();
复制代码
了解具体的执行过程, 我们知道 f 执行上下文维护了一个作用域链:
fContext = {Scope: [AO, checkscopeContext.AO, globalContext.VO],
}
复制代码
对的,就是因为这个作用域链,f 函数依然可以读取到 checkscopeContext.AO
的值,说明当 f 函数引用了 checkscopeContext.AO
中的值的时候,即使 checkscopeContext
被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO
活在内存中,f 函数依然可以通过 f 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。
所以,让我们再看一遍实践角度上闭包的定义:
- 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
- 在代码中引用了自由变量
2-3 深入javascript——作用域和闭包
原文地址
作用域
- 变量声明提前
- 没有块级作用域
作用域链
作用域链的逐级查找,也会影响到程序的性能,变量作用域链越长对性能影响越大,这也是我们尽量避免使用全局变量的一个主要原因。
闭包
- 基础概念(略)
- 闭包中的变量
在使用闭包时,由于作用域链机制的影响,闭包只能取得内部函数的最后一个值,这引起的一个副作用就是如果内部函数在一个循环中,那么变量的值始终为最后一个值。
例子1:
//该实例不太合理,有一定延迟因素,此处主要为了说明闭包循环中存在的问题
function timeManage() {for (var i = 0; i < 5; i++) {setTimeout(function() {console.log(i);},1000)};
}
//调用timeManage输出都是5
复制代码
例子2:
function createClosure(){var result = [];for (var i = 0; i < 5; i++) {result[i] = function(){return i;}}return result;
}
//调用timeManage输出仍都是5
复制代码
以上两个例子可以看出闭包在带有循环的内部函数使用时存在的问题:因为每个函数的作用域链中都保存着对外部函数(timeManage、createClosure)的活跃对象,因此,他们都引用着同一变量i,当外部函数返回时,此时的i值为5,所以内部的每个函数i的值也为5。
可以通过 匿名包裹器 (匿名自执行函数表达式)来强制返回预期的结果:
function timeManage() {for (var i = 0; i < 5; i++) {(function(num) {setTimeout(function() {console.log(num);}, 1000);})(i);}
}
复制代码
或者 在闭包匿名函数中再返回一个匿名函数赋值 :
function timeManage() {for (var i = 0; i < 10; i++) {setTimeout((function(e) {return function() {console.log(e);}})(i), 1000)}
}
//timeManager();输出1,2,3,4,5
function createClosure() {var result = [];for (var i = 0; i < 5; i++) {result[i] = function(num) {return function() {console.log(num);}}(i);}return result;
}
//1, 2, 3, 4, 5
复制代码
无论是匿名包裹器还是通过嵌套匿名函数的方式,原理上都是由于函数是按值传递,因此会将变量i的值复制给实参num,在匿名函数的内部又创建了一个用于返回num的匿名函数,这样每个函数都有了一个num的副本,互不影响了。
- 闭包中的 this
由于匿名函数的作用域是全局性的,因此闭包的this通常指向全局对象window:
var scope = "global";
var object = {scope:"local",getScope:function(){return function(){return this.scope;}}
}
复制代码
调用object.getScope()()
返回值为global
而不是我们预期的local
,前面我们说过闭包中内部匿名函数会携带外部函数的作用域,那为什么没有取得外部函数的this
呢?每个函数在被调用时,都会自动创建this
和arguments
,内部匿名函数在查找时,搜索到活跃对象中存在我们想要的变量,因此停止向外部函数中的查找,也就永远不可能直接访问外部函数中的变量了。总之,在闭包中函数作为某个对象的方法调用时,要特别注意,该方法内部匿名函数的this
指向的是全局变量。
解决办法 :
var scope = "global";
var object = {scope:"local",getScope:function(){var that = this;return function(){return that.scope;}}
}
复制代码
-内存与性能
由于闭包中包含与函数运行期上下文相同的作用域链引用,因此,会产生一定的负面作用,当函数中活跃对象和运行期上下文销毁时,由于必要仍存在对活跃对象的引用,导致活跃对象无法销毁,这意味着闭包比普通函数占用更多的内存空间,在IE浏览器下还可能会导致内存泄漏的问题,如下:
function bindEvent(){var target = document.getElementById("elem");target.onclick = function(){console.log(target.name);}}
复制代码
上面例子中匿名函数对外部对象target产生一个引用,只要是匿名函数存在,这个引用就不会消失,外部函数的target对象也不会被销毁,这就产生了一个循环引用。解决方案是通过创建target.name副本减少对外部变量的循环引用以及手动重置对象:
function bindEvent(){var target = document.getElementById("elem");var name = target.name;target.onclick = function(){console.log(name);}target = null;}
复制代码
闭包中如果存在对外部变量的访问,无疑增加了标识符的查找路径,在一定的情况下,这也会造成性能方面的损失。解决此类问题的办法我们前面也曾提到过:尽量将外部变量存入到局部变量中,减少作用域链的查找长度。
总结:闭包不是javascript独有的特性,但是在javascript中有其独特的表现形式,使用闭包我们可以在javascript中定义一些私有变量,甚至模仿出块级作用域,但闭包在使用过程中,存在的问题我们也需要了解,这样才能避免不必要问题的出现。
前端进阶(第二期)- 作用域闭包笔记相关推荐
- 浪晋的测试小讲堂萌芽计划第二期视频学习笔记
学习测试ing 按照知乎: https://zhuanlan.zhihu.com/p/32505591 上面提供的B站课程整理笔记 视频链接如下: https://www.bilibili.com/v ...
- 【进阶2-3期】JavaScript深入之闭包面试题解
(关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue.React.Node源码和实战.面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第8天. 本计划一共28期,每 ...
- 阿里云“7天实践训练营”入门班第二期学习笔记 第五天 在线编程挑战
阿里云"7天实践训练营"入门班第二期学习笔记 第五天 在线编程挑战 吾辈,完全不会编程 以下内容全程来自阿里云社区的大佬分析讲解 原题目 知识点:搜索.字符串.位运算 有一天Jer ...
- 七月在线机器学习中的数学第二期笔记1
这套笔记是跟着七月在线机器学习中的数学第二期的学习而记录的,主要记一下我再学习机器学习的时候一些概念比较模糊的地方,具体课程参考七月算法官网: 七月 数理统计和参数估计部分 一. 概率与数理统计 首先 ...
- 『AD域攻防实践』第二期学习笔记
上一周的直播课中,小伙伴们跟随御守实验室的师傅一起了解了"AD域在攻防对抗场景下的安全现状",课程结束后,我们为大家整理了学习笔记,也将录屏和PPT公布在了公众号和微信群,帮助大家 ...
- OpenMMLab AI实战营第二期|人体关键点检测与MMPose学习笔记
OpenMMLab AI实战营第二期|人体关键点检测与MMPose学习笔记 文章目录 OpenMMLab AI实战营第二期|人体关键点检测与MMPose学习笔记 一.前言 1.1 人体姿态概述 1.2 ...
- 『赠书活动--第二期』清华社赞助 | 《前端系列丛书》
『赠书活动 | 第二期』 本期书籍:<前端系列丛书> Electron入门与实战 Vue.js核心技术解析与uni-app跨平台实战开发 HTML5+CSS3+JavaScript从入门 ...
- 【进阶3-1期】JavaScript深入之史上最全--5种this绑定全面解析
this的绑定规则总共有下面5种. 1.默认绑定(严格/非严格模式) 2.隐式绑定 3.显式绑定 4.new绑定 5.箭头函数绑定 1 调用位置 调用位置就是函数在代码中被调用的位置(而不是声明的位置 ...
- 【进阶3-5期】深度解析 new 原理及模拟实现
定义 new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例. --(来自于MDN) 举个栗子 function Car(color) {this.color = color; ...
最新文章
- 软考-网络总结-协议
- python装饰器函数-Python精进-装饰器与函数对象
- 计算机键盘是编码键盘还是非编码键盘,矩阵按键原理图_矩阵按键扫描实例
- debug疯了_作业没疯,我先疯了 益智解谜《作业疯了》试玩
- linux内核链表的使用
- php获取掩码,如何在PHP中实现位掩码?
- 史上最全亚历山大大帝名言
- Python 2.7 Tutorial —— 流程控制
- 技术管理规划-设定团队的职能
- 关于IE透明度失效的问题
- c语言中next和prior连在一起,C语言中双向链表和双向循环链表详解
- 阿里架构师的工作总结:Spring Cloud在架构演进中起到的作用
- 我的世界基岩版种子和java版种子_我的世界无限循环种子
- Ubuntu18.04安装HP laser 1020打印机
- matlab中的封装引脚,lm5117封装引脚图及功能
- 微信小程序实现控制标题栏背景色
- xshell (sed 命令)
- 淘宝商品点赞可以增加权重吗?
- 洛谷——P1348 Couple number(java实现)
- Mac book笔记本输入法错乱
热门文章
- 余承东和张小龙背后的故事:成年人,请远离线性努力
- 千亿参数大模型时代,QQ浏览器团队十亿级小模型「摩天」登顶CLUE
- 一文读懂深度学习:这个AI核心技术被美国掌控,很危险
- 个性化推荐系统研究热点之用户画像
- 人工智能领域最重要的10大里程碑:AlphaGo征服世人
- 人工智能浪潮下的语音交互——VUI设计(基础篇)
- AI从业者需要应用的10种深度学习方法
- 领英2017报告:机器学习、大数据工程师成为最热门的新兴职业
- torch.unbind()
- (已解决)IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY。Someone could be eavesdropping on you