jQuery技术

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

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

答:标准的promise中的this,是指向全局作用域的,例如window。 var d = new Promise((r)= {r()})d.then( function (){console.log( this )}) // 注意,此处不可以用()={console.log(this)} 输出的是window,证明列

答:标准的promise中的this,是指向全局作用域的,例如window。

var d = new Promise((r)=>{r()}) d.then(function(){console.log(this)}) //注意,此处不可以用()=>{console.log(this)}

输出的是window,证明列我们之前的说法。

我们再来看看jQuery.Deferred的示意源码:

jQuery.extend( { Deferred: function( func ) { var tuples = [ [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], [ "notify", "progress", jQuery.Callbacks( "memory" ) ] ], state = "pending", promise = { then: ) { var fns = arguments; return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[ 1 ] ]( function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .progress( newDefer.notify ) .done( newDefer.resolve ) .fail( newDefer.reject ); } else { newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } } ); } ); fns = null; } ).promise(); }, promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; promise[ tuple[ 1 ] ] = list.add; if ( stateString ) { list.add( function() { state = stateString; }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); promise.promise( deferred ); if ( func ) { func.call( deferred, deferred ); } return deferred; } } );

代码中构建了两个对象——deferred和promise。deferred相当于对异步过程的封装,而promise是对promise模型的封装。代码的最后将promise织入到deferred中。

resolve、reject、notify是promise的三种设置状态的函数,实际上这三个函数的执行策略相似,所以jQuery采用策略模式,用一个二维数组tuples将这3种策略封装起来。

代码下半部分对tuples的遍历,我们可以看到,jQuery实现了promise的done、fail、progress这三个函数,这三个函数实现方式都是对jQuery.Callbacks.add的封装。对deferred扩展了六个函数——resolve、reject、notify和resolveWith、rejectWith、notifyWith,前三个函数是对后三个函数的封装,后三个函数都是对jQuery.Callbacks.fireWith封装的。前三个函数调用后三个函数的时候,context参数都是传递的promise(除非回调函数被用bind、reject、resolve改变了上下文)。所以当deferred执行resolve、reject、notify的时候,回调函数的this是promise对象;当this使用resolveWith、rejectWith、notifyWith,或者用bind、call、apply改变了resolve、reject、notify的执行上下文的时候,回调的this是指向给定的对象。

再看上边then方法的定义,同样是遍历tuples。tuples数组定义的done、fail、progress这三个函数名称,与then的三个参数一致(done、catch、notify),这三个参数都是回调函数。回调他们的方式是deferred的done、fail、progress三个函数,我们知道这三个函数是使用promise织入进来的,真正的方法是promise的resolve、reject、notify这三个函数,因此then中的回调的this也是指向的是promise或者之前被指定的上下文。

this指向promise其实并没有太多意义,而通过xxxWith函数或者用bind、call、apply改变了resolve、reject、notify的上下文的方式调用,才是jQuery的亮点。jQuery提供这样的api的目的是为了我们可以指定promise的this,这样貌似更灵活,更方便我们操作回调。

但是,回调中this的不同,是jQuery.Deferred和标准promise一个很大的区别,这是不标准的用法,这一点一定要切记。jQuery提供的api虽然很方便,但是这样改变列promise模型,是不推荐的用法,尤其是promise如今已经收纳到es6的语法中,es7的异步语法也是基于promise的,在不支持promise的浏览器上创建出标准的promise才是jQuery更该做的,因为只有这样才能实现promise语法的对接。

提问:为什么将promise织入到deferred中?

答:按照jQuery的思路,deferred相当于对异步过程的封装,是promise的创建者与指挥者,但是根据jQuery的思路,将二者统一能更好的简化异步对象的模型,如:

var d = $.Deferred(); d.resolve(); d.promise().then(()=>{ ... }) d = $.Deferred(); d.resolve() .then(()=>{ ... })

下边的写法是不是更加简单紧凑呢?同时也符合jQuery的链式操作。简单来说,少了一个对象的概念,大家当然更容易理解。

事实上,promise还可以织入到其他对象中,如:

var d = $.Deferred(); d.resolve(); d.promise(myObj); myObj.then(()=>{ ... })

通过这种方式,jQuery可以很灵活的把promise的操作嵌入到任何对象中,非常方便。

提问:jQuery.Deferred.promise()有没有实现promises/A+吗?

 

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

相关文章
网友点评