谈谈Javascript中的异步调用的上下文

早些日子审查同事的一段代码--Javascript实现的类似WindowForm下的MessageBox,有一段代码大致是这样的(这是示意代码,仅保留了和问题相关的部分):

MessageBox.prototype = {

// 方法定义

Show : function(callback,requester,…){

// 这里实际上是异步调用,单击按钮后触发。

callback(requester, result);

}

}

 

// 调用显示MessageBox的方法

Message.Show(callbackFunc, this);

这里的requester,和MessageBox没有任何关系,其实是callback所需要的参数,用于显性的告知callback请求回调时的上下文。这和Javascript作用域有关,网上谈得很多,傅小康也整理过一篇文档,这里不赘述了。这是一个不差的做法。jQuery中也大量充斥了类似代码,例如jQuery.ajax有一个参数context,就是类似的作用(jQuery内部会以此重设回调函数的上下文)。

虽然被普遍接受,但是总有一些不和谐的地方:既然requester仅仅是callback关注的内容,那么为什么要通过MessageBox来传递呢,MessageBox引用了和它完全无关的东西。用《重构》的话来讲,这就是Smell

其实有更雅致的做法,调用者在传递callback的时候,就指定了callback的上下文,修改后的定义就很简洁了:

MessageBox.prototype = {

// 方法定义

Show : function(callback,…){

// 实际上是在用于单击按钮后调用

callback(result);

}

}

调用的地方也需要稍作修改:

Message.Show(callbackFunc.bind(this));

bind的操作结果就是返回了一个工作在指定上下文下的新的函数实例,我们应该把它作为Javascript中异步调用的标准模式。

BTW:有关JavascriptAPI,还是MSDN比较靠谱,也比W3School中完整。)

 

由此也可以引申一个比较有趣的话题,Javascript是面向对象的范式语言么?

过去我一直觉得是的,源于外界刻意强调Javascript的某些类似OO的特性,例如:原型继承、通过构造函数定义类型等。其实并非如此,this就暴露了一切根源,Javascript的方法(函数)和定义时绑定的对象是没有强关联的,仅和调用上下文相关,可以随时改变绑定的对象,例如bindcallapply等一系列方法就是起这个作用的。

Javascript是面向函数的范式语言,它封装的单位并不是Type),而是闭包Closure)也就是调用函数和它的上下文。按照这个思路去理解的话,对于回调函数,我们传递的不仅仅是函数本身,还应该包括它执行时的上下文,所以调用bind等于创建了一个闭包,它才是在Javacript下封装的正确做法。理解这一点,才能更好的理解Javascript这门语言。

 

还想再引申一下,大概可以上升为设计模式的一点东西。

我们在封装的时候会考虑两类事物:数据和行为。对于面向对象的范式而言,数据和行为是一起定义的,在需要的时候创建一个包含数据和行为的实例;对于面向函数的范式,分别定义数据和行为(PlainObjectFunction),在需要的时候将行为绑定到数据上(如Javacript中的callapply)。

你可能感兴趣的:(开发心得)