1、什么是链式调用
这个很容易理解,例如:
$(this).setStyle("color", "red").show();
一般的函数调用和链式调用的区别:调用完方法后,return this返回当前调用方法的对象。
function Dog(){this.run= function(){alert("The dog is running....");return this;//返回当前对象 Dog};this.eat= function(){alert("After running the dog is eatting....");return this;//返回当前对象 Dog};this.sleep= function(){alert("After eatting the dog is running....");return this;//返回当前对象 Dog};}//一般的调用方式;/* var dog1 =new Dog();dog1.run();dog1.eat();dog1.sleep();*/var dog2 = new Dog();dog2.run().eat().sleep();2、分解链式调用链式调用其实是两个部分:
1).操作对象(也就是被操作的DOM元素,如上例的$(this))
2).操作方法(具体要做什么事情,如上例的setStyle和show)
如何实现操作对象与操作方法
创建一般的$函数:
function $(){var elements = [];for(var i= 0,len=arguments.length; i<len; i++){var element = arguments[i];if(typeof element==="string"){element = document.getElementById(element);}if(arguments.length==1){return element;}elements.push(element);}return elements;}但是,如果把这个函数改造为一个构造器,把那些元素作为数组保存在一个实例属性中,并让所有定义在构造器函数的prototype属性所指对象中的方法都返回用以调用方法的那个实例的引用,那么它就具有了链式调用的能力。(说了这么多,就是在每个方法最后return this;),
我首先需要把这个$函数改为一个工厂方法,它负责创建支持链式调用的对象。这个函数应该能接受元素数组形式的参数,以便我们能够使用与原来一样的公用接口。这样以来,它就具有了进行链式调用的能力。
改造后如下:
(function(){function _$(els){this.elements = [];//把那些元素作为数组保存在一个实例属性中,for(var i= 0, len=els.length; i<len; i++){var element = els[i];if(typeof element==="string"){element = document.getElementById(element);}this.elements.push(element);}}_$.prototype = {each: function(fn){for(var i= 0,len=this.elements.length; i<len; i++){fn.call(this, this.elements[i]);}return this; //在每个方法的最后return this;},setStyle: function(prop, val){this.each(function(el){el.style[prop] = val;});return this; //在每个方法的最后return this;},show: function(){var that = this;this.each(function(el){that.setStyle("display", "block");});return this; //在每个方法的最后return this;},addEvent: function(type, fn){var add = function(el){if(window.addEventListener){el.addEventListener(type, fn, false);}else if(window.attachEvent){el.addEvent("on"+type, fn);}};this.each(function(el){add(el);});return this; //在每个方法的最后return this;}}window.$ = function(){return new _$(arguments);}})();在最后return this,这就将调用方法的对象传给调用链上的下一个方法。
3、模拟jquery底层链式编程// 块级作用域//特点1 程序启动的时候 里面的代码直接执行了//特点2 内部的成员变量 外部无法去访问 (除了不加var修饰的变量)(function(window , undefined){// $ 最常用的对象 返回给外界 大型程序开发 一般使用"_"作为私用的对象(规范)function _$(arguments){//实现代码...这里仅实现ID选择器// 正则表达式匹配id选择器var idselector = /#w+/ ;this.dom ; // 此属性 接受所得到的元素// 如果匹配成功 则接受dom元素arguments[0] = "#inp"if(idselector.test(arguments[0])){this.dom = document.getElementById(arguments[0].substring(1));} else {throw new Error(" arguments is error !");}};// 在Function类上扩展一个可以实现链式编程的方法Function.prototype.method = function(methodName , fn){this.prototype[methodName] = fn ;return this ; //链式编程的关键}// 在_$的原型对象上 加一些公共的方法_$.prototype = {constructor : _$ ,addEvent:function(type,fn){// 给你的得到的元素 注册事件if(window.addEventListener){// FF this.dom.addEventListener(type , fn);} else if (window.attachEvent){// IEthis.dom.attachEvent("on"+type , fn);}return this ; },setStyle:function(prop , val){this.dom.style[prop] = val ;return this ;}}; // window 上先注册一个全局变量 与外界产生关系window.$ = _$ ;// 写一个准备的方法_$.onReady = function(fn){ // 1 实例化出来_$对象 真正的注册到window上window.$ = function(){return new _$(arguments);};// 2 执行传入进来的代码fn();// 3 实现链式编程_$.method("addEvent",function(){// nothing to do}).method("setStyle",function(){// nothing to do});};})(window); // 程序的入口 window传入作用域中$.onReady(function(){var inp = $("#inp");//alert(inp.dom.nodeName);//alert($("#inp"));inp.addEvent("click",function(){alert("我被点击了!");}).setStyle("backgroundColor" , "red");});4、使用回调函数从支持链式调用的方法获取数据链式调用很适合于赋值器方法,但对于取值器方法,就不方便了,因为每个方法返回的都是this啊。
不过,变通的方法还是有的,那就是回调函数。
未使用回调函数时
//without callbackwindow.API = window.API || function(){var name = "JChen";this.setName = function(newName){name = newName;return this;};this.getName = function(){return name;};};var o = new API();console.log(o.getName());console.log(o.setName("Haha").getName());使用回调函数时
//with callbackwindow.API2 = window.API2 || function(){var name = "JChen";this.setName = function(newName){name = newName;return this;};this.getName = function(callback){callback.call(this, name);return this;};};var o2 = new API2();o2.getName(console.log).setName("Hehe").getName(console.log);在使用回调函数时候callback.call(this, name)在一般情况下是没问题的,但是,这个例子偏偏用到了console.log,那么就有问题了。原因是console的this是指向console而不是winodw。
这个问题也很好解决。如下:
//with callbackwindow.API2 = window.API2 || function(){var name = "JChen";this.setName = function(newName){name = newName;return this;};this.getName = function(callback){callback.call(this, name);return this;};};var o2 = new API2();var log = function(para){console.log(para);};o2.getName(log).setName("Hehe").getName(log);链式调用这种风格有助于简化代码的编写工作,让代码更加简洁、易读,同时也避免多次重复使用一个对象变量,希望大家可以熟练掌握。