学习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 相关文章推荐
IE 缓存策略的BUG的解决方法
Jul 21 Javascript
用js实现判断当前网址的来路如果不是指定的来路就跳转到指定页面
May 02 Javascript
学习JavaScript编程语言的8张思维导图分享
Mar 27 Javascript
JavaScript仿静态分页实现方法
Aug 04 Javascript
Node.js项目中调用JavaScript的EJS模板库的方法
Mar 11 Javascript
详解原生JavaScript实现jQuery中AJAX处理的方法
May 10 Javascript
AngularJS验证信息框架的封装插件用法【w5cValidator扩展插件】
Nov 03 Javascript
JS 实现可停顿的垂直滚动实例代码
Nov 23 Javascript
jQuery实现文字超过1行、2行或规定的行数时自动加省略号的方法
Mar 28 jQuery
vue2路由基本用法实例分析
Mar 06 Javascript
JavaScript中CreateTextFile函数
Aug 30 Javascript
Vue 组件注册全解析
Dec 17 Vue.js
学习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
我的论坛源代码(四)
2006/10/09 PHP
PHP中比较时间大小实例
2014/08/21 PHP
Laravle eloquent 多对多模型关联实例详解
2017/11/22 PHP
类似框架的js代码
2006/11/09 Javascript
jQuery 页面载入进度条实现代码
2009/02/08 Javascript
Extjs学习笔记之一 初识Extjs之MessageBox
2010/01/07 Javascript
基于jQuery的弹出警告对话框美化插件(警告,确认和提示)
2010/06/10 Javascript
使用JavaScript 实现对象 匀速/变速运动的方法
2013/05/08 Javascript
jQuery 获取、设置HTML或TEXT内容的两种方法
2014/05/23 Javascript
教你使用javascript简单写一个页面模板引擎
2015/05/05 Javascript
require.js的用法详解
2015/10/20 Javascript
AngularJS 中的指令实践开发指南(一)
2016/03/20 Javascript
第一次接触Bootstrap框架
2016/10/24 Javascript
nodejs multer实现文件上传与下载
2017/05/10 NodeJs
Vue AST源码解析第一篇
2017/07/19 Javascript
简述jQuery Easyui一些用法
2017/08/01 jQuery
微信小程序图片轮播组件gallery slider使用方法详解
2018/01/31 Javascript
JS中的算法与数据结构之链表(Linked-list)实例详解
2019/08/20 Javascript
js实现窗口全屏示例详解
2019/09/17 Javascript
node静态服务器实现静态读取文件或文件夹
2019/12/03 Javascript
es6函数之尾递归用法实例分析
2020/04/25 Javascript
Vue 实现创建全局组件,并且使用Vue.use() 载入方式
2020/08/11 Javascript
python将ansible配置转为json格式实例代码
2017/05/15 Python
Python中实现变量赋值传递时的引用和拷贝方法
2018/04/29 Python
Django+boostrap 美化admin后台的操作
2020/03/11 Python
python Django 反向访问器的外键冲突解决
2020/05/20 Python
Django 解决distinct无法去除重复数据的问题
2020/05/20 Python
AmazeUI 手机版页面的顶部导航条Header与侧边导航栏offCanvas的示例代码
2020/08/19 HTML / CSS
电子技术专业中专生的自我评价
2013/12/17 职场文书
国际会议邀请函范文
2014/01/16 职场文书
入党自荐书范文
2014/03/09 职场文书
医院信息公开实施方案
2014/05/09 职场文书
2014年学习全国道德模范事迹思想汇报
2014/09/15 职场文书
初三学生语文考试作弊检讨书
2014/12/14 职场文书
CSS3 制作精美的定价表
2021/04/06 HTML / CSS
Ruby使用Mysql2连接操作MySQL
2022/04/19 Ruby