闭包是函数的一种特性 一种现象,它不指代某个具体的语法、定义、概念等,要理解闭包就必须要清楚函数的作用域。
1、词法作用域和激活对象
JS中的函数是通过词法(语法、定义)划分作用域的 而不是动态创建作用域,这就是说函数是运行在它定义时的作用域,而不是执行时的作用域,例如:当嵌套函数从外围函数中被导出时,它的执行地发生了改变 但是作用域不变。
在调用函数时,JS会首先将作用域设为定义时起作用的那个作用域,然后创建一个激活对象添加到作用域链的头部。函数体内的局部变量、定义时的形参、嵌套的函数,都是激活对象的属性。
2、闭包的成因、原理
当嵌套函数被导出时 定义的作用域是如何起作用的呢,这是因为函数定义时 会将作用域链保存起来 并且成为函数内部状态的一部分。
这里嵌套函数返回的值,是存在于外围函数的激活对象中,可是当外围函数调用结束时,外围函数的激活对象会删除,从而使函数恢复之前的状态,这也是我们所期待的。
但是嵌套函数改变了这一规则,当函数体内有嵌套函数时,如果嵌套函数只在函数体内使用(调用),嵌套函数随着外围函数退出(执行完毕)而结束,这也没有任何问题。
可是,如果此时嵌套函数被导出或者存在其他引用,这时外围函数的激活对象就会保留下来。
结果是外围函数的一次特定调用的激活对象中的值被保留下来,这种代码和作用域的综合体称为闭包。(解释了 此处为什么g调用能拿到值)。
3、闭包的应用场景
当嵌套函数被导出 或者存在其他引用时 会发生闭包,那么嵌套函数是如何被导出?
1、将嵌套函数作为返回值;
2、嵌套函数作为外部对象的属性(方法 属性值是一个匿名函数的是方法);
3、使用自调用匿名函数,在闭包中能够帮助我们简化结构,便于处理;
很多时候,我们期望给某段代码一个独立的执行环境,避免与其他部分的代码之间相互干扰,可以将它们放入一个函数中去执行。
自调用匿名函数
4、闭包的使用案例
1、设置一个私有 且 持久存储的数据 例如 一个能不断自增的计数器
很多时候 我们要给某个值自增需要把它设为全局变量 但是这样在全局裸露的环境中 值很容易被篡改 可以考虑将值放入一个局部环境中 存储 但是如何才能维持局部环境中的数据呢 只能通过闭包实现。
2、定义一个访问器 给一个对象 添加一对get/set方法 用来操作对象的属性。
网友评论