第二章 jQuery技术解密(一)

2.2 jQuery 原型技术分解

任何复杂的技术都是从最简单的问题开始的,如果你被 jQuery 几千行庞杂结构的源代码所困惑,那么建议你阅读本节内容,我们将探索 jQuery 是如何从最简单的问题开始,并逐步实现羽翼渐丰的演变过程,从 jQuery 核心技术的还原过程来理解 jQuery 框架的搭建原理。

2.2.1 起源 -- 原型继承

用过 JavaScript 的读者都会明白,在 JavaScript 脚本中到处都是函数,函数可以归置代码段,把相对独立的功能封装在一个函数包中。函数也可以实现类,这个类是面向对象编程中最基本的概念,也是最高抽象,定义一个类就相当于制作了一个模型,然后借助这个模型复制无数的实例。

例如,下面的代码就可以定义最初的 jQuery 类,类名就是 jQuery ,你可以把它视为一个函数,函数名是 jQuery 。当然,你也可以把它视为一个对象,对象名就是 jQuery 。与其他面向对象的编程语言相比,JavaScript 对于这个概念的界定好像很随意,这降低了编程的门槛,反之也降低了 JavaScript 作为编程语言的层次。

<script language="javascript" type="text/javascript">

var jQuery = function(){

// 函数体

};

</script>

上面创建了一个空的函数,好像什么都不能够做,这个函数实际上就是所谓的构造函数。构造函数在面向对象语言中是类的一个特殊方法,用来创建类。在 JavaScript 中,你可以把任何函数都视为构造函数,这没有什么不可以的,这样不会伤害代码本身。

所有类都有最基本的功能,如继承、派生和重写等。JavaScript 很奇特,它通过为所有函数绑定一个 prototype 属性,由这个属性指向一个原型对象,原型对象中可以定义类的继承属性和方法等。所以,对于上面的空类,可以继续扩展原型,其代码如下。

<script language="javascript" type="text/javascript">

var jQuery = function(){};

jQuery.prototype = {

// 扩展的原型对象

};

</script>

原型对象是 JavaScript 实现继承的基本机制。如果你觉得 jQuery.prototype 名称太长,没有关系,我们可以为其重新命名,如 fn ,当然你可以随便命名。如果直接命名
fn ,则表示该名称属性 Window 对象,即全局变量名。更安全的方法是为 jQuery 类定义一个公共属性, jQuery.fn ,然后把 jQuery 的原型对象传递给这个公共属性,实现代码如下。

<script language="javascript" type="text/javascript">

jQuery.fn = jQuery.prototype = {

// 扩展的原型对象

};

</script>

这里的 jQuery.fn 相当于 jQuery.prototype 的别名,方便以后使用,它们指向同一个引用。因此若要调用 jQuery 的原型方法,直接使用
jQuery.fn 公共属性即可,不需要直接引用 jQuery.prototype ,当然直接使用 jQuery.prototype 也是可以的。

既然原型对象可以使用别名,jQuery 类也可以起个别名,我们可以使用 $ 符号来引用它,代码如下。

var $ = jQuery = function(){};

现在模仿 jQuery 框架源码,给它添加两个成员,一个是原型属性 jquery ,一个是原型方法 size(),其代码如下。

<script language="javascript" type="text/javascript">

var $ = jQuery = function(){};

jQuery.fn = jQuery.prototype = {

jquery: "1.3.2", // 原型属性

size: function(){// 原型方法

return this.length;

}

};

</script>

2.2.2 生命 -- 返回实例

当我们为 jQuery 添加了两个原型成员:jquery 属性和 size() 方法之后,这个框架最基本的样子就孕育出来了。但是该如何调用 jquery 属性和 size() 方法呢?

也许,你可以采用如下方法调用:

<script language="javascript" type="text/javascript">

var my$ = new $(); // 实例化

alert(my$.jquery);// 调用属性,返回 "1.3.2"

alert(my$.size());// 调用方法,返回 undefined

</script>

但是,jQuery 不是这样调用的。它模仿类似下面的方法进行调用。

$().jquery;

$().size();

也就是说,jQuery 没有使用 new 运算符将 jQuery 类实例化,而是直接调用 jQuery() 函数,然后在这个函数后面直接调用 jQuery 的原型方法。这是怎么实现的呢?

如果你模仿 jQuery 框架的用法执行下面的代码,浏览器会显示编译错误。这说明上面这个案例代码还不是真正的 jQuery 技术原型。

alert($().jquery);

alert($().size());

也就是说,我们应该把 jQuery 看做一个类,同时也应该把它视为一个普通函数,并让这个函数的返回值为 jQuery 类的实例。因此,下面这种结构模型才是正确的。

[html]view
plain
copy

  1. <scripttype="text/javascript">
  2. var$=jQuery=function(){
  3. returnnewjQuery();//返回类的实例
  4. };
  5. jQuery.fn=jQuery.prototype={
  6. jquery:"1.3.2",//原型属性
  7. size:function(){//原型方法
  8. returnthis.length;
  9. }
  10. };
  11. alert($().jquery);
  12. alert($().size());
  13. </script>

但是,如果在浏览器中预览,则会提示如图 2.1 所示的错误。内存外溢,说明出现了死循环引用。

那么如何返回一个 jQuery 实例呢?

回忆一下,当使用 var my$ = new $(); 创建 jQuery 类的实例时,this 关键字就指向对象 my$ ,因此 my$ 实例对象就获得了 jQuery.prototype 包含的原型属性或方法,这些方法内的 this 关键字就会自动指向 my$ 实例对象。换句话说,this 关键字总是指向类的实例。

因此,我们可以这样尝试:在 jQuery 中使用一个工厂方法来创建一个实例 (就是 jQuery.fn),把这个方法放在 jQuery.prototype 原型对象中,然后在 jQuery() 函数中返回这个原型方法的调用。代码如下所示。

[html]view
plain
copy

  1. <scripttype="text/javascript">
  2. var$=jQuery=function(){
  3. returnjQuery.fn.init();//调用原型init()
  4. };
  5. jQuery.fn=jQuery.prototype={
  6. init:function(){//在初始化原型方法中返回实例的引用
  7. returnthis;
  8. },
  9. jquery:"1.3.2",//原型属性
  10. size:function(){//原型方法
  11. returnthis.length;
  12. }
  13. };
  14. alert($().jquery);//调用属性,返回"1.3.2"
  15. alert($().size());//调用方法,返回undefined
  16. </script>

2.2.3 学步 -- 分隔作用域

我们已经初步实现了让 jQuery() 函数能够返回 jQuery 类的实例,下面继续思考:init() 方法返回的是 this 关键字,该关键字引用的是 jQuery 类的实例,如果在 init() 函数中继续使用 this 关键字,也就是说,假设我们把 init() 函数也视为一个构造器,则其中的 this 该如何理解和处理?

例如,在下面示例中,jQuery 原型对象中包含一个 length 属性,同时 init() 从一个普通的函数转身变成了构造器,它也包含一个 length 属性和一个 test() 方法。运行该示例,我们可以看到,this 关键字引用了 init() 函数作用域所在的对象, 此时它访问 length 属性时,返回0. 而 this 关键字也能够访问上一级对象 jQuery.fn 对象的作用域,所以 $().jquery 返回 "1.3.2"
。但是调用 $().size() 方法时,返回的是 0, 而不是 1 。

[html]view
plain
copy

  1. <scripttype="text/javascript">
  2. var$=jQuery=function(){
  3. returnjQuery.fn.init();//调用原型init()
  4. };
  5. jQuery.fn=jQuery.prototype={
  6. init:function(){//在初始化原型方法中返回实例的引用
  7. this.length=0;
  8. this.test=function(){
  9. returnthis.length;
  10. }
  11. returnthis;
  12. },
  13. jquery:"1.3.2",//原型属性
  14. length:1,
  15. size:function(){//原型方法
  16. returnthis.length;
  17. }
  18. };
  19. alert($().jquery);//返回"1.3.2"
  20. alert($().test());//返回0
  21. alert($().size());//返回0
  22. </script>

这种设计思路很容易破坏作用域的独立性,对于 jQuery 这样的框架来说,很可能会造成消极影响。因此,我们可以看到 jQuery 框架是通过下面的方式调用 init() 初始化构造函数的。

<script type="text/javascript">

var $ = jQuery = function(){

return new jQuery.fn.init(); // 实例化 init 初始化类型,分隔作用域

};

</script>

这样就可以把 init() 构造器中的 this 和 jQuery.fn 对象中的 this 关键字隔离开来,避免相互混淆。但是,这种方式也会带来另一个问题:无法访问 jQuery.fn 对象的属性或方法。例如,在下面的示例中,访问 jQuery.fn 原型对象的 jquery 属性和 size() 方法时就会出现这个问题。

[html]view
plain
copy

  1. <scripttype="text/javascript">
  2. var$=jQuery=function(){
  3. returnnewjQuery.fn.init();//实例化init初始化类型,分隔作用域
  4. };
  5. jQuery.fn=jQuery.prototype={
  6. init:function(){//在初始化原型方法中返回实例的引用
  7. this.length=0;
  8. this.test=function(){
  9. returnthis.length;
  10. }
  11. returnthis;
  12. },
  13. jquery:"1.3.2",//原型属性
  14. length:1,
  15. size:function(){//原型方法
  16. returnthis.length;
  17. }
  18. };
  19. alert($().jquery);//返回undefined
  20. alert($().test());//返回0
  21. alert($().size());//抛出异常
  22. </script>

2.2.4 生长 -- 跨域访问

如何做到既能够分隔初始化构造器函数与 jQuery 原型对象的作用域,又能够在返回实例中访问 jQuery 原型对象呢?

jQuery 框架巧妙地通过原型传递解决了这个问题,它把 jQuery.fn 传递给 jQuery.fn.init.prototype ,也就是说用 jQuery 的原型对象覆盖 init 构造器的原型对象,从而实现跨域访问,其代码如下所示。

[html]view
plain
copy

  1. <scripttype="text/javascript">
  2. var$=jQuery=function(){
  3. returnnewjQuery.fn.init();//实例化init初始化类型,分隔作用域
  4. };
  5. jQuery.fn=jQuery.prototype={
  6. init:function(){//在初始化原型方法中返回实例的引用
  7. this.length=0;
  8. this.test=function(){
  9. returnthis.length;
  10. }
  11. returnthis;
  12. },
  13. jquery:"1.3.2",//原型属性
  14. length:1,
  15. size:function(){//原型方法
  16. returnthis.length;
  17. }
  18. };
  19. jQuery.fn.init.prototype=jQuery.fn;//使用jQuery的原型对象覆盖init的原型对象
  20. alert($().jquery);//返回"1.3.2"
  21. alert($().test());//返回0
  22. alert($().size());//返回0
  23. </script>

这是一招妙棋,new jQuery.fn.init() 创建的新对象拥有 init 构造器的 prototype 原型对象的方法,通过改变 prototype 指针的指向,使其指向 jQuery 类的 prototype ,这样创建出来的对象就继承了 jQuery.fn 原型对象定义的方法。

2.2.5 成熟 -- 选择器

jQuery 返回的是 jQuery 对象,jQuery 对象是一个类数组的对象,本质上它就是一个对象,但是它拥有数组的长度和下标,却没有继承数组的方法。

很显然,上面几节的讲解都是建立在一种空理论基础上的,目的是希望读者能够理解 jQuery 框架的核心构建过程。下面,我们就尝试为 jQuery() 函数传递一个参数,并让它返回一个 jQuery 对象。

jQuery() 函数包含两个参数 selector 和 context ,其中 selector 表示选择器,而 context 表示选择的内容范围,它表示一个 DOM 元素。为了简化操作,我们假设选择器的类型仅限定为标签选择器。实现的代码如下所示。

[html]view
plain
copy

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

在上面示例中,$("div") 基本拥有了 jQuery 框架中 $("div") 语法的功能,使用它可以选取页面中指定范围的 div 元素。同时,调用 size() 方法可以返回 jQuery 对象集合的长度。

时间: 2024-08-01 03:39:37

第二章 jQuery技术解密(一)的相关文章

第二章 jQuery技术解密 (二)

2.2.6 延续 -- 迭代器 在 jQuery 框架中,jQuery 对象是一个很奇怪的概念,具有多重身份,所以很多初学者一听说 jQuery 对象就感觉很是不解,误以为它是 John Resig 制造的新概念.我们可以对jQuery 对象进行如下分解. 第一,jQuery 对象是一个数据集合,它不是一个个体对象.因此,你无法直接使用 JavaScript 的方法来操作它. 第二,jQuery 对象实际上就是一个普通的对象,因为它是通过 new 运算符创建的一个新的实例对象.它可以继承原型方法

第二章 jQuery技术解密 (四)

2.3.4 生成 DOM 元素 jQuery.fn.init() 构造函数能够构建 jQuery 对象,并把匹配的 DOM 元素存储在 jQuery 对象内部集合中.jQuery.fn.init() 构造函数可以接收单个的 DOM 元素,也可以接收 DOM 集合.如果接收的是字符串型 ID 值,则直接在文档中查找对应的 DOM 元素,并把它传递给 jQuery 对象:如果接收的是字符串型 HTML 片段,则需要把这个字符串片段生成 DOM 元素.下面我们将重点分析 jQuery 是如何把 HTM

第二章 jQuery技术解密 (六)

2.4 解析 jQuery 选择器引擎 Sizzle jQuery 从 1.3 版本开始,使用了新的选择器引擎 Sizzle(官方网址 http://sizzlejs.com) .Sizzle 是 jQuery 作者 John Resig 开发的 DOM 选择器引擎 (Dom Selector Engine),速度号称业界第一.而且它有一个重要的特点就是 Sizzle 是完全独立于 jQuery 的,如果用户不想用 jQuery ,还可以只用 Sizzle . Sizzle 选择器引擎目前成为

第二章 jQuery技术解密 (五)

2.3.5 引用 DOM 元素 jQuery() 函数能够直接接受 HTML 字符串,并把它们转换为 DOM 结构,这是上一节中所讲解的利用 jQuery() 函数生成 DOM 元素.当然,我们也可以看到 jQuery() 函数还可以接收 DOM 元素.DOM元素集合.HTML标签或者 ID 值.下面我们就来分析 jQuery.fn.init() 构造器是如何把这些类型的参数转换为 DOM 元素的. 对于 HTML 标签来说,它使用 document.getElementsByTagName()

第二章 jQuery技术解密 (七)

2.4.5 Sizzle 构造器 在 jQuery.fn.init() 构造器中,通过调用 jQuery(context).find(selector) 函数来解析并匹配 DOM 元素.jQuery.find() 函数实际上是引用 Sizzle() 函数,而 Sizzle() 函数仅是 Sizzle 引擎的构造器,它主要调用 Sizzle.find() 函数在 DOM 文档树中查找与 CSS 语法相匹配 DOM 的元素节点的集合.jQuery 名字中 Query 的意义就体现在这里.下面我们来分

《众妙之门——JavaScript与jQuery技术精粹》——第1章 初学JavaScript 需知的七件事 1.1 缩略标记

第1章 初学JavaScript 需知的七件事 我很早以前就开始编写JavaScript代码,很高兴看到这种语言在今天所取得的成功,能成为这个成功故事中的一部分我很开心.关于JavaScript,我写过许多文章.章节以及一整本书,直到今天我仍在寻找新的东西.下文是一些我工作学习过程中激动时刻的记录,大家与其守株待兔,不如自己尝试去体会这种感受. 1.1 缩略标记 众妙之门--JavaScript与jQuery技术精粹 在创建对象和数组过程中可以使用缩略标记是我喜欢JavaScript的重要原因之

&amp;gt; 第二章 NGWS Runtime 技术基础(rainbow 翻译) (转自重粒子空

<<展现C#>> 第二章 NGWS Runtime 技术基础(rainbow 翻译)   出处:http://www.informit.com/matter/ser0000001/chapter1/ch02.shtml 正文: 第二章  NGWS  runtime 技术基础     既然你已经具有了C#全面的印象,我也想让你了解NGWS runtime的全貌.C#依靠由NGWS提供的运行时:因此,有必要知道运行时如何工作,以及它背后所蕴含的概念.    所以,这一章分为两部分--它

《众妙之门——JavaScript与jQuery技术精粹》——导读

前 言 众妙之门--JavaScript与jQuery技术精粹 对于网站开发设计人员而言,在面对选择解决方案时做出正确的决定并不容易.不论是在建立复杂的网站应用还是在改进网站的过程中,都会有很多前期解决方案可供选择,有时选择最合适的一款方案至关重要.本书着重讲述了在选择相应解决方案时务必要注意的事项,即是否稳定并易于定制.是否有实用性并易于理解.是否具有可维护性.兼容性,以及功能的可拓展性. 本书重点阐述了检验代码的重要性以及在执行JavaScript程序时需要避免的问题.所选择的解决方案应能符

jQuery技术内幕:深入解析jQuery架构设计与实现原理1

jQuery技术内幕:深入解析jQuery架构设计与实现原理 高 云 著 图书在版编目(CIP)数据 jQuery技术内幕:深入解析jQuery架构设计与实现原理 / 高云著. -北京:机械工业出版社,2013.11 ISBN 978-7-111-44082-6 I. j- II. 高- III. JAVA语言-程序设计 IV. TP312 中国版本图书馆CIP数据核字(2013)第221662号 版权所有·侵权必究 封底无防伪标均为盗版 本书法律顾问 北京市展达律师事务所     本书由阿里巴