8.6闭包
背景:3.10 变量作用域
在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明一个局部变量或者函数参数中带有的变量和全局变量重名,则局部变量会覆盖全局变量;
在全局作用域编写代码时可以不写var 语句,但是声明局部变量时必须使用var语句
3.10.1函数作用域和声明提前
js中没有块级作用域,取而代之是函数作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
js的函数作用域值:在函数内声明的所有变量在函数体内是始终可见的。
变量在声明之前就已经可以使用---------声明提前
3.10.2作为属性的变量
使用var声明一个变量,不可以使用delete删除;不用var定义的变量则可以使用delete删除。
js中使用this关键字来引用全局对象,却没有方法引用局部变量中存放的对象。
3.10.3作用域链
类比 局部变量看做是自定义实现的对象的属性。
每一段js都有一个与之关联的作用域链:该作用域链是一个对象列表或者链表,该组对象定义了这段代码”作用域中“的变量。
当js需要查找变量x时(过程称作变量解析)
1 、它会从链中的第一个对象进行查找,如果这个对象有一个名为x的变量,则会直接使用这个属性的值
2 、如果第一个对象中不存在名为x的属性,则会继续查找链上的下一个对象。
3如果第二个对象依旧没有名为x的属性,则会继续向下查找
若是始终没有找到 则会抛出一个引用错误异常。
在js'的最顶层代码中(即不包括在任何函数定义的代码),作用域链由一个全局对象组成。
在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局变量;
在一个嵌套的函数体内,作用域链上至少有三个对象。
理解对象链的创建规则:
当定义一个函数时,它实际上保存一个作用域链。
当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上,同时创建一个新的更长的表示函数调用作用域的”链“,当函数返回值时就从域链中将这个
绑定变量的对象删除。
对于嵌套函数来说:
每次调用外部函数时候,内部函数在每次定义的时候都有微妙的差别--------在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同
闭包
函数对象可以通过作用域链关联起来,函数体内部的变量都可以保存在函数作用域内--------------闭包
当调用函数时,闭包所指向的作用域链和定义函数时的作用域链不是同一个作用域链时,事情就变得很微妙;
当一个函数嵌套了另一个函数时,外部函数将嵌套的函数对象作为返回值的时候往往会发生这样的事情;
1、了解闭包首先要了解嵌套函数的词法作用域规则
闭包特性 可以捕捉到局部变量(和参数)并一直保存下来,看起来像变量绑定到了在其中定义它们的外部函数。
2、实现闭包:
函数定义时的作用域链到函数执行时以及函数执行结束后,仍然有效。
回想作用域链,每次调用js函数时候,都会为之创建一个新的对象用来保存局部变量,把这个对象添加至作用域链中。
当函数返回的时候,就从域链中将这个绑定变量的对象删除。
如果不存在嵌套的函数,每个嵌套函数都各自对应一个作用域链,并且这个作用域链指向一个变量绑定对象。
如果这些嵌套的函数对象在外部函数中保存下来,那么他们也会和所指向的变量绑定对象一样当作垃圾回收。
如果这个函数定义了嵌套函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一外部引用指向这个嵌套的函数,他就不会作为垃圾被回收,
并且它所指向的变量绑定对象也不会被当做垃圾回收
闭包可以捕捉单个函数调用时的局部变量,并将这些变量用作私有状态。
自定义函数:
uniqueInterget.counter=0;
function uniqueInterget(){ return uniqueInterget.counter++ ;}
此时,恶意代码可能将计数器重置或者把一个非正数的值赋个它,导致函数不一定能产生”唯一的“整数;
闭包重写:
var uniqueInterget=(function(){
var counter=0;
return function(){return counter++;};
}());
第一段代码定义了一个立即调用的函数(函数的开始带有左圆括号),因此是这个函数的返回值赋给变量uniqueInterget。
函数体中,这个函数返回另一个函数,这是一个嵌套函数,我们将它赋值给变量uniqueInterget,嵌套的函数是可以访问作用域内的变量的,而且可以访问外部函数中定义的counter变量
当外部函数返回之后,其他任何代码都无法访问counter变量,只有内部的函数才能访问到它。
对于counter私有变量,并不是只能在一个单独的闭包内,在同一个外部函数内定义的多个嵌套函数也可以访问它,且多个嵌套函数都共享一个作用域链:
function counter() { var n=0; return { count:function () { return n++; }, reset:function () { n = 0; } };}
var c=counter(),d=counter(); //创建两个计数器c.count(); //0 d.count(); //0 它们互不干扰c.reset(); //reset() 和count()方法共享状态c.count(); //0 因为重置了Cd.count(); //1 因为没有重置d闭包技术可以用来共享的私有状态的通用做法。