学习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 相关文章推荐
JavaScript的Function详细
Nov 14 Javascript
在textarea中显示html页面的javascript代码
Apr 20 Javascript
jquery对ajax的支持介绍
Dec 10 Javascript
jQuery实现点击图片翻页展示效果的方法
Feb 16 Javascript
Javascript URI 解析介绍
Mar 15 Javascript
Kindeditor在线文本编辑器如何过滤HTML
Apr 14 Javascript
图片加载完成再执行事件的实例
Nov 16 Javascript
vue登录路由验证的实现
Dec 13 Javascript
vue中使用gojs/jointjs的示例代码
Aug 24 Javascript
使用Three.js实现太阳系八大行星的自转公转示例代码
Apr 09 Javascript
详解vue项目中调用百度地图API使用方法
Apr 25 Javascript
JavaScript如何判断input数据类型
Feb 06 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
LotusPhp笔记之:Logger组件的使用方法
2013/05/06 PHP
php发送与接收流文件的方法
2015/02/11 PHP
PHP获取远程图片并保存到本地的方法
2015/05/12 PHP
微信公众号模板消息群发php代码示例
2016/12/29 PHP
TP3.2框架分页相关实现方法分析
2020/06/03 PHP
javascript OFFICE控件测试代码
2009/12/08 Javascript
js 多浏览器分别判断代码
2010/04/01 Javascript
jquery 图片 上一张 下一张 链接效果(续篇)
2010/04/20 Javascript
javascript温习的一些笔记 基础常用知识小结
2011/06/22 Javascript
jQuery 计算iframe 窗口大小的方法
2014/05/13 Javascript
22点关于jquery性能优化的建议
2014/05/28 Javascript
JS动态显示表格上下frame的方法
2015/03/31 Javascript
javascript变量声明实例分析
2015/04/25 Javascript
javascript匀速动画和缓冲动画详解
2016/10/20 Javascript
JavaScript构建自己的对象示例
2016/11/29 Javascript
微信小程序 两种滑动方式(横向滑动,竖向滑动)详细及实例代码
2017/01/13 Javascript
利用JavaScript在网页实现八数码启发式A*算法动画效果
2017/04/16 Javascript
Vue单文件组件基础模板小结
2017/08/10 Javascript
详解vue-cli构建项目反向代理配置
2017/09/07 Javascript
分享一款超好用的JavaScript 打包压缩工具
2020/04/26 Javascript
python实现的登录和操作开心网脚本分享
2014/07/09 Python
跟老齐学Python之总结参数的传递
2014/10/10 Python
介绍Python的Django框架中的静态资源管理器django-pipeline
2015/04/25 Python
python使用wmi模块获取windows下的系统信息 监控系统
2015/10/27 Python
Python实现PS图像调整黑白效果示例
2018/01/25 Python
Django框架实现逆向解析url的方法
2018/07/04 Python
15行Python代码实现网易云热门歌单实例教程
2019/03/10 Python
python中的数组赋值与拷贝的区别详解
2019/11/26 Python
python函数调用,循环,列表复制实例
2020/05/03 Python
keras 指定程序在某块卡上训练实例
2020/06/22 Python
Python中猜拳游戏与猜筛子游戏的实现方法
2020/09/04 Python
CSS3样式linear-gradient的使用实例
2017/01/16 HTML / CSS
HTML5 解决苹果手机不能自动播放音乐问题
2017/12/27 HTML / CSS
非洲NO.1网上商店:Jumia肯尼亚
2016/08/18 全球购物
竞选纪律委员演讲稿
2014/09/13 职场文书
2015年初中元旦晚会活动总结
2014/11/28 职场文书