jQuery技术

问答形式阅读jQuery源码(三)

字号+ 作者:H5之家 来源:H5之家 2016-11-13 15:00 我要评论( )

通过艾伦的博客,我们能看出,jQuery的promise和其他回调都是通过jQuery.Callbacks实现的。所以我们一起简单看看jQuery.Deferred和jQuery.Callbacks。来看看关于

通过艾伦的博客,我们能看出,jQuery的promise和其他回调都是通过jQuery.Callbacks实现的。所以我们一起简单看看jQuery.Deferred和jQuery.Callbacks。来看看关于他们的一些提问。

提问:jQuery.Callbacks的配置为什么是用字符串参数?

jQuery.Callbacks有四种配置,分别是once、memory、unique、stopOnFalse。而jQuery.Callbacks的配置形式却和以往我们熟悉的不同,不是使用json,而是使用字符串的形式配置的,这是为什么呢?

答:jQuery.Callbacks的配置形式,确实很怪异,jQuery.Callbacks使用了一个createOptions函数将字符串转为了json。

var rnotwhite = ( /\S+/g ); function createOptions( options ) { var object = {}; jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; }

如"once memory unique"字符串,最终会转为{"once":true,"memory":true,"unique":true}。但是笔者想问为什么不直接使用json呢?

像这种使用字符串配置的形式,JavaScript中也是有的,js中的正则表达式就是这种配置形式。

如:

var patt = new RegExp("e","gim");

这里的“gim”就是配置项。但是这种配置方法很难让人理解,这样不符合jQuery追求的理念,所以笔者认为jQuery.Callbacks使用字符串配置代替数组配置是jQuery里面的一个败笔。

提问:jQuery.Deferred封装的函数一定是异步的吗?

答:这是笔者以前一直疑惑的一个问题。我们先跳出jQuery,先使用标准的JavaScript,看看他的异步函数一定是异步的吗?

测试代码如下:

setTimeout(function(){ console.log(0); },0); setTimeout(function(){ console.log(1); },0);

console.log(2);

非常简单的一行代码,我们也很清楚输出结果是“201”。JavaScript是单线程执行的,所以异步函数的回调会在后边执行。同时异步的回调函数会被放入队列中,所以会按照进入队列的顺序执行下来。注意setTimeout的时间参数一定要给0,因为setTimeout的回调时间默认值不一定是0。

我们把中间的输出1的函数的setTimeout去除,让其不再异步。

setTimeout(function(){ console.log(0); },0); console.log(1); console.log(2);

结果变为了“120”。这个结果不需要作说明,仅是为了做对比而做的实验。

现在换位使用异步函数的语法,将输出1的部分用es7的异步函数包裹:

async function test(){ console.log(1); } setTimeout(function(){ console.log(0); },0); test(); //注意test执行的时候不能给await修饰,否则log(2)一定在test之后执行 console.log(2);

结果是“120”。

我们再将异步函数转为promise。

setTimeout(function(){
  console.log(0);
},0);
new Promise(function(resolve){
  console.log(1)
  resolve();
})
console.log(2);

上述代码应该和异步函数结果相同,结果也确实相同,结果是“120”。

从这些例子我们可以看出,使用promise封装的函数(异步函数),如果其内容不是异步的,promise执行的行为也与非异步函数的执行结果是相同。这说明异步函数也好,promise也好,仅是改变了回调方式,并不改变执行顺序。如果使用异步函数去封装非异步函数,回调的方法会发生变化,但是回调部分并不会“异步执行”。

因此jQuery.Deferred封装的函数,也应该有这样的行为。

setTimeout(function(){   console.log(0); },0); $.Deferred().resolve().then(function(){ console.log(1); }); console.log(2);

如上的结果也是“120”。

我们也可以看看jQuery.Callbacks源码,里面是不含有setTimeout,或者其他可以返回异步回调的。因此异步函数也好,promise也好,jQuery.Deferred也好,他们改变的都只是回调的方式。不过这是jQuery2.0的结果,jQuery3.0的结果是“201”,有兴趣的话大家可以自己尝试。

提问:jQuery.Callbacks的回调中的this指向什么?

答:简单的分析一下源码结构:

jQuery.Callbacks = function( options ) { var list = []; var fire = function() { list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) ; } return { add: function() { ( function add( args ) { jQuery.each( args, function( _, arg ) { list.push( arg ); } ); } )( arguments ); return this; }, fireWith: function( context, args ) { args = [ context, args.slice ? args.slice() : args ]; queue.push( args ); if ( !firing ) { fire(); } return this; }, fire: function() { self.fireWith( this, arguments ); return this; }, }; };

从这段代码可以清楚看到,通过add我们将函数保存在内部私有变量list里面,然后使用apply调用。对外暴露的函数有fireWith和fire。fireWith的context参数是最终传递给了apply,所以是我们回调中的this就是这个context。而fire函数里面调用了fireWith,传递的是自身this,所以回调的函数中的this是Callbacks对象。

提问:jQuery.Deferred的回调中的this指向什么?

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
网友点评