手机版

完全理解JavaScript中的闭包

时间:2021-10-31 来源:互联网 编辑:宝哥软件园 浏览:

介绍

闭包是可以访问另一个函数范围内的变量的函数。闭包是javascript中的一个难点,许多高级应用程序都依赖闭包来实现。让我们先看下面的例子:

函数outer(){ var I=100;function inner(){ console . log(I);}}在上面的代码中,根据变量的范围,函数外层的所有局部变量对函数内层都是可见的;函数内部的局部变量在函数内部之外是不可见的,所以不可能读取函数内部之外的局部变量。

由于函数内部可以读取函数外部的局部变量,所以只要将内部作为返回值,就可以直接在外部读取内部的局部变量。

函数outer(){ var I=100;function inner(){ console . log(I);}返回内部;} var RS=outer();RS();该功能有两个特点:

内部函数嵌套在外部函数中;函数外部返回函数内部。这样,在执行var rs=outer()之后,实际的rs指向函数inner。这段代码实际上是一个闭包。也就是说,当函数外部中的函数内部被函数外部之外的变量引用时,就创建了闭包。

范围只是变量和函数的可访问范围,也就是说,范围控制变量和函数的可见性和生命周期。在JavaScript中,变量的作用域包括全局作用域和局部作用域。

全球范围

var num1=1fun 1(){ num 2=2;}以上三个对象num1、num2、fun1都是全局范围。这里需要注意的是,没有直接赋值的变量被自动声明为具有全局作用域;

本地范围

函数wrap(){ var obj='我被包装在包装中,不能从包装外部直接访问';函数innerFun(){ //我无法从外部访问}}作用域链中的Javascript中的所有内容都是一个对象,这些对象都有一个[[Scope]]属性,该属性包含创建函数的作用域中的对象集合。这个集合称为函数的作用域链,它决定函数可以访问哪些数据。

函数add(a,b){ return a b;}创建函数时,其[[作用域]]属性会自动添加一个全局作用域2016512181604658.png  (824236)

var sum=add(3,4);调用函数时,会创建一个称为执行上下文的内部对象,该对象z定义了函数执行的环境。它也有自己的用于标识符解析的作用域链,并且它的作用域链被初始化为当前运行的函数的[[作用域]]中包含的对象。

2016512181652539.png  (835400)

在函数执行过程中,每次遇到一个变量,都会经过一个标识符解析过程,决定在哪里获取和存储数据。这个过程从作用域链的头开始,即从活动对象开始,并搜索同名的标识符。如果找到,则使用对应于该标识符的变量。如果没有找到搜索范围链中的下一个对象,如果没有找到所有对象(最后一个是全局对象),则认为标识符未定义。

闭包闭包只是一个访问其外部变量的函数。

var quo=function(status){ return { getstatus 3360 function(){ return status;} }}status保存在quo中,返回一个对象,该对象中的方法getStatus引用这个状态变量,即getStatus函数访问其外部变量status;

var new value=quo(' string ');//返回一个匿名对象,该对象被newValue引用。get status();//我考察了现状的内部变量。如果没有获得状态的方法,状态将在现状结束后自动回收(“刺痛”)。正是因为返回的匿名对象被全局对象引用,所以这个匿名对象依赖于状态,这将阻止状态的释放。

示例:

//错误方案var测试=函数(节点){ var I;for(I=0;索引节点长度;i ){ nodes[i]。onclick=function(e){ alert(I);}}}匿名函数创建一个闭包,所以它访问的I就是外部测试函数中的I,所以每个节点实际上都引用了同一个I。

2016512181810708.png  (346302)

//改进方案var测试=函数(节点){ var I;for(I=0;索引节点长度;i ){ nodes[i]。onclick=function(I){ return function(){ alert(I);};}(I);}}每个节点都绑定了一个事件。此事件接收一个参数并立即运行,然后传入I。因为它是按值传递的,所以每个周期都会为当前I生成一个新的备份。

2016512181833005.png  (358306)

闭包的作用。

函数outer(){ var I=100;function inner(){ console . log(I);}返回内部;} var RS=outer();RS();//100 RS();//101 RS();//102在上面的代码中,rs是闭包内部函数。Rs已经运行了三次,第一次100,第二次101,第三次102,说明函数outer中的局部变量I一直保存在内存中,没有调用自动清零。

闭包的作用是在执行并返回outer之后,闭包防止javascript的grabage收集机制回收outer占用的内存,因为outer的内部函数inner的执行依赖于outer中的变量。(另一种解释:outer是inner的父函数,赋给一个全局变量,这样inner就会一直在内存中,而inner的存在依赖于outer,outer会一直在内存中,调用后不会被垃圾收集。).

闭包可以访问函数中的所有变量。当函数返回闭包时,函数的作用域将保留在内存中,直到闭包不存在。

闭包和变量

由于作用域链的机制,闭包只能得到函数中任意变量的最后一个值。请看下面的例子:

函数f(){ var RS=[];for(var I=0;i 10I){ RS[I]=function(){ return I;};}返回RS;} var fn=f();for(var I=0;I fn . long;I) {console.log('函数fn[' i ']()返回值: ' fn[I]());}函数将返回一个数组。从表面上看,似乎每个函数都应该返回自己的索引值。事实上,每个函数返回10。这是因为函数f的活动对象存储在第一个函数的作用域链中,它们都引用同一个变量I,当函数f返回时,变量I的值为10,然后每个函数都持有变量I的同一个变量对象,我们可以通过创建另一个匿名函数来强制闭包按照预期的方式进行操作。

函数f(){ var RS=[];for(var I=0;i 10I){ RS[I]=function(num){ return function(){ return num;};}(I);}返回RS;} var fn=f();for(var I=0;I fn . long;I) {console.log('函数fn[' i ']()返回值: ' fn[I]());}在这个版本中,我们没有将闭包直接分配给数组,而是定义了一个匿名函数,并将执行匿名函数的结果立即分配给数组。这里,匿名函数有一个参数num。当调用每个函数时,我们传入变量I。由于参数是通过值传递的,变量I将被复制到参数num。在这个匿名函数中,创建并返回一个用于访问num的闭包,这样rs数组中的每个函数都有自己num变量的副本,因此它可以返回不同的值。

闭包中的这个对象。

var name=' Jackvar o={ name : 'bingdian ',getName : function(){ return function(){ return this . name;};} } console . log(o . getname()();//杰克瓦尔名字='杰克';var o={ name : 'bingdian ',getName : function(){ var self=this;return function(){ return self . name;};} } console . log(o . getname()();//兵点内存泄露。

函数assignHandler(){ var El=document . getelementbyid(' demo ');El . onclick=function(){ console . log(El . id);} } assignHandler();上面的代码创建了一个闭包作为el元素事件处理程序,这个闭包创建了一个循环引用。只要匿名函数存在,el的引用数至少为1,所以它占用的内存永远不会被回收。

函数assignHandler(){ var El=document . getelementbyid(' demo ');var id=el.idEl . onclick=function(){ console . log(id);} el=null} assignHandler();将变量el设置为null可以取消对DOM对象的引用,保证其占用内存的正常恢复。

模仿块级范围。

任何一对花括号({和})中的语句集都属于一个块,其中定义的所有变量在代码块之外都是不可见的,这称为块级范围。

(function(){ //块级范围})();闭包的应用。

保护函数中的变量。和前面的例子一样,函数外层的I只能被函数内层访问,不能被其他方式访问,从而保护了I的安全性,在内存中维护一个变量。和前面的例子一样,因为闭包,函数outer中的I总是存在于内存中,所以每次执行rs()的时候,I都会增加1。

版权声明:完全理解JavaScript中的闭包是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。