学习JavaScript设计模式(链式调用)


Posted in Javascript onNovember 26, 2015

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){// IE
        this.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 callback
window.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 callback
window.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 callback
window.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);

链式调用这种风格有助于简化代码的编写工作,让代码更加简洁、易读,同时也避免多次重复使用一个对象变量,希望大家可以熟练掌握。

Javascript 相关文章推荐
两种方法实现文本框输入内容提示消失
Mar 17 Javascript
用JQuery 判断某个属性是否存在hasAttr的解决方法
Apr 26 Javascript
JavaScript中的this关键字介绍与使用实例
Jun 21 Javascript
js对列表中第一个值处理与jsp页面对列表中第一个值处理的区别详解
Nov 05 Javascript
解决Jquery鼠标经过不停滑动的问题
Mar 03 Javascript
基于JavaScript实现div层跟随滚动条滑动
Jan 12 Javascript
在AngularJS中使用jQuery的zTree插件的方法
Apr 21 Javascript
JavaScript使用delete删除数组元素用法示例【数组长度不变】
Jan 17 Javascript
详细分析JS函数去抖和节流
Dec 05 Javascript
js断点调试心得分享(必看篇)
Dec 08 Javascript
深入理解ES6中let和闭包
Feb 22 Javascript
如何在Angular应用中创建包含组件方法示例
Mar 23 Javascript
学习JavaScript设计模式(继承)
Nov 26 #Javascript
js图片跟随鼠标移动代码
Nov 26 #Javascript
学习JavaScript设计模式(封装)
Nov 26 #Javascript
JS实现密码框根据焦点的获取与失去控制文字的消失与显示效果
Nov 26 #Javascript
学习JavaScript设计模式(接口)
Nov 26 #Javascript
Jquery中request和request.form和request.querystring的区别
Nov 26 #Javascript
Jquery检验手机号是否符合规则并根据手机号检测结果将提交按钮设为不同状态
Nov 26 #Javascript
You might like
php+ajax无刷新分页实例详解
2015/12/07 PHP
PHP/HTML混写的四种方式总结
2017/02/27 PHP
PHP给前端返回一个JSON对象的实例讲解
2018/05/31 PHP
laravel 解决ajax异步提交数据,并还回填充表格的问题
2019/10/15 PHP
PHP7 新增常量
2021/03/09 PHP
动态加载图片路径 保持JavaScript控件的相对独立性
2010/09/03 Javascript
javascript实现动态改变层大小的方法
2015/05/14 Javascript
jQuery实现点击后标记当前菜单位置(背景高亮菜单)效果
2015/08/22 Javascript
AngularJS使用指令增强标准表单元素功能
2016/07/01 Javascript
js实现千分符和保留几位小数的简单实例
2016/08/01 Javascript
AngularJS中isolate scope的用法分析
2016/11/22 Javascript
jquery实现下拉框多选方法介绍
2017/01/03 Javascript
Vue路由切换页面不更新问题解决方案
2020/07/10 Javascript
[05:02]2014DOTA2 TI中国区预选赛精彩TOPPLAY第三弹
2014/06/25 DOTA
Python和Ruby中each循环引用变量问题(一个隐秘BUG?)
2014/06/04 Python
Python使用剪切板的方法
2017/06/06 Python
django模型层(model)进行建表、查询与删除的基础教程
2017/11/21 Python
通过celery异步处理一个查询任务的完整代码
2019/11/19 Python
使用wxpy实现自动发送微信消息功能
2020/02/28 Python
Python并发concurrent.futures和asyncio实例
2020/05/04 Python
python PIL模块的基本使用
2020/09/29 Python
澳大利亚领先的运动鞋商店:Hype DC
2018/03/31 全球购物
美国家居装饰网上商店:Lulu & Georgia
2019/09/14 全球购物
工程现场管理求职自荐信
2013/10/02 职场文书
夜大毕业生自我评价分享
2013/11/10 职场文书
应届毕业生求职信范文分享
2013/12/26 职场文书
考核工作实施方案
2014/03/30 职场文书
计生工作先进事迹
2014/08/15 职场文书
超市开店计划书
2014/09/15 职场文书
重阳节演讲稿:尊敬帮助老人 弘扬传统美德
2014/09/25 职场文书
法学专业毕业实习自我鉴定2014
2014/09/27 职场文书
民事诉讼代理授权委托书范本
2014/10/08 职场文书
2015年体育部工作总结
2015/04/02 职场文书
冬季作息时间调整通知
2015/04/24 职场文书
电力安全学习心得体会
2016/01/18 职场文书
pd.DataFrame中的几种索引变换的实现
2022/06/16 Python