jQuery如何实现链式操作
沉沙 2018-05-28 来源 : 阅读 1055 评论 0

摘要:链式操作仅仅是通过对象上的方法最后return this把对象再返回回来,对象当然可以继续调用方法啦,所以就可以链式操作了。希望阅读本篇文章以后大家有所收获,加深大家对jQuery的理解。

链式操作

原理相信百度一下一大把,实际上链式操作仅仅是通过对象上的方法最后

return this

把对象再返回回来,对象当然可以继续调用方法啦,所以就可以链式操作了。那么,简单实现一个:

//定义一个JS类function Demo() {
 
}//扩展它的prototype
Demo.prototype ={
    setName:function (name) {
        this.name = name;
        return this;
    },
    getName:function () {
        return this.name;
    },
    setAge:function (age) {
        this.age = age;
        return this;
    }
};
////工厂函数function D() {
    return new Demo();
}//去实现可链式的调用
D().setName("CJ").setAge(18).setName();

 

但……为什么要用呢?

一般的解释:

节省代码量,代码看起来更优雅。

例如如果没有链式,那么你可能需要这样写代码:

document.getElementById("ele").dosomething();
document.getElementById("ele").dootherthing();

这个代码中调用了两次document.getElementById来获取DOM树的元素,这样消耗比较大,而且要写两行,而链式只要写一行,节省了代码……

但我们也可以用缓存元素啊。比如:

var ele = document.getElementById("ele");
ele.dosomething();
ele.dootherthing();

而且两行并没有比一行多多少代码,甚至相应的封装反而使得代码更多了。

最糟糕的是所有对象的方法返回的都是对象本身,也就是说没有返回值,这不一定在任何环境下都适合。

举个例子,我们想弄一个超大整数BigInteger(意思是如果用Javascript的Number保存可能会溢出的整数),顺便扩展他的运算方法,会适合用链式操作么?

 例如运算31415926535 * 4 - 271828182,如果设计成链式风格的方法可能会是这样的:

var result = (new BigInteger("31415926535")).multiply(new BigInteger("4")).subtract(new BigInteger("271828182")).val();
console.log("result == " + result);

这看起来似乎也很优雅,但是如果我们想要中间的结果怎么办呢?或许会写成这样:

var bigInteger = new BigInteger("31415926535");var result1 = bigInteger.multiply(new BigInteger("4")).val();var result2 = bigInteger.subtract(new BigInteger("271828182")).val();
console.log("result1 == " + result1 + ", result2 == " + result2);

这似乎一点也不优雅了,和不用链式操作没啥不同嘛!

那么如果要求是原来的BigInteger不能改变呢?好吧,链式操作似乎不能满足这个需求了。

jQuery专注于DOM对象操作,而DOM的操作会在页面上体现,不需要在Javascript中通过返回值来表示,但计算操作却不一样,我们很可能需要通过Javascript返回中间过程值另作他用。

在设计的时候,我们需要考虑链式带来的好处和坏处,因为别人用了链式,所以就用链式,可能并不是一个很好的方案。

那么到底为什么要用链式操作呢?


为了更好的异步体验

Javascript是无阻塞语言,所以他不是没阻塞,而是不能阻塞,所以他需要通过事件来驱动,异步来完成一些本需要阻塞进程的操作。

但是异步编程是一种令人疯狂的东西……运行时候是分离的倒不要紧,但是编写代码时候也是分离的就……

常见的异步编程模型有哪些呢?

· 回调函数 

所谓的回调函数,意指先在系统的某个地方对函数进行注册,让系统知道这个函数的存在,然后在以后,当某个事件发生时,再调用这个函数对事件进行响应。

function f(num, callback){  
    if(num<0)  {   
        alert("调用低层函数处理!");  
        alert("分数不能为负,输入错误!");   
    }else if(num==0){  
        alert("调用低层函数处理!");  
        alert("该学生可能未参加考试!");  
    }else{  
        alert("调用高层函数处理!");  
        setTimeout(function(){callback();}, 1000);  
    }  
}

    

这里callback则是回调函数。可以发现只有当num为非负数时候callback才会调用。

但是问题,如果我们不看函数内部,我们并不知道callback会几时调用,在什么情况下调用,代码间产生了一定耦合,流程上也会产生一定的混乱。

虽然回调函数是一种简单而易于部署的实现异步的方法,但从编程体验来说它却不够好。

· 事件监听

也就是采用事件驱动,执行顺序取决于事件顺序。 

function EventTarget(){
    this.handlers = {};
}
 
EventTarget.prototype = {
    constructor: EventTarget,
    addHandler: function(type, handler){
        this.handlers[type] = [];
    },
    fire: function(){
        if(!event.target){
            event.target = this;
        }
        if(this.handlers[event.type instanceof Array]){
            var handlers = this.handlers[event.type];
            for(var i = 0, len = handlers.length, i < len; i++){
                handlers[i](event);
            }
        }
    },
    removeHandler: function(type, handler){
        if(this.handlers[type] instanceof Array){
            var handlers = this.handlers[type];
            for(var i = 0, le = handlers.length; i < len; i++){
                if(handlers[i] === handler){
                    break;
                }
            }
 
            handlers.splice(i, 1);
        }
    }
};

上面是《JavaScript高级程序设计》中的自定义事件实现。于是我们就可以通过addHandler来绑定事件处理函数,用fire来触发事件,用removeHandler来删除事件处理函数。

虽然通过事件解耦了,但流程顺序更加混乱了。

· 链式异步 

个人觉得链式操作最值得称赞的还是其解决了异步编程模型的执行流程不清晰的问题。jQuery中$(document).ready就非常好的阐释了这一理念。DOMCotentLoaded是一个事件,在DOM并未加载前,jQuery的大部分操作都不会奏效,但jQuery的设计者并没有把他当成事件一样来处理,而是转成一种“选其对象,对其操作”的思路。$选择了document对象,ready是其方法进行操作。这样子流程问题就非常清晰了,在链条越后位置的方法就越后执行。

(function(){
       var isReady=false; //判断onDOMReady方法是否已经被执行过
       var readyList= [];//把需要执行的方法先暂存在这个数组里
       var timer;//定时器句柄
       ready=function(fn) {
              if (isReady )
                     fn.call( document);
              else
                     readyList.push( function() { return fn.call(this);});
              return this;
       }
       var onDOMReady=function(){
              for(var i=0;i<readyList.length;i++){
                     readyList[i].apply(document);
              }
              readyList = null;
       }
       var bindReady = function(evt){
              if(isReady) return;
              isReady=true;
              onDOMReady.call(window);
              if(document.removeEventListener){
                     document.removeEventListener("DOMContentLoaded", bindReady, false);
              }else if(document.attachEvent){
                     document.detachEvent("onreadystatechange", bindReady);
                     if(window == window.top){
                            clearInterval(timer);
                            timer = null;
                     }
              }
       };
       if(document.addEventListener){
              document.addEventListener("DOMContentLoaded", bindReady, false);
       }else if(document.attachEvent){
              document.attachEvent("onreadystatechange", function(){
                     if((/loaded|complete/).test(document.readyState))
                            bindReady();
              });
              if(window == window.top){
                     timer = setInterval(function(){
                            try{
                                   isReady||document.documentElement.doScroll('left');//在IE下用能否执行doScroll判断dom是否加载完毕
                            }catch(e){
                                   return;
                            }
                            bindReady();
                     },5);
              }
       }
})();

上面的代码不能用$(document).ready,而应该是window.ready。

· Deferred & Promise

CommonJS中的异步编程模型也延续了这一想法,

每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。 

所以我们可以这样写:

f1().then(f2).then(f3);

这种方法我们无需太过关注实现,也不太需要理解异步,只要懂得通过函数选对象,通过then进行操作,就能进行异步编程。 

 

本文由职坐标整理发布,欢迎关注职坐标WEB前端jQuery频道,获取更多jQuery知识!

本文由 @沉沙 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程