作用域闭包
来源:互联网 发布:排序算法比较次数 编辑:程序博客网 时间:2024/05/23 14:24
闭包是基于词法作用域书写代码产生的自然结果
当函数可以记住并访问所在的此法作用域的时候,就产生了闭包
var a=2; (function () { console.log(a); })()
由上面的定义就知道,上面的代码并不是严格意义上的闭包。因为并没有在函数本身创建的此法作用域以外执行。
。也就是当前词法作用域之外执行。
function foo() { var a=2; function bar() { console.log(a) } bar(); } foo();
但是上面的这个不是严格意义上的闭包,下面这个程序是严格意义上的闭包
function foo() { var a=2; function bar() { console.log(a) } return bar; } var baz=foo(); baz();
一个实际意义上的完整的闭包。
bar()的此法作用域能够访问foo() 的内部作用域。然后将bar()函数本身当作一个值进行传递。也就是在这个例子中,bar所引用的函数对象本身当作返回值。所以bar在自己定义的词法作用域以外的地方被执行。
闭包的缺点:
在foo执行之后,我们通常会期待foo的整个作用域都被销毁。但是实际上并没有。foo()的内部作用域依然存在,因此没有被 回收。bar()本身还在使用这个作用域。这个作用域会一直存活,以供bar()在任何时间被调用。
闭包:bar()依然持有该作用域的的引用,这个引用就是闭包。
简单的说闭包就是那个会一直存在的作用域。。
闭包的其他出现的形式;
函数类型作为参数进行传递
无论使用何种方式对函数的值进行传递,当函数在别出被调用时可以观察到闭包。
function foo() { var a=2; function baz() { console.log(a) //2 } bar(baz) } function bar(fn) { fn(); //这也是闭包 } foo();
所以闭包在定时器、事件监听器、AjAx请求,跨窗口通信,web workers或者任何异步或者同步的任务中,只要使用回调函数,实际上就是在使用闭包。
循环和闭包
for(var i=0;i<=5;i++){ setTimeout(function timer() { console.log(i); },i*1000); }
运行的结果是每隔一秒就会输出一个6.
原因:延迟函数的回调会在循环结束的时候才执行。尽管循环中的5个函数是在各个迭代中分别定义的,但是他们被封闭在一个共享的全局作用域中,因此实际上只有一个i。
解决办法是:每次循环的时候都要分别为自己保留一个i的副本。
for(var i=0;i<=5;i++){ (function (i) { setTimeout(function timer() { console.log(i); },i*1000); })(i) }
以上代码就实现了每隔一秒就输出0,1,2,3,4,5
for(var i=0;i<=5;i++){ let j=i; setTimeout(function timer() { console.log(j); },j*1000); }
以上代码就实现了每隔一秒就输出0,1,2,3,4,5,使用let关键字让每一次循环都新生成一个作用域,执行延迟函数。
for(let i=0;i<=5;i++){ setTimeout(function timer() { console.log(i); },i*1000); }
以上代码就实现了每隔一秒就输出0,1,2,3,4,5。变量在循环的过程中不止被声明一次每次迭代都会声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。
模块
function CoolModule() { var something="cool"; var another=[1,2,3]; function doSomething() { console.log(something); } function doAnother() { console.log(another.join("!")); } return { doSomething:doSomething, doAnother:doAnother } } var module=new CoolModule(); module.doSomething(); module.doAnother();
这种模式就被称为模块。CoolModule这个名字一定要起得非常有意义和独特。
分析上面的这种模块的模式:
首先:CoolModule()只是一个函数,必须要通过调用它创建一个模块的实例。如果不执行外部函数,内部作用域和闭包都无法创建。
其次:CoolModule()返回一个用对象字面量的语法{key:value}来表示对象。这个返回的对象中含有对内部函数而不是内部数据变量的引用。这样内部的数据还是处于隐藏的状态。可以将这个对象类型的返回值看作本质上是模块的公共API。(一个很好的例子就是jquery)
所以模块模式需要两个必要的条件:
1、必须有外部的封闭函数(CoolModule),该函数必须至少被调用一次(每次创建一个新的模块的时候,都会创建一个新的模块实例)
2、封闭函数至少要返回一个内部函数,这样内部函数才能在私有作用域中形成闭包。
注意:
模块也是普通的函数,因此可以接收参数
function CoolModule(id) { function identify() { console.log(id); } return { identify:identify } } var cc1=new CoolModule('sa'); cc1.identify(); //sa var cc2=new CoolModule('ffdfd'); cc2.identify(); //ffdfd
模块模式的另一个强大的用法:
命名将要作为公共API返回的对象
function CoolModule(id) { function change() { PublicAPI.identify=identify2 } function identify1() { console.log(id); } function identify2() { console.log(id.toUpperCase()); } var PublicAPI={ //命名将要作为公共API返回的对象 identify:identify1, change:change } return PublicAPI; } var cc=new CoolModule("sasas"); cc.identify(); //sasas cc.change(); cc.identify(); //SASAS
也就是给模块的返回值起了一个名字,这样的话就可以修改。
现代模块机制
var Mymodules=(function Manager() { var modules={}; function define(name,deps,impl) { //deps为依赖的模块 for(var i=0;i<deps.length;i++){ deps[i]=modules[deps[i]]; } modules[name]=impl.apply(impl,deps); } function get(name) { return modules[name]; } return { define:define, get:get } })(); Mymodules.define("bar",[],function () { function hello(who) { return "Let me introduce " +who; } return {hello:hello}; }); Mymodules.define("test",[],function () { function testConsole() { return "we are testing"; } return {testConsole:testConsole}; }); Mymodules.define("foo",["bar","test"],function (bar) { function awesome() { var cc="dsdasa"; console.log(test.testConsole().toUpperCase()) console.log(bar.hello("cc").toUpperCase()) } return { awesome:awesome } }); var test=Mymodules.get("test"); var bar=Mymodules.get("bar"); var foo=Mymodules.get("foo"); foo.awesome(); console.log(bar.hello("hippo")); console.log(2121);
块作用域的替代方案
在ES3发布以来,js中就有了块作用域,with和catch语句就是块作用域的两个例子,但是随着ES6的let语句的引入,我们的代码终于有了创建完整、不受约束的块作用域的能力。
{ let a=3; console.log(a); //3 } console.log(a); //a is not defined
let让程序形成了一个块级作用域。
{ var a=3; console.log(a); //3 } console.log(a); // 3
var 就不可以形成一个块级作用域。
以上是ES6解决块级作用域的方案,但是在ES6之前怎样解决块级作用域?
try{ throw 2; }catch (a){ console.log(a); //2 } console.log(a);// a is not defined
总结
当函数可以记住并访问所在的此法作用域,即使函数在当前的词法作用域外执行,这时就产生了闭包。
- 作用域闭包
- 作用域闭包
- 作用域闭包
- JavaScript笔记:作用域闭包
- 作用
- 作用
- 作用
- 深入学习js之浅谈作用域(作用域闭包)
- 作用域闭包四五章及附录
- 你不懂JS:作用域与闭包 第五章:作用域闭包
- __define_initcall 作用(subsys_initcall 作用)
- static作用const作用
- 作用域::的作用
- 作用域的作用
- JavaScript作用域闭包(你不知道的JavaScript)
- 读书笔记《你不知道的JavaScript上卷》1.5作用域闭包
- IsPostBack作用
- encodeRedirectURL 作用?
- Discuz论坛主页和板块分区使用不同的模板
- linux命令_压缩文件
- 仿太极图自定义view
- Java类的实例化
- Scala:函数式编程之下划线underscore
- 作用域闭包
- 十二之续、快速排序算法的深入分析
- Mac使用笔记(一):文件显示完整路径
- 备忘链接
- 设置paint.setXfermode(mMode)无效
- LeetCode 319. Bulb Switcher
- 十二之再续:快速排序算法之所有版本的c/c++实现
- Python-随请求发送data/headers
- 洛谷 P1019 [NOIP2000 T3] 单词接龙