Node.js中的事件驱动编程详解(2)

Node最初就被设计成一个非阻塞I/O服务器平台,因此一般情况下,你应该期望运行在它上面的所有代码都是非阻塞的。因为JavaScript非常小,而且它不强制使用任何I/O模型(因为它没有标准的I/O类库),因此Node建立在一个很纯净的环境里,不会有什么历史遗留问题。

Node和JavaScript如何简化了异步应用程序

Node的作者Ryan Dahl,最初使用C来开发这个项目,但是发现维护函数调用的上下文太复杂,导致代码复杂度很高。然后他转用Lua,但是Lua已经有个几个阻塞的I/O类库,阻塞和非阻塞混在一起可能会让开发人员很迷惑并因此阻碍了很多人构建可伸缩的应用,于是Lua也被Dahl抛弃了。最后他转向了JavaScript,JavaScript中的闭包及第一级对象的函数,这些特性使JavaScript非常适合用作事件驱动编程。JavaScript的魔力是让Node如此流行的一个主要原因。

什么是闭包

闭包可以理解为一个特殊的函数,但是它可以继承并访问它自身被定义的那个作用域里的变量。当你将一个回调函数作为参数传递给另外一个函数时,它稍候会被调用,神奇的是,这个回调函数被稍候调用时,它居然记住了它自身定义所在的那个上下文以及父上下文里的变量,而且还可以正常访问它们。这个强大的特性是Node成功的核心。

下面的例子将展示在Web浏览器里JavaScript闭包是如何工作的。假如,你要监听一个按钮的单机事件,你可以这样做:

复制代码 代码如下:


var clickCount = 0;

document.getElementById('myButton').onclick = function() {

clickCount += 1;

alert("clicked " + clickCount + " times.");

};

使用jQuery时是这样:

复制代码 代码如下:


var clickCount = 0;

$('button#mybutton').click(function() {

clickedCount ++;

alert('Clicked ' + clickCount + ' times.');

});

JavaScript里,函数是第一类对象,就是说你可以把函数当作参数来传递给其他函数。上面的两个例子,前者把一个函数赋值给另一个函数,后者把函数作为参数传递给另一个函数,单击事件的处理函数(回调函数)可以访问函数定义所在代码块下的每个变量,在这个例子里,它可以访问在它父闭包内定义的clickCount变量。

clickCount变量处在全局作用域(JavaScript里最外层的作用域),它保存了用户点击按钮的次数,通常在全局作用域下存储变量是个坏习惯,因为那样很容易跟其他代码冲突,你应该把变量放在使用它们的本地作用域里。大多时候,只用把代码用一个函数包装起来,等于另外创建了闭包,这样就可以很容易避免污染全局环境,就像这样:

复制代码 代码如下:


                  (function() {

var clickCount = 0;

$('button#mybutton').click(function() {

clickCount ++;

alert('Clicked ' + clickCount + ' times.');

});

}());


  注意:上面代码的第七行,定义了一个函数后立刻调用它,这是JavaScript里一个常见的设计模式:通过创建函数来创建一个新的作用域。

闭包如何帮助异步编程

在事件驱动编程模型里,先编写事件发生后将要运行的代码,然后把这些代码放到一个函数里,最后把这个函数当作参数传递给调用者,稍后由调用者函数调用。

在JavaScript里,一个函数并不是个孤立的定义,它同时会记住自己被声明的那个作用域的上下文,这种机制让JavaScript的函数可以访问函数定义所在那个上下文及父上下文里的所有变量。

当你把一个回调函数当作参数传递给调用者后,这个函数就会在稍后的某个时刻被调用。即使定义回调函数的那个作用域已经结束,在回调函数被调用时,它依然能够访问这个已结束的作用域及其父作用域里的所有变量。像最后那个例子,回调函数在jQuery的click()内部被调用,它却依然能访问clickCount变量。

前面展现了闭包的神奇之处,把状态变量传递给一个函数就可以让你不用维护状态就能进行事件驱动编程,JavaScript的闭包机制会帮你维护它们。

小结

事件驱动编程是一种通过事件触发来决定程序执行流程的编程模型。程序员为他们感兴趣的事件注册回调函数(通常被称作事件处理器),然后系统在事件发生时调用已注册的事件处理器。这种编程模型有很多传统阻塞编程模型所不具备的优势,以前要实现类似的特性,就必须使用多进程/多线程才行。

JavaScript是种强大的语言,因为它的第一类型对象的函数和闭包特性,让它很适合事件驱动编程。

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgspxy.html