JavaScript函数及闭包
前面一片文章讲到过一点函数,了解到每声明一个函数就会产生一个作用域。而外面的作用域访问不了里面的作用域(把里面的变量和函数隐藏起来),而里面的可以访问到外面的。对于隐藏变量和函数是一个非常有用的技术。
基于作用域隐藏的方法叫做最小授权或最小暴露原则。
这个原则是指在软件设计中,应该最小限度的暴露必要内容,而将其内容都隐藏起来,比如某个模块或对象得API设计。隐藏变量和函数可以解决同名标识符的之间的冲突,冲突会导致变量的意外覆盖。
例如:
var a = 2;
function foo(){
var a = 3;
console.log(a);
}
foo();
console.log(a);
虽然这种技术可以解决一些问题,但是他并不理想,会导致一些额外的问题,首先必须声明一个具名函数foo(),意味着foo这个名称本身“污染”了所在的作用域,其次必须显式的通过函数名foo()调用这个函数才能运行其中的代码。
如果函数不需要函数名,并且能够自动运行,这会更加理想。幸好js提供了同时解决这两个问题的方案 -- (IIFE) Immediately Invoked Function Expression -- 立即执行函数
var a = 2;
(function foo(){
var a = 3;
console.log(a);
})()
console.log(a);
首先立即执行函数不会当做函数声明处理而是当做函数表达式处理。
区分函数声明还是函数表达式:看function在声明中是不是第一个词,如果是第一个词就是函数声明否则就是函数表达式。而立即执行函数" (function ",不是" function ",所以是函数表达式。
函数声明和函数表达式之间最重要的区别是他们的名称标识符将会绑定在何处
函数声明的函名称数会绑定在当前作用域内。假如在全局作用域创建一个函数声明,就可以在全局作用域访问这个函数名称并执行。而函数表达式的函数名称会绑定在自身的函数中,而不是当前说在作用域中。例如你全局创建一个函数表达式,如果你直接执行这个你创建的函数表达式的函数名就会报错,因为当前作用域下没有这个标识符,而你在函数表达式里面的作用域里访问这个函数名就会返回这个函数的引用。
作用域闭包,嗯,闭包这儿两个字就有点让人难以理解,(可以想象成一个包是关上的,里面隐藏了一些神秘的东西)而对于闭包的定义是这样说的:当函数可以记住并访问所在的作用域时,就产生了闭包,即使函数是在当前作用域之外执行。
for instance(拽个英文,哈哈)。
function foo() {
var a = 2;
function bar() {
console.log(a);
}
bar();
}
foo();
上面的 代码bar()可以访问外部作用域中的变量。根据上面的定义这是闭包吗?从技术来讲也许是,但我们理解的是作用域在当前作用域查找变量如果没找到会继续向上面查找,找到返回,找不到继续找,直到全局作用域。-- 而这些正是闭包的一部分。函数bar()具有一个涵盖foo()作用域的闭包。
function foo(){
var a = 2;
function bar (){
console.log(a);
}
return bar;
}
var baz = foo();
baz();
在上面的代码更好的展示了闭包。
bar()函数在定义时作用域以外的地方执行(此时在全局作用域执行)。在foo()函数执行后,通常会期待foo()整个内部作用域都被销毁,因为我们知道引擎有垃圾回收器用来释放不在使用的内存空间,由于foo()已经执行完,看上去内容不会再被使用,所以很自然的会考虑对齐进行回收,回收后意味着里面的函数和变量访问不到了。foo()执行完,baz变量存着bar函数的引用。当执行baz也就是bar函数时。console.log(a)。不理解闭包的人可能认为会报错,事实上,打印的是2;???what?
foo()函数作用域不是执行完销毁了吗?怎么还能访问到a变量?-- 这就是闭包。
当foo()执行后,bar函数被返回全局作用域下,但是bar函数还保留着当时的词法作用域(当时写代码是的顺序就已经定义了作用域,这个作用域叫词法作用域--外面函数套着里面的函数的那种)甚至直到全局作用域。所以bar还留有foo()函数的引用。使得foo()函数没有被回收。
闭包可以说不出不在,只是你没有发现认出他。在定时器,事件监听器,ajax请求,跨窗口通信或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是使用闭包。
for instance
function wait(message) {
setTimeout(function timer() {
console.log(message);
}, 1000);
}
wait("hello");
在上面的代码中将一个内部函数(名为timer)传递给setTimerout(...).timer具有涵盖wait(...)的作用域的闭包。因此还保有对变量message的引用。wait()执行1000毫秒后,它的内部作用域不会消失,timer函数依然保有wait()作用域的闭包。
而闭包和立即执行函数息息相关。
循环和闭包
for(var i = 1; i <= 5; i++){
setTimeout(function timer(){
console.log(i);
},i*1000);
}
上面代码我们以为输出的会是1-5,可事实上输出的是5个6,这是为啥啊 -- 闭包啊。
延迟函数的回调会在循环结束时执行。事实上,当定时器运行时即使每个迭代的是setTimerout(...,0),所有的回调函数依然是循环结束后才会执行。我猜是跟js执行机制有关系吧。至于为什么都是6. 因为即使5个函数是在各个迭代中分别定义的,但是他们又被封闭在一个共享的全局作用域中因此实际上只有一个i.而怎么解决呢,立即执行函数来了!!!
for (var i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
})(i) }
打印出来1,2,3,4,5了欧,这回是你想要的数了。解释一下,5次循环创建了5个立即执行函数,这5个函数的作用域都不相同,立即函数接收的参数是当前循环的i.所以当timer执行时访问的就是自己立即执行函数对应的作用域。也就是说5个timer函数分别对应5个作用域,每个作用域保存的变量i都不同,解决啦!!!
你懂闭包了吗?
js执行机制
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。JavaScript语言的设计者意识到这个问题,将所有任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。
哪些语句会放入异步任务队列及放入时机一般来说,有以下四种会放入异步任务队列:setTimeout 和 setlnterval ,DOM事件,ES6中的Promise,Ajax异步请求
JavaScript函数及闭包的更多相关文章
- JavaScript函数、闭包、原型、面向对象
JavaScript函数.闭包.原型.面向对象 断言 单元测试框架的核心是断言方法,通常叫assert(). 该方法通常接收一个值--需要断言的值,以及一个表示该断言目的的描述. 如果该值执行的结果为 ...
- javaScript函数与闭包
js中函数也是对象,具有一切对象的特征,可以作为表达式给变量赋值,可以作为函数的形参,或者函数的返回值,函数内可以嵌套函数等等,函数内以声明方式定义的函数是局部函数,用表达式声明的函数则由赋值变量的性 ...
- JavaScript 函数之 ------------------ 闭包
谈到闭包,人们常常会把匿名函数和闭包混淆在一起.闭包是指由权访问另一个函数作用域中的变量的函数.创建闭包的常见方式,就是在一个函数内部创建另一个函数,仍以前面的 createComparisonFun ...
- javascript函数闭包(closure)
一,首先感受下javascript函数的闭包 二,闭包 1,定义:闭包就是能够读取其他函数内部变量的函数,由于在javascript语言中,只有在函数内部的子函数才能够读取局部变量,因此可以把闭包简单 ...
- JavaScript函数之作用域 / 作用链域 / 预解析
关于作用域和作用链域的问题,很多文章讲的都很详细,本文属于摘录自己觉得对自己有价值的部分,留由后用,仅供参考,需要查看详细信息请点击我给出的原文链接查看原文件 做一个有爱的搬运工~~ -------- ...
- JavaScript权威设计--命名空间,函数,闭包(简要学习笔记十二)
1.作为命名空间的函数 有时候我们需要声明很多变量.这样的变量会污染全局变量并且可能与别人声明的变量产生冲突. 这时.解决办法是将代码放入一个函数中,然后调用这个函数.这样全局变量就变成了 局部变量. ...
- 深入理解javascript函数参数与闭包(一)
在看此文章,希望先阅读关于函数基础内容 函数定义与函数作用域 的章节,因为这篇文章或多或少会涉及函数基础的内容,而基础内容,我放在函数定义函数作用域 章节. 本文直接赘述函数参数与闭包,若涉及相关知识 ...
- 深入理解javascript原型和闭包(2)——函数和对象的关系
上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断. var fn = function () { }; co ...
- JavaScript中的闭包和匿名函数
JavaScript中的匿名函数及函数的闭包 1.匿名函数 2.闭包 3.举例 4.注意 1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没 ...
随机推荐
- MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections"
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establish ...
- 简析TCP的三次握手与四次分手(TCP协议头部的格式,数据从应用层发下来,会在每一层都会加上头部信息,进行封装,然后再发送到数据接收端)good
2014-10-30 分类:理论基础 / 网络开发 阅读(4127) 评论(29) TCP是什么? 具体的关于TCP是什么,我不打算详细的说了:当你看到这篇文章时,我想你也知道TCP的概念了,想要更 ...
- CPU多核控速
初学者很多对自己开发的软件使用硬件资源的时候并不注意,造成写出的东西不是很满意. 一般有两种情况: 1.写的都是同步单线程任务,不管你电脑有多少个核都不关我事 我就用你1个核所以不管怎么样都不会把CP ...
- WPF与WinForm的抉择
微软曾经对WPF(代号Avalon)抱很大的期望——新一代的华丽用户界面平台,一统Web应用和桌面应用,Flash杀手,尽管微软口头上不承认.几年下来,WPF确实实现了当初的预期的大部分功能,但离称霸 ...
- wpf中的datagrid绑定操作按钮是否显示或者隐藏
如图,需要在wpf中的datagrid的操作那列有个确认按钮,然后在某些条件下确认按钮可见,某些情况下不可见的,放在mvc里直接在cshtml页面中if..else就行了. 但是在wpf里不行..网上 ...
- scrapy爬虫框架研究!
最近由于项目需要,开始研究scrapy爬虫,走过不少弯路,准备写个记录,记下踩过的各种坑.
- javaweb各种框架组合案例(一):maven+spring+springMVC+jdbcTemplate
为了体现spring jdbc对于单表操作的优势,我专门对dao层做了一个抽离,使得抽离出的核心dao具有通用性.主要技术难点是对于泛型的反射.注意:单表操作中,数据库表的字段要和实体类的属性名保持高 ...
- HTML连载13-CSS基本格式以及文字相关的属性
一.CSS格式 1.注意点: (1)style标签必须写在head标签的开始标签和结束标签之间(也就是必须和title标签师兄弟关系) (2)style标签中的属性type属性其实可以不写,默认就是t ...
- 3012C语言_数据
第二章 数据 2.1 数据类型 2.1.1 数据类型决定 1. 数据占内存字节数 2. 数据取值范围 3. 其上可进行的操作 2.2基本数据类型 2.2.1分类 基本类型 类型 符号 关键字 字节 1 ...
- 利用org.mybatis.generator生成实体类
springboot+maven+mybatis+mysql 利用org.mybatis.generator生成实体类 1.添加pom依赖: 2.编写generatorConfig.xml文件 ( ...