jQuery内核详解与实践读书笔记1:原型技术分解2

上一篇已经搭建了一个非常简陋的jQuery框架雏形,如没有阅读搭建过程,请先阅读《jQuery内核详解与实践读书笔记1:原型技术分解1》初始搭建过程。接下来,完成书中介绍的剩下三个步骤:

7. 延续--功能扩展

jQuery框架是通过extend()函数来扩展功能的,extend()函数的功能实现起来也很简单,它只是吧指定对象的方法复制给jQuery对象或jQuery.prototype对象,如下示例代码就为jQuery类和原型定义了一个扩展功能的函数extend()。

 1 var $ = jQuery = function(selector, context) {       //定义类
 2   return new jQuery.fn.init(selector, context);    //返回选择器的实例
 3 };
 4 jQuery.fn = jQuery.prototype = {                  //jQuery类的原型对象
 5     init : function(selector, context) {      //定义选择器构造器
 6        selector = selector || document;      //设置默认值为document
 7        context = context || document;        //设置默认值为document
 8        if(selector.nodeType) {               //如果选择符为节点对象
 9          this[0] = selector;               //把参数节点传递给实例对象的数组
10          this.length = 1;                  //并设置实例对象的length属性,定义包含的元素个数
11          this.context = selector;          //设置实例的属性,返回选择范围
12          return this;                      //返回当前实例
13        }
14        if(typeof selector === "string") {                    //如果选择符是字符串
15          var e = context.getElementsByTagName(selector);   //获取指定名称的元素
16          for(var i=0; i<e.length; i++) {                   //遍历元素集合,并把所有元素填入到当前实例数组中
17            this[i] = e[i];
18          }
19          this.length = e.length;                          //设置实例的length属性,即定义包含的元素个数
20          this.context = context;                          //设置实例的属性,返回选择范围
21          return this;                                     //返回当前实例
22        } else {
23          this.length = 0;                  //否则,设置实例的length属性值为0
24          this.context = context;           //设置实例的属性,返回选择范围
25          return this;                      //返回当前实例
26        }
27     },
28     jquery : "1.3.2",         //原型属性
29     size : function() {       //原型方法
30        return this.length;
31     }
32 };
33 jQuery.fn.init.prototype = jQuery.fn; //使用jQuery的原型对象覆盖init的原型对象
34 //jQuery功能扩展函数
35 jQuery.extend = jQuery.fn.extend = function(obj) {
36   for(var prop in obj) {
37     this[prop] = obj[prop];
38   }
39   return this;
40 };
41 //扩展jQuery对象方法
42 jQuery.fn.extend({
43   test : function() {
44     alert("测试扩展功能");
45   }
46 });

在上面的代码中,先定义了一个功能扩展函数extend(),然后为jQuery.fn原型对象调用extend()函数,为其添加一个测试方法test()。这样就可以在实践中应用,如$("div").test()。

jQuery框架定义的extend()函数的功能要强大很多,它不仅能够完成基本的功能扩展,还可以实现对象合并等功能,代码和解释如下所示:

 1 var $ = jQuery = function(selector, context) {       //定义类
 2   return new jQuery.fn.init(selector, context);    //返回选择器的实例
 3 };
 4 jQuery.fn = jQuery.prototype = {                  //jQuery类的原型对象
 5     init : function(selector, context) {      //定义选择器构造器
 6        selector = selector || document;      //设置默认值为document
 7        context = context || document;        //设置默认值为document
 8        if(selector.nodeType) {               //如果选择符为节点对象
 9          this[0] = selector;               //把参数节点传递给实例对象的数组
10          this.length = 1;                  //并设置实例对象的length属性,定义包含的元素个数
11          this.context = selector;          //设置实例的属性,返回选择范围
12          return this;                      //返回当前实例
13        }
14        if(typeof selector === "string") {                    //如果选择符是字符串
15          var e = context.getElementsByTagName(selector);   //获取指定名称的元素
16          for(var i=0; i<e.length; i++) {                   //遍历元素集合,并把所有元素填入到当前实例数组中
17            this[i] = e[i];
18          }
19          this.length = e.length;                          //设置实例的length属性,即定义包含的元素个数
20          this.context = context;                          //设置实例的属性,返回选择范围
21          return this;                                     //返回当前实例
22        } else {
23          this.length = 0;                  //否则,设置实例的length属性值为0
24          this.context = context;           //设置实例的属性,返回选择范围
25          return this;                      //返回当前实例
26        }
27     },
28     jquery : "1.3.2",         //原型属性
29     size : function() {       //原型方法
30        return this.length;
31     }
32 };
33 jQuery.fn.init.prototype = jQuery.fn; //使用jQuery的原型对象覆盖init的原型对象
34 //jQuery功能扩展函数
35 jQuery.extend = jQuery.fn.extend = function(obj) {
36   //定义复制操作的目标对象
37   var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
38   //获取是否深度复制处理
39   if( typeof target === "boolean") {
40     deep = target;
41     target = arguments[i] || {};
42     //跳出布尔值和目标对象
43     i = 2;
44   }
45   //如果第一个参数是字符串,则设置为空对象
46   if(typeof target !== "object" && !jQuery.isFunction(target)) {
47     target = {};
48   }
49   //如果只有一个参数,表示把参数对象的方法复制给当前对象,则设置target为this
50   if(length == i) {
51     target = this;
52     --i;
53   }
54   for( ; i<length; i++) {
55     //若参数值不为null,则进行处理
56     if((options = arguments[i]) != null) {
57        //扩展基类对象
58        for( var name in options) { //遍历参数对象
59          var src = target[name], copy = options[name];
60          //防止死循环
61          if(target === copy) {
62            continue;
63          }
64          //递归运算
65          if(deep && copy && typeof copy === "object" && !copy.nodeType) {
66            target[name] = jQuery.extend(deep,
67                //不要复制原对象
68                src || (copy.length != null ? [] : {}), copy);
69          } else if(copy != undefined) { //不要传递未定义的值
70            target[name] = copy;
71          }
72        }
73     }
74   }
75   //返回修改后的对象
76   return target;
77 };

以上代码就是jQuery框架提供的功能扩展代码。

8. 延续--参数处理

jQuery的方法都要求传递的参数为对象结构,这是因为jQuery框架的很多方法都包含大量的参数,且都是可选的,位置也没有固定的要求,所以使用对象直接量是唯一的解决方法。
使用对象直接量作为参数传递的载体,如何解析并提出参数?如何处理参数的默认值?可以通过下面的方式:

 1 var $ = jQuery = function(selector, context) {       //定义类
 2   return new jQuery.fn.init(selector, context);    //返回选择器的实例
 3 };
 4 jQuery.fn = jQuery.prototype = {                  //jQuery类的原型对象
 5     init : function(selector, context) {      //定义选择器构造器
 6        selector = selector || document;      //设置默认值为document
 7        context = context || document;        //设置默认值为document
 8        if(selector.nodeType) {               //如果选择符为节点对象
 9          this[0] = selector;               //把参数节点传递给实例对象的数组
10          this.length = 1;                  //并设置实例对象的length属性,定义包含的元素个数
11          this.context = selector;          //设置实例的属性,返回选择范围
12          return this;                      //返回当前实例
13        }
14        if(typeof selector === "string") {                    //如果选择符是字符串
15          var e = context.getElementsByTagName(selector);   //获取指定名称的元素
16          for(var i=0; i<e.length; i++) {                   //遍历元素集合,并把所有元素填入到当前实例数组中
17            this[i] = e[i];
18          }
19          this.length = e.length;                          //设置实例的length属性,即定义包含的元素个数
20          this.context = context;                          //设置实例的属性,返回选择范围
21          return this;                                     //返回当前实例
22        } else {
23          this.length = 0;                  //否则,设置实例的length属性值为0
24          this.context = context;           //设置实例的属性,返回选择范围
25          return this;                      //返回当前实例
26        }
27     },
28     setOptions : function(options) {
29        this.options = {                 //方法的默认值,可以扩展
30            StartColor : "#000",
31            EndColor : "#DDC",
32            Background : false,
33            Step : 20,
34            Speed : 10
35        };
36        jQuery.extend(this.options, options || {}); //如果传递参数,则覆盖原默认参数
37     }
38 };
39 jQuery.fn.init.prototype = jQuery.fn; //使用jQuery的原型对象覆盖init的原型对象
40 //jQuery功能扩展函数
41 jQuery.extend = jQuery.fn.extend = function(destination, source) {//重新定义extend()函数
42   for(var property in source) {
43     destination[property] = source[proprty];
44   }
45   return destination;
46 };

在上面的示例中,定义了一个原型方法setOptions(),该方法能够对传递的参数对象进行处理,并覆盖默认值。
在jQuery框架中,extend()函数包含了所有功能,它既能为当前对象扩展方法,也能处理参数对象,并覆盖默认值。

9. 涅槃--名字空间

在介绍该内容前,先介绍一个JavaScript函数中的一个核心概念:闭包,其实就是匿名函数。闭包的一个很重要的作用就是将闭包内部的代码封装在一个封闭的空间中,不将内部的信息暴露出来,别的代码也不能随意访问闭包内的函数或变量等。只需要提供外接可以访问的接口,就可以方便的与外接进行联系。

我们看jQuery源代码时就会发现,它几千行的代码就是封装在一个闭包之中的,这样就解决了框架内部的变量名与其它框架或JavaScript代码重名冲突问题。另外jQuery框架的$和jQuery名字也很有可能发生名字冲突,这个问题jQuery框架是如何解决的呢?

首先,jQuery的所有代码全部封装在一个闭包(即匿名函数)中
其次,jQuery提供了一个noConfilit()函数,该函数实现禁止jQuery框架使用这两个名字。

那它又是如何做的呢?

jQuery在框架的最前面,先使用_$和_jQuery临时变量寄存$和jQuery这两个变量的内容,当需要禁用jQuery框架的名字时,可以使用一个临时变量_$和_jQuery恢复$和jQuery这两个变量的实际内容。代码如下:

 1 (function(){
 2   var window = this,
 3       undefined,
 4       _jQuery = window.jQuery, //缓存jQuery变量内容
 5       _$ = window.$,
 6       jQuery = window.jQuery = window.$ = function(selector, context) {
 7        return new jQuery.fn.init(selector, context);
 8       },
 9       quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
10       isSimple = /^.[^:#\[\.,]*$/;
11    jQuery.fn = jQuery.prototype = {
12         init : function(selector, context) {}
13    };
14 })(); 

至此,jQuery框架的简单雏形就已经搭好了,虽然jQuery框架的功能是极其强大的,但是它也是在这个架构上建立起来的。后面的工作就是根据应用需要或者功能需要,使用extend()函数不断扩展jQuery框架的工具函数和jQuery对象的方法。

今天就先写到这里了,下次就要开始研究jQuery的选择器接口了,欢迎转载,转载时请注明出处。

个人微信公众号:programmlife,如有兴趣敬请关注,主要内容是一个码农的所看所思所想所叹,或扫描下方二维码关注:

时间: 2024-10-19 21:58:19

jQuery内核详解与实践读书笔记1:原型技术分解2的相关文章

jQuery内核详解与实践读书笔记1:原型技术分解1

一直以来都有研究一下jQuery源代码的想法,但是每次看到jQuery几千行的代码,头就大了,没有一点头绪,也不知道从哪里开始.昨天去图书馆无意间发现了这本<jQuery内核详解和实践>,翻看了一下里面的内容,这正是我寻觅多时剖析jQuery源码的好书. 废话不多说,直入正题吧.第一章介绍了一下jQuery的起步和一些历史故事,没什么重要内容.这里直接进入第二章,jQuery技术解密,从这一章开始就全部是干货了.这一章主要分四部分:jQuery原型技术分解,破解jQuery选择器接口,解析jQ

jQuery内核详解与实践读书笔记2:破解jQuery选择器接口1

前两篇已经介绍了如何搭建一个jQuery框架的雏形,从这一篇开始详细了解jQuery选择器的接口.jQuery选择器功能强大但用法很简单,它仅仅提供了一个接口:jQuery(),也可以简写为$(). 1. 简单但很复杂的黑洞 jQuery提供了唯一的接口(jQuery()或$())使选择器与外界进行交流.jQuery框架的基础是查询,即查询文档元素对象,因此可以认为jQuery对象就是一个选择器,并在此基础上构建和运行查询过滤器.jQuery查询结果的数据集合是jQuery对象的一部分.在$()

《TCP/IP详解 卷一》读书笔记-----第三章 IP

1.Network byte order:数据在网络中的传输是按照大端模式来的,即如果需要传递一个四个字节的int变量,先传递最高的字节,然后依次类推.因此无论主机存储数据用的是大端模式还是小端模式,在传输之前都必须将其转换为大端. 2.IP数据报报头的检验和字段只对报头进行检验.因为通常IP数据报的数据字段中包含的TCP,UDP,ICMP,IGMP数据头部都包含对自身每个比特的校验,所以并不需要在IP数据报报头中进行重复校验. 3.IP路由:当IP层收到一个数据报之后,首先1)检验目的IP地址

《TCP/IP详解 卷一》读书笔记-----广播&amp;多播&amp;IGMP

1.广播和多播都只适用于UDP,因为TCP是面向连接的,需要将两台主机的两个进程绑定在一起,即IP地址和端口对 2.通常,网卡能看到网络中的每一个数据帧,但是往往它只接受目的地址与自己MAC地址相同的或者为广播地址的帧(现在有些也能接收多播地址),然后将它传输给设备驱动 3.广播的问题在于它给那些对广播数据不感兴趣的主机增加了处理负担(需要通过网卡,驱动,IP等一层层的判断),而多播的出现恰好解决了这些问题.通常一台主机会被指定加入一个或多个多播组,网卡也会被告知自己属于哪几个多播组,于是只有属

《TCP/IP详解 卷一》读书笔记-----TCP数据流

1.Delayed Acknowledgements:TCP通常不会在收到数据之后立即返回一个ACK,而是会有一个延时,希望能ACK报文段中带上一些数据,通常这个延时为200ms 2.Nagle Algorithm:在TCP连接中,只能有一个小的报文段未被确认.即TCP在发送了一个小的报文段之后,会将之后的小数据都收集起来,直到之前的小报文段得到确认,才将收集到的小数据打包成一个报文段发出.这样做的减少了网络中小报文段的数量,减轻了网络的阻塞,提高了传输的效率 3.当一个包到达以后,它首先被设备

《TCP/IP详解 卷一》读书笔记-----动态路由协议

1.以下条件只要有一个不满足,则需要使用动态路由协议:1)网络规模小,2)只有一个连接点用于连接其他网络,3)没有冗余的路由器(一般用作备份) 2.所谓动态路由就是各个路由器与自己相邻的路由器交换各自连接的网络的信息,从而使自身的路由表一直处于最新状态,而不同的交流更新方式,称为不同的动态路由协议 3.我们将整个互联网分为许多个自治系统(autonomous system,AS),每个自治系统里的路由器使用同一个动态路由协议,称为IGP(interior gateway protocol)而两个

《TCP/IP详解 卷一》读书笔记-----DNS

1.DNS是一个分布式数据库系统用来提供主机名和IP地址之间的映射,之所以称为分布式原因的原因是因特网上没有一台主机知道这类映射的全部信息,当然也不可能做到,因为数据量实在太大了 2.应用程序通过一个叫resolver的程序(也可以认为是DNS的客户端)调用DNS服务,通常在UNIX系统下由gethostbyname(由主机名得到IP地址)和gethostbyaddr(由IP地址得到主机名)这两个库函数实现 3.不是每个name server都知道如何和其他所有的name sever通信的,但它

《TCP/IP 详解 卷一》读书笔记-----IP静态 路由

1.主机中的路由表只能被守护进程routing daemon或者“redirect”类型的ICMP报文所更新. 2.在根据路由表进行路由选择时,判断的优先级从高到低依次为1)表中存在与目的IP完全匹配的表项2)表中存在与目的地址的网络地址匹配的表项3)表中存在default,即默认路由表项.经历上述三个步骤仍未匹配成功的,则丢弃该数据报. 3.netstat指令用于查看主机的路由表,如下图所示: 其中Gateway的“0.0.0.0”表示目的主机与当前主机在同一网段中,可直接到达,无需网关进行转

《TCP/IP 详解 卷一》读书笔记-----Ping

1.ping是用于测试对方主机是否可达的命令,其实本质上就是echo类型的ICMP报文.同时,ping还能用于计算RTT(round-trip time),即两台主机间的往返时延. 2.随着网络安全意识的增加,路由器的访问权限控制和防火墙等网络安全措施的使用,使得主机的可达性不仅仅取决于网络层的状况,而且也与协议的类型和端口号等等其他因素有关,所以现在ping指令的失败并不能代表目的主机就是不可达的. 3.IP record route:许多版本的ping命令在添加了-R的选项后,能够让数据报经