首先,从一个计数器开始。编程
var counter = 0; function increment() { counter = counter + 1; console.log("Number of events: " + counter); }
increment(); // Number of events: 1 increment(); // Number of events: 2 increment(); // Number of events: 3
上面的代码简单粗暴,可是咱们很快会遇到下一个问题:若是想要再建立一个计数器怎么办呢。固然咱们能够建立两个变量,两个函数,但这样是否是太low了:闭包
var counter1 = 0; function incrementCounter1() { counter1 = counter1 + 1; console.log("Number of events: " + counter1); } var counter2 = 0; function incrementCounter2() { counter2 = counter2 + 1; console.log("Number of events: " + counter2); } incrementCounter1(); // Number of events: 1 incrementCounter2(); // Number of events: 1 incrementCounter1(); // Number of events: 2
并且当须要更多的计数器时,就更不可能用这种方法了。函数
上面的那段代码,咱们更想作的是封装成一个函数,去除冗余代码。下面咱们就初步尝试一下闭包:优化
function createCounter() { var counter = 0; function increment() { counter = counter + 1; console.log("Number of events: " + counter); } return increment; }
如今来看一下发生了什么。咱们将会建立两个计数器,而且使用它们来跟踪两个独立的事件:this
var counter1 = createCounter(); var counter2 = createCounter(); counter1(); // Number of events: 1 counter1(); // Number of events: 2 counter2(); // Number of events: 1 counter1(); // Number of events: 3
额,看起来有点复杂。。但实际上很简单。咱们只须要分解一下实现的逻辑。翻译
首先,建立了一个局部变量counter
而后,建立了一个局部函数increment
,能够增长counter
的值。code
其实这个createCounter()
的实现几乎跟最开始那个计数器同样。惟一不一样的就是它被函数包裹起来了。因而,这种结构就被称做闭包。对象
而后就到了最重要的地方:blog
createCounter()
里的最后一步 返回了 increment
局部函数 ,注意,这里返回的不是函数的调用结果,而是函数自己。接口
这也就是说,当咱们使用下面的语句建立计数器,咱们其实是生成了一个新的函数。
// fancyNewCounter is a function in this scope var fancyNewCounter = createCounter();
这就是闭包强大的地方。每一个经过createCounter()
生成的函数都保持追踪它们本身产生的counter
的值。也就是说,这个返回的函数会记住他被建立时的环境。
能够看到的重要的一点就是内部的counter变量都是互相独立的。建立了两个计数器,它们会各自在闭包中分配一个新的counter变量。咱们能够看到:
每一个计数器都从1开始计数:
var counter1 = createCounter(); counter1(); // Number of events: 1 counter1(); // Number of events: 2 var counter2 = createCounter(); counter2(); // Number of events: 1
第二个计数器没有收到第一个计数器的值的影响。
counter1(); // Number of events: 3
“Number of events: x”这种消息是能够的,可是若是信息能够描述咱们正在计数的事件的类型,会不会更好。例如,若是咱们能够在咱们的计数器里面加一个名字:
var catCounter = createCounter("cats"); var dogCounter = createCounter("dogs"); catCounter(); // Number of cats: 1 catCounter(); // Number of cats: 2 dogCounter(); // Number of dogs: 1
咱们能够往闭包里传一个参数
function createCounter(counterName) { var counter = 0; function increment() { counter = counter + 1; console.log("Number of " + counterName + ": " + counter); } return increment; }
能够看出来,createCounter在实现过程当中不只能记住局部变量counter,也记住了传进来的变量。
上面的写法有个问题就是,当咱们在执行计数器的时候,很难直观的看出来这个计数器是个将要计算增量的函数,下面的写法会更简洁:
var dogCounter = createCounter("dogs"); dogCounter.increment(); // Number of dogs: 1
function createCounter(counterName) { var counter = 0; function increment() { counter = counter + 1; console.log("Number of " + counterName + ": " + counter); }; return { increment : increment }; }
在上面的代码中,咱们返回了一个包含闭包中全部函数的对象。从某种意义上来讲,咱们正在定义咱们的闭包能够响应的一系列消息
如今咱们能够很是简单的将递减的函数加入到计数器中。
function createCounter(counterName) { var counter = 0; function increment() { counter = counter + 1; console.log("Number of " + counterName + ": " + counter); }; function decrement() { counter = counter - 1; console.log("Number of " + counterName + ": " + counter); }; return { increment : increment, decrement : decrement }; } var dogsCounter = createCounter("dogs"); dogsCounter.increment(); // Number of dogs: 1 dogsCounter.increment(); // Number of dogs: 2 dogsCounter.decrement(); // Number of dogs: 1
上面的代码的console.log重复了两次。应该明确的建立一个函数来显示计数器的值。
function createCounter(counterName) { var counter = 0; function display() { console.log("Number of " + counterName + ": " + counter); } function increment() { counter = counter + 1; display(); }; function decrement() { counter = counter - 1; display(); }; return { increment : increment, decrement : decrement }; } var dogsCounter = createCounter("dogs"); dogsCounter.increment(); // Number of dogs: 1 dogsCounter.increment(); // Number of dogs: 2 dogsCounter.decrement(); // Number of dogs: 1
display
函数看起来和increment()
和decrement()
很像的样子,但实际上是很是不一样的。咱们没有在对象的结果中返回这个函数,也就是说下面的语句会报错:
var dogsCounter = createCounter("dogs"); dogsCounter.display(); // ERROR !!!
咱们让display()
函数从外面的世界隐藏了,它只供createCounter()
内部访问
让咱们使用闭包来实现栈操做
function createStack() { var elements = []; return { push: function(el) { elements.unshift(el); }, pop: function() { return elements.shift(); } }; } var stack = createStack(); stack.push(3); stack.push(4); stack.pop(); // 4
注意:在Javascript中,闭包可能并非实现栈数据类型的最佳实现方式,Prototypes可能会更好一些
若是你作过面向对象编程,你可能会注意到,上面的结构特别像类啊对象啊实例变量和公有/私有方法。
按:很久没翻译了,这篇文章翻译的有些智障,可是不耽误看代码,看完仍是颇有收获的!