闭包是函数创建时范围内所有变量的集合. 要使用闭包, 请在另一个称为嵌套函数的函数内创建一个函数. 内部函数将有权访问外部函数范围中的变量(Closure 有助于访问外部函数范围), 即使在返回外部函数之后也是如此. 每次创建函数时都会创建闭包.
在继续了解闭包之前, 让我们首先了解一下 JavaScript 作用域链.
通常, 范围有两种:
全局作用域
局部作用域
在 ES5 版本中, 函数内部的变量在外部不可见. 但是, 块内部的变量 (条件如 if 或 while) 在外部也可见.
由此, ES5 具有功能范围. 没有块作用域.
根据 ES5, 使用函数是在代码中声明块作用域的唯一方法.
但是, 在 ES6 中, 通过提供块范围的 let 和 const 关键字可以缓解这种情况.
无论如何, 最好了解 JavaScript 是如何逐步发展的.
让我们在 ES5 版本中继续:
- var a = 10;
- function App(){
- var b = 2;
- console.log(a); // 10
- console.log(b); // 2
- }
- console.log(b); // ReferenceError: b is not defined
- App();
我们已经知道, a 是全局变量, b 是特定于 App 函数的局部变量.
我们无法从局部范围中获取局部变量的值.
使用嵌套函数 - 函数内的函数
- var a = 10;
- function App(){
- var b = 2;
- var d = 3;
- function add(){
- var c = a + b;
- }
- return add;
- }
- var x = App();
- console.dir(x);
在这里, App 是父函数, add 是子函数.
使用 console.dir 而不是使用 console.log 来控制指定 JavaScript 对象的所有属性, 帮助开发人员获取该对象的属性
变量 x 被分配给 App 函数, 而 App 函数返回 add 函数. 因此, 我们可以看到 add 函数的所有对象属性.
如果在浏览器中看到控制台, 则可以在 Scopes 数组中看到 Closure 对象.
由于内部函数添加访问外部函数变量 b 和 d, 所以这两个变量将被添加到闭包对象中以供引用.
让我们来看下一个闭包的例子:
- var a = 10;
- var startFunc;
- function App(){
- var b = 2;
- function add(){
- var c = a + b;
- console.log(c);
- }
- startFunc = add();
- }
- App(); // Invoke the App function
- startFunc;
- // as the App function invoked above will assign the add function to startFunc & console the value of c
一个名为 startFunc 的全局函数被分配给 add 函数, add 函数是父 App 函数的子函数.
这只有在调用 App 函数之后才可能实现, 否则 startFunc 将作为一个全局变量, 没有任何赋值
闭包在 JavaScript 中的应用
我们大多数人在编码时使用闭包, 但我们不知道为什么要使用闭包. JavaScript 不像其他面向对象的编程语言那样具有私有, 公共, 受保护等访问修饰符. 因此, 我们必须使用函数来保护名称空间不受 ES5 中外部代码的使用.
特别是在函数中, 立即调用的函数表达式 (IIFE) 是在声明之后立即执行的表达式. 不需要在函数声明之后调用该函数.
IIFE 允许用 JavaScript 编写模块模式(设计模式之一).
IIFE 的语法定义为:
- (function(){
- //variables & scope that inside the function
- })();
让我们举个例子:
- var studnetEnrollment = (function () {
- //private variables which no one can change
- //except the function declared below.
- var count = 0;
- var prefix = "S";
- // returning a named function expression
- function innerFunc() {
- count = count + 1;
- return prefix + count;
- };
- return innerFunc;
- })();
- var x = studnetEnrollment(); // S1
- console.log(x);
- var y = studnetEnrollment(); // S2
- console.log(y);
count 和 prefix 是两个私有变量, 不能被任何人改变, 只能被内部函数访问 (这里是它的 innerFunc). 这种访问只能通过称为闭包(Closure) 的特性实现.
在第一次调用 studentEnrollment 函数时, 函数中的 count 变量被 innerFunc 函数递增 1.
在第二次时, 计数将递增计数的前一个值, 即 1 到 2
这些都可以通过闭包特性实现.
结论
闭包是外部函数中的变量集合, 它允许访问内部函数作用域来保护全局命名空间.
闭包使开发人员能够编写干净的代码, 就像 OOP 语言一样, 不会混淆 ES5 版本中的全局变量名和局部变量名.
快乐编码......! ! ! ! !
来源: http://www.css88.com/web/javascript/15019.html