说说闭包跟构造函数,然后再来看看用闭包实现的Deferred

说说闭包和构造函数,然后再来看看用闭包实现的Deferred

大家都知道,得到对象有两种方式,第一是构造函数,第二是闭包,两种方式各有优点,而且用的情景也是不一样的,但是从“外观上”看,有个比较醒目的区别就是:闭包创建的对象不必用new。我们先来看看两者的具体区别:

构造函数:

优点:

1.理解起来相对较简单

2.可以实现继承

3.原型方法用的同一块内存

缺点:

1.如果没有做特殊处理,必须要用new运算符

2.无法实现私有属性,所有属性都会暴露在对象中

闭包:

优点:

1.无需使用new运算符

2.可以利用作用域干很多事情

3.可以将不想被外部看到的属性封装在闭包内部

缺点:

1.比较不好理解

2.不好实现继承

3.无法运用apply和call,因为是利用作用域实现的。

光说没意思,先来个例子先:

构造函数方式:

function Person(name,age){
  this.name=name;
  this.age=age;
}
Person.prototype.sayName=function(){
  alert(this.name);
}

闭包方式

function createPerson(name,age){
    return {
      sayName:function(){
        alert(name);
      }
      getAge:function(){
        return age;
      }
    }
}

在jquery内部中,deferred对象,jqXHR都是采用的闭包的方式,而主构造函数、Event事件对象采用的是构造函数的方式。

我们先说说deferrd对象,说之前先看看是怎么用的,最值得一提的就是jq的pipe方法,当然后面变成then方法。由于我看的源码是1.7.1版本的,所以就叫pipe方法吧,这个方法可以说是deferred的精髓。

 var defer1=$.Deferred();
 defer1
	 .pipe(function(arg){
	 	var defer=$.Deferred();
	 	setTimeout(function(){
	 		console.log(arg++);
	 		defer.resolve(arg);
	 	},1000);
	 	return defer;
	 })
	 .pipe(function(arg){
	 	var defer=$.Deferred();
	 	setTimeout(function(){
	 		console.log(arg++);
	 		defer.resolve(arg);
	 	},1000);
	 	return defer;
	 })
	 .pipe(function(arg){
	 	console.log(arg);
	 	return arg;
	 })
	 .pipe(function(arg){
	 	console.log(arg);
	 });

 defer1.resolve(1);
结果:

说说闭包跟构造函数,然后再来看看用闭包实现的Deferred
打开浏览器后:1s 后   打印1   再1s后  打印2  3  3

对于单线程的js来说,学会异步编程是非常重要的,而这里也使用职责链的模式让结构更加清晰,对于deferred整体是怎样实现的我就不一一赘述,我们直接来看pipe方法是怎么实现的。

pipe: function( fnDone, fnFail, fnProgress ) {
					return jQuery.Deferred(function( newDefer ) {
						jQuery.each( {
							done: [ fnDone, "resolve" ],
							fail: [ fnFail, "reject" ],
							progress: [ fnProgress, "notify" ]
						}, function( handler, data ) {
							var fn = data[ 0 ],
								action = data[ 1 ],
								returned;
							if ( jQuery.isFunction( fn ) ) {
								deferred[ handler ](function() {//为deferred增加回调函数
									returned = fn.apply( this, arguments );
									if ( returned && jQuery.isFunction( returned.promise ) ) {
										returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
									} else {
										newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
									}
								});
							} else {
								deferred[ handler ]( newDefer[ action ] );//为deferred增加失败回调函数,函数为defer的执行
							}
						});
					}).promise();
				}

先从第二行看,每次执行pipe方法,都返回了另一个deferred对象,而每个deferred对象,无非就两种方法,一是往里面加方法,二是执行里面的方法,你可以看成跟自定义事件差不多,只不过deferred对象是基于CallBacks(回调函数列表)的,功能更强大,但是原理跟自定义事件差不多,都是基于发布-订阅模式。有了这个概念,再来往后看。

在jQuery.Deferred传入一个函数,会在jQuery.Deferred最后执行这个函数,并且会传入参数本deferred对象,执行环境也会被修改为本deferred对象,但是不要忘了,现在还在第一个deferred对象的函数体内,也就是这个函数既能访问到第一个deferred对象,又能访问到新建这个deferred对象。


然后我们抛开each不看,直接看each的第二个参数,hanlder为"done","fail","progress",   data为对应的数组,然后往下看,fn为pipe调用时传进来的函数,action为对应的执行方法名,你可以看作是自定义事件里的fire,让所有函数执行,只不过有三个自定义的事件。而returned就是pipe传进来的函数的执行结果。


后面都以done举例:


然后看deferred[ handler ](function() {})这句话

这里的deferred为第一个deferred对象,往第一个deferred对象里加回调函数。也就是当异步队列(deferred)resolve的时候会执行function里面的代码.那到底执行了什么呢?


returned = fn.apply( this, arguments );

这句话,就是把传进来的函数执行一次,而且这个this取决与第一个derferred触发的时候执行环境是什么,而arguments就是触发第一个deferred的时候传进的实参。


if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
}

如果返回值拥有promise方法,说明是deferred对象或者是promise对象,promise对象就是去掉了执行方法的deferred对象,跟deferred对象差不多,因为一般返回的都是自己执行,不需要在外部执行。

先将返回值变成promise对象,然后把第二个deferred对象的执行方法放到返回promise的函数列表里。可能这里有点晕了,那就画图来解释。

说说闭包跟构造函数,然后再来看看用闭包实现的Deferred

现在涉及三个deferred对象,第一个deferred对象执行,将导致第二个传进去的function执行,如果这个function返回值为deferred,则把第二个deferred的执行方法,也就是resolve等方法放在第三个deferred的函数列表中,也就是第三个deferred执行将导致第二个deferred执行,而第二个deferred的执行时机则是自己控制的。这样就实现了异步队列。


而当返回值不是deferred时,直接执行第二个deferred对象,且参数为返回值。


这里利用作用域链让其拥有第一个deferred的引用,又用形参和apply让其拥有第二个deferred的引用,这样当自己返回一个deferred的时候,就能有效的将三个deferred对象链接起来,形成一条异步的职责链模式,是非常巧妙的。在这里充分运用了闭包创造对象的方法,要是改用构造函数的方式,则需要把第一个和第二个deferred用变量存起来,然后再来操作各个deferred对象,虽然也是可以实现的,但是可能就没这么巧妙了。

1楼wangdan1030昨天 20:47
学习了,讲的很详细!