方法链式调用的实现

写的更少,做的更多。是JQuery的核心理念。

那么链式方法的设计与这个核心理念不谋而合。那么从深层次考虑这种设计其实就是一种Internal DSL。

DSL是指Domain Specific Language,也就是用于描述和解决特定领域问题的语言。

看一段链式代码:

$('input[type="button"]')
.eq(0).click(function() {
alert('点击我!');
}).end().eq(1)
.click(function() {
$('input[type="button"]:eq(0)').trigger('click');
}).end().eq(2)
.toggle(function() {
$('.aa').hide('slow');
}, function() {
$('.aa').show('slow');
});

看这个代码的结构:

1.找出type类型为button的input元素

2.找到第一个按钮,并绑定click事件处理函数

3.返回所有按钮,再找到第二个

4.为第二个按钮绑定click事件处理函数

5.为第三个按钮绑定toggle事件处理函数

让代码更贴近作者的思维模式;阅读代码时,让读者更容易理解代码的含义;应用DSL可以有效的提高系统的可维护性

(缩小了实现模型和领域模型的距离,提高了实现的可读性)和灵活性,并且提供开发的效率。

jQuery的这种管道风格的DSL链式代码,总的来说:

1. 节约JS代码;

2.所返回的都是同一个对象,可以提高代码的效率。

通过简单扩展原型方法并通过return this的形式来实现跨浏览器的链式调用。

利用JS下的简单工厂方法模式,来将所有对于同一个DOM对象的操作指定同一个实例。

这个原理就超简单了,如下代码:

aQuery().init().name()

分解:

a = aQuery();
a.init()
a.name()

把代码分解一下,很明显实现链式的基本条件就是要实例对象先创建好,调用自己的方法。

aQuery.prototype = {
init: function() {
return this;
},
name: function() {
return this
}
}

所以我们如果需要链式的处理,只需要在方法内部方法当前的这个实例对象this就可以了,因为返回当前实例的this,从而又可以访问自己的原型了,

这样的就节省代码量,提高代码的效率,代码看起来更优雅。

但是这种方法有一个问题是:所有对象的方法返回的都是对象本身,也就是说没有返回值,所以这种方法不一定在任何环境下都适合。

虽然Javascript是无阻塞语言,但是他并不是没阻塞,而是不能阻塞,

所以他需要通过事件来驱动,异步来完成一些本需要阻塞进程的操作,这样处理只是同步链式,

除了同步链式还有异步链式,异步链式jQuery从1.5开始就引入了Promise.

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://img.mukewang.com/down/540812440001e40e00000000.js" type="text/javascript"></script>
<title></title>
</head>
<body> 链式调用$$().setName('孙丽媛-Aaron').getName()的结果:
<div id="aaron"></div> <script type="text/javascript"> var $$ = ajQuery = function(selector) {
return new ajQuery.fn.init(selector);
} ajQuery.fn = ajQuery.prototype = {
name: 'aaron',
init: function(selector) {
this.selector = selector;
return this;
},
constructor: ajQuery
} ajQuery.fn.init.prototype = ajQuery.fn ajQuery.fn.setName = function(myName) {
this.myName = myName
return this;
} ajQuery.fn.getName = function() {
$("#aaron").html(this.myName)
return this;
} $$().setName('孙丽媛-Aaron').getName(); </script>
</body>
</html>

插件接口的设计

基于插件接口设计的好处也是颇多的,其中一个最重要的好处是把扩展的功能从主体框架中剥离出去,降低了框架的复杂度。

接口的设计好比电脑上的配件如:CPU、内存、硬盘都是作为独立的模块分离出去了,但是主板提供模块的接口,

例如支持串口的硬盘,我只要这个硬盘的接口能插上,甭管是500G还是1000G的容量的硬盘,都能使用。

所以在软件设计中插件接口的提供把独立的功能与框架以一种很宽松的方式松耦合。

jQuery插件的开发分为两种:

1.一种是挂在jQuery命名空间下的全局函数,也可称为静态方法;

2.另一种是jQuery对象级别的方法,即挂在jQuery原型下的方法,这样通过选择器获取的jQuery对象实例也能共享该方法。

提供的接口:

$.extend(target, [object1], [objectN])

接口的使用:

jQuery.extend({
data:function(){},
removeData:function(){}
}) jQuery.fn.extend({
data:function(){},
removeData:function(){}
})

jQuery的主体框架就是之前提到的那样,通过工厂模式返回一个内部的init构造器生成的对象。

但是根据一般设计者的习惯,如果要为jQuery添加静态方法或者实例方法从封装的角度讲是应该提供一个统一的接口才符合设计的。

jQuery支持自己扩展属性,这个对外提供了一个接口,jQuery.fn.extend()来对对象增加方法,

从jQuery的源码中可以看到,jQuery.extend和jQuery.fn.extend其实是同指向同一方法的不同引用。

这里有一个设计的重点,通过调用的上下文,我们来确定这个方法是作为静态还是实例处理,

在javascript的世界中一共有四种上下文调用方式:

1.方法调用模式、

2.函数调用模式、

3.构造器调用模式、

4.apply调用模式:

jQuery.extend调用的时候上下文指向的是jQuery构造器

jQuery.fn.extend调用的时候上下文指向的是jQuery构造器的实例对象了

通过extend()函数可以方便快速的扩展功能,不会破坏jQuery的原型结构,

jQuery.extend = jQuery.fn.extend = function(){...}; 这个是连等,也就是2个指向同一个函数,怎么会实现不同的功能呢?这就是this力量了!

fn与jQuery其实是2个不同的对象

jQuery.extend 调用的时候,this是指向jQuery对象(jQuery是函数,也是对象!),所以这里扩展在jQuery上。

而jQuery.fn.extend 调用的时候,this指向fn对象,jQuery.fn 和jQuery.prototype指向同一对象,扩展fn就是扩展jQuery.prototype原型对象。

这里增加的是原型方法,也就是对象方法了。所以jQuery的API中提供了以上2个扩展函数

jQuery的extend代码实现比较长,我们简单说一下重点:

aAron.extend = aAron.fn.extend = function() {
var options, src, copy,
target = arguments[0] || {},
i = 1,
length = arguments.length; //只有一个参数,就是对jQuery自身的扩展处理
//extend,fn.extend
if (i === length) {
target = this; //调用的上下文对象jQuery/或者实例
i--;
}
for (; i < length; i++) {
//从i开始取参数,不为空开始遍历
if ((options = arguments[i]) != null) {
for (name in options) {
copy = options[name];
//覆盖拷贝
target[name] = copy;
}
}
}
return target;
}

因为extend的核心功能就是通过扩展收集功能(类似于mix混入),所以就会存在收集对象(target)与被收集的数据,

因为jQuery.extend并没有明确实参,而且是通过arguments来判断的,所以这样处理起来很灵活。

arguments通过判断传递参数的数量可以实现函数重载。

其中最重要的一段target = this,通过调用的方式我们就能确实当前的this的指向,所以这时候就能确定target了。

最后就很简单了,通过for循环遍历把数据附加到这个target上了。

当然在这个附加的过程中我们还可以做数据过滤、深拷贝等一系列的操作了。

<div id="aaron"></div>

<script type="text/javascript">

var $$ = ajQuery = function(selector) {
return new ajQuery.fn.init(selector);
} ajQuery.fn = ajQuery.prototype = {
name: 'aaron',
init: function(selector) {
this.selector = selector;
return this;
},
constructor: ajQuery
} ajQuery.fn.init.prototype = ajQuery.fn ajQuery.extend = ajQuery.fn.extend = function() {
var options, src, copy,
target = arguments[0] || {},
i = 1,
length = arguments.length; //只有一个参数,就是对jQuery自身的扩展处理
//extend,fn.extend
if (i === length) {
target = this; //调用的上下文对象jQuery/或者实例
i--;
}
for (; i < length; i++) {
//从i开始取参数,不为空开始遍历
if ((options = arguments[i]) != null) {
for (name in options) {
copy = options[name];
//覆盖拷贝
target[name] = copy;
}
}
}
return target;
} ajQuery.fn.extend({
setName: function(myName) {
this.myName = myName
return this;
},
getName: function() {
$("#aaron").html(this.myName)
return this;
}
}) $$().setName('孙丽媛-Aaron').getName();

JQuery源码分析(六)的更多相关文章

  1. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  2. [转] jQuery源码分析-如何做jQuery源码分析

    jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...

  3. jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)

    在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...

  4. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  5. jquery源码分析之一前言篇

    1.问:jquery源码分析的版本是什么? 答:v3.2.1 2.问:为什么要分析jquery源码? 答:javascript是一切js框架的基础,jquery.es6.vue.angular.rea ...

  6. jQuery源码分析-each函数

    本文部分截取自且行且思 jQuery.each方法用于遍历一个数组或对象,并对当前遍历的元素进行处理,在jQuery使用的频率非常大,下面就这个函数做了详细讲解: 复制代码代码 /*! * jQuer ...

  7. jQuery源码分析系列(转载来源Aaron.)

    声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...

  8. jQuery源码分析系列——来自Aaron

    jQuery源码分析系列——来自Aaron 转载地址:http://www.cnblogs.com/aaronjs/p/3279314.html 版本截止到2013.8.24 jQuery官方发布最新 ...

  9. jQuery 源码分析(十六) 事件系统模块 底层方法 详解

    jQuery事件系统并没有将事件监听函数直接绑定到DOM元素上,而是基于数据缓存模块来管理监听函数的,事件模块代码有点多,我把它分为了三个部分:分底层方法.实例方法和便捷方法.ready事件来讲,好理 ...

  10. js菜鸟进阶-jQuery源码分析(1)-基本架构

    导读: 本人JS菜鸟一枚,为加强代码美观和编程思想.所以来研究下jQuery,有需要进阶JS的同学很适合阅读此文!我是边看代码(jquery2.2.1),边翻“javascript高级程序设计”写的, ...

随机推荐

  1. 网络与RPC

    网络与RPC 标签 : Java基础 Java为网络编程提供的java.net包封装了底层通信细节, 包含了大量的基础组件以及TCP/UDP协议的编程接口, 使得开发者可以专注于解决问题, 而不用关注 ...

  2. BluetoothAdapter.LeScanCallback 参考文档

      BluetoothAdapter.LeScanCallback 参考文档   [翻译自: android开发文档] Class Overview:回调接口被用于传输LE扫描后的结果; 详情参看: ...

  3. SpringMVC整合Quartz实现定时任务以及Tomcat服务执行初始化定时任务

    1.导入quartz.jar包,或者pom.xml 配置对应的依赖: <dependency> <groupId>org.quartz-scheduler</groupI ...

  4. 百度地图API:利用瓦片生成工具,自定义背景图片

    参考酸奶小妹的博文<[百度地图API]如何制作一张魔兽地图!!——CS地图也可以,哈哈哈> (http://www.cnblogs.com/milkmap/archive/2011/05/ ...

  5. oracle第一章

    1.oracle对比sqlserver oracle sqlserver 数据文件.dbf 数据文件.mdf 控制文件.ctl   日志文件.log 日志文件.log     2.内置用户 1.sys ...

  6. Notepad++ 配置java编译环境

    仅限于学习java或小的java程序使用.正常写代码还是eclipse吧 ---------------------分割线----------------------------- 1.配置JDK环境 ...

  7. LInux软件的卸载和安装(转)

    在linux环境中,尤其是cenos中安装过一些软件,一般是二进制安装与源码安装,现小结一下linux中的安装与卸载. 一.通常Linux应用软件的安装包有三种: 1) tar包,如software- ...

  8. js中文正则

    正则表达式用于字符串处理.表单验证等场合,实用高效.现将一些常用的表达式收集于此,以备不时之需. 匹配中文字符的正则表达式: [\u4e00-\u9fa5]评注:匹配中文还真是个头疼的事,有了这个表达 ...

  9. java之socket

    socket系java中网络编程的内容. 1客户端: package com.wtd.socket; import java.io.BufferedReader; import java.io.IOE ...

  10. 怎么学好python?

    文章摘自:http://www.jb51.net/article/16100.htm 1)学好python的第一步,就是马上到www.python.org网站上下载一个python版本.我建议初学者, ...