第二章 jQuery技术解密 (二)

简介: 2.2.6 延续 -- 迭代器 在 jQuery 框架中,jQuery 对象是一个很奇怪的概念,具有多重身份,所以很多初学者一听说 jQuery 对象就感觉很是不解,误以为它是 John Resig 制造的新概念。

2.2.6 延续 -- 迭代器

在 jQuery 框架中,jQuery 对象是一个很奇怪的概念,具有多重身份,所以很多初学者一听说 jQuery 对象就感觉很是不解,误以为它是 John Resig 制造的新概念。我们可以对jQuery 对象进行如下分解。

第一,jQuery 对象是一个数据集合,它不是一个个体对象。因此,你无法直接使用 JavaScript 的方法来操作它。

第二,jQuery 对象实际上就是一个普通的对象,因为它是通过 new 运算符创建的一个新的实例对象。它可以继承原型方法或属性,同时也拥有 Object 类型的方法和属性。

第三,jQuery 对象包含数组特性,因为它赋值了数组元素,以数组结构存储返回的数据。我们可以以 JavaScript 的概念理解 jQuery 对象,例如下面的示例。

[html] view plain copy
  1. <scripttype="text/javascript">
  2. varjquery={//定义对象直接量
  3. name:"jQuery",//以属性方式存储信息
  4. value:"1.3.2"
  5. };
  6. jquery[0]="jQuery";//以数组方式存储信息
  7. jquery[1]="1.3.2";
  8. alert(jquery.name);//返回"jQuery"
  9. alert(jquery[0]);//返回"jQuery"
  10. </script>
上面的 jQuery 对象就是一个典型的 jQuery 对象,jQuery 对象的结构就是按这种形式设计的。 可以说,jQuery 对象就是对象和数组的混合体,但是它不拥有数组的方法,因为它的数组结构是人为附加的,也就是说它不是 Array 类型数据,而是 Object 类型数据。

第四,jQuery 对象包含的数据都是 DOM 元素,是通过数组形式存储的,即通过 jQuery[n] 形式获取。同时 jQuery 对象又定义了几个模仿 Array 基本特性的属性,如 length 等。

所以,jQuery 对象是不允许直接操作的,只有分别读取它包含的每一个 DOM 元素,才能实现各种操作,如插入、删除、嵌套、赋值和读写 DOM 元素属性等。

那么如何实现直接操作 jQuery 对象中的 DOM 元素呢?

在实际应用中,我们可以看到类似下面的 jQuery 用法。

$("div").html()

也就是直接在 jQuery 对象上调用 html(),并实现操作 jQuery 包含的所有 DOM 元素。那么这个功能是怎么实现的呢?

jQuery 定义了一个工具函数 each(),利用这个工具可以遍历 jQuery 对象中所有的 DOM 元素,并把需要操作的内容封装到一个回调函数中,然后通过在每个 DOM 元素上调用这个回调函数即可。实现代码如下所示,演示效果如图 2.2 所示。

[html] view plain copy
  1. <scripttype="text/javascript">
  2. var$=jQuery=function(selector,context){//定义类
  3. returnnewjQuery.fn.init(selector,context);//返回选择器的实例
  4. };
  5. jQuery.fn=jQuery.prototype={//jQuery类的原型对象
  6. init:function(selector,context){//定义选择器构造器
  7. selector=selector||document;//设置默认值为document
  8. context=context||document;//设置默认值为document
  9. if(selector.nodeType){//如果选择符为节点对象
  10. this[0]=selector;//把参数节点传递给实例对象的数组
  11. this.length=1;//并设置实例对象的length属性,定义包含的元素个数
  12. this.context=selector;//设置实例的属性,返回选择范围
  13. returnthis;//返回当前实例
  14. }
  15. if(typeofselector==="string"){//如果选择符是字符串
  16. vare=context.getElementsByTagName(selector);//获取指定名称的元素
  17. for(vari=0;i<e.length;i++){//遍历元素集合,并把所有元素填入到当前实例数组中
  18. this[i]=e[i];
  19. }
  20. this.length=e.length;//设置实例的length属性,即定义包含的元素个数
  21. this.context=context;//设置实例的属性,返回选择范围
  22. returnthis;//返回当前实例
  23. }else{
  24. this.length=0;//否则,设置实例的length属性值为0
  25. this.context=context;//设置实例的属性,返回选择范围
  26. returnthis;//返回当前实例
  27. }
  28. },
  29. html:function(val){//模仿jQuery框架中的html()方法,为匹配的每一个DOM元素插入html代码
  30. jQuery.each(this,function(val){//调用jQuery.each()工具函数,为每一个DOM元素执行回调函数
  31. this.innerHTML=val;
  32. },val);
  33. },
  34. jquery:"1.3.2",//原型属性
  35. size:function(){//原型方法
  36. returnthis.length;
  37. }
  38. };
  39. jQuery.fn.init.prototype=jQuery.fn;//使用jQuery的原型对象覆盖init的原型对象
  40. //扩展jQuery工具函数
  41. jQuery.each=function(object,callback,args){
  42. for(vari=0;i<object.length;i++){
  43. callback.call(object[i],args);
  44. }
  45. returnobject;
  46. };
  47. $("div").html("测试代码");
  48. </script>
在上面的示例中,通过先为自己的 jQuery 对象绑定 html() 方法,然后利用 jQuery() 选择器获取页面中所有的 div 元素,再调用 html() 方法,为所有匹配的元素插入 HTML 源码。

注意,在上面的代码中,each() 函数的当前作用对象是 jQuery 对象,故 this 指向当前 jQuery 对象,即 this 表示一个集合对象;而在 html() 方法中,由于 each() 函数是在指定 DOM 元素上执行的,所以该函数内的 this 指针指向的是当前 DOM 元素对象,即 this 表示一个元素。


2.2.7 延续 -- 功能扩展

根据一般设计习惯,如果要为 jQuery 或者 jQuery.prototype 添加函数或方法,可以直接通过点语法实现,或者在 jQuery.prototype 对象结构中增加一个属性即可。但是,如果分析 jQuery 框架的源代码,你会发现它是通过 extend() 函数来实现功能扩展的。例如,下面两段代码都是 jQuery 框架通过 extend() 函数来扩展功能的。

jQuery.extend({ // 扩展工具函数

noConflict: function(deep){},

isFunction: function(obj){},

isArray: function(obj){},

isXMLDoc: function(elem){},

globalEval: function(data){}

});

或者

jQuery.fn.extend({ // 扩展 jQuery 对象方法

show: function(speed, callback){},

hide: function(speed, callback){},

toggle: function(fn, fn2){},

fadeTo: function(speed, to, callback){},

animate: function(prop, speed, easing, callback){},

stop: function(clearQueue, gotoEnd){}

});

这样做的好处是什么呢?

extend() 函数能够方便用户快速扩展 jQuery 框架的功能,但是不会破坏 jQuery 框架的原型结构,从而避免后期人工手动添加工具函数或者方法破坏 jQuery 框架的单纯性,同时也方便管理。如果不需要某个插件,只需要简单地删除即可,而不需要在 jQuery 框架源代码中去筛选和删除。

extend() 函数的功能实现起来也很简单,它只是把指定对象的方法复制给 jQuery 对象或者 jQuery.prototype 对象。例如,在下面的示例中,我们为 jQuery 类和原型定义了一个扩展功能的函数 extend() ,该函数的功能很简单,它能够把指定参数对象包含的所有属性复制给 jQuery 或者 jQuery.prototype 对象,这样就可以在应用中随时调用它,并动态扩展 jQuery 对象的方法。

[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. //jQuery功能扩展函数
  39. jQuery.extend=jQuery.fn.extend=function(obj){
  40. for(varpropinobj){
  41. this[prop]=obj[prop];
  42. }
  43. returnthis;
  44. };
  45. //扩展jQuery对象方法
  46. jQuery.fn.extend({
  47. test:function(){
  48. alert("测试扩展功能");
  49. }
  50. });
  51. //测试代码
  52. $("div").test();
  53. </script>

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

jQuery 框架定义的 extend() 函数的功能要强大很多,它不仅能够完成基本的功能扩展,还可以实现对象合并等功能。

2.2.8 延续 -- 参数处理

在很多时候,你会发现 jQuery 的方法都要求传递的参数为对象结构,例如:

$.ajax({

type: "GET",

url: "test.js",

dataType: "script"

});

使用对象直接量作为参数进行传递,方便参数管理。当方法或者函数的参数长度不固定时,使用对象直接量作为参数存在很多优势。例如,对于下面的用法,ajax()函数就需要进行更加复杂的参数排查和过滤。

$.ajax("GET", "test.js", "script");

如果 ajax() 函数的参数长度是固定的,且是必须的,那么通过这种方式进行传递也就无所谓了,但是如果参数的个数和排序是动态的,那么使用 $.ajax("GET", "test.js", "script"); 这种方法是无法处理的。而 jQuery 框架的很多方法都包含大量的参数,且都是可选的,位置也没有固定要求,所以使用对象直接量是惟一的解决方法。

使用对象直接量作为参数传递的载体,这里就涉及参数处理问题。如何解析并提出参数?如何处理参数和默认值?我们可以通过下面的方法来实现。

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

在上面的示例中,定义了一个原型方法 setOptions(),该方法能够对传递的参数对象进行处理,并覆盖默认值。这种用法在本书插件部分还将进行讲解。

在 jQuery 框架中, extend() 函数包含了所有功能,它既能够为当前对象扩展方法,也能够处理参数对象,并覆盖默认值。

2.2.9 涅槃 -- 名字空间

现在,我们终于模拟出了 jQuery 框架的雏形,虽然它还比较稚嫩,经不起风雨,但至少能够保证读者理解 jQuery 框架构成的初期状态。不过对于一个成熟的框架来说,需要设计者考虑的问题还是很多的,其中最核心的问题就是名字空间冲突问题。

当一个页面中存在多个框架,或者自己写了很多 JavaScript 代码,我们是很难确保这些代码不发生冲突的,因为任何人都无法确保自己非常熟悉 jQuery 框架中的每一行代码,所以难免会出现名字冲突,或者功能覆盖现象。为了解决这个问题,我们必须把 jQuery 封装在一个孤立的环境中,避免其他代码的干扰。

在详细讲解名字空间之前,我们先来温习两个 JavaScript 概念。首先,请看下面的代码。

var jQuery = function(){};

jQuery = function(){};

上面所示的代码是两种不同的写法,且都是合法的,但是它们的语义完全不同。第一行代码声明了一个变量,而第二行代码定义了 Window 对象的一个属性,也就是说它等同于下面的语句。

window.jQuery = function();

在全局作用域中,变量和 Window 对象的属性是可以相等的,也可以是互通的,但是当在其他环境中 (如局部作用域中),则它们是不相等的,也是无法互通的。

因此如果希望 jQuery 具有类似 $.method(); 调用方式的能力,就需要将 jQuery 设置为 Window 对象的一个属性,所以你就会看到 jQuery 框架中是这样定义的。

[html] view plain copy
  1. <scripttype="text/javascript">
  2. varjQuery=window.jQuery=window.$=function(selector,context){
  3. returnnewjQuery.fn.init(selector,context);
  4. };
  5. </script>
你可能看到过下面的函数用法。

(function(){

alert("观察我什么时候出现");

})();

这是一个典型的匿名函数基本形式。为什么要用到匿名函数呢?

这时就要进入正题了,如果希望自己的 jQuery 框架与其他任何代码完全隔离开来,也就是说如果你想把 jQuery 装在一个封闭空间中,不希望暴露内部信息,也不希望别的代码随意访问,匿名函数就是一种最好的封闭方式。此时我们只需要提供接口,就可以方便地与外界进行联系。例如,在下面的示例中分别把 f1 函数放在一个匿名函数中,而把 f2 函数放在全局作用域中。可以发现,全局作用域中的 f2 函数可以允许访问,而匿名函数中的 f1 函数是禁止外界访问的。

[html] view plain copy
  1. <scripttype="text/javascript">
  2. (function(){
  3. functionf1(){
  4. return"f1()";
  5. }
  6. })();
  7. functionf2(){
  8. return"f2()";
  9. }
  10. alert(f2());//返回"f2()"
  11. alert(f1());//抛出异常,禁止访问
  12. </script>
实际上,上面的匿名函数就是所谓的闭包,闭包是 JavaScript 函数中一个最核心的概念。

当然,$ 和 jQuery 名字并非是 jQuery 框架的专利,其他一些经典框架中也会用到 $ 名字,也许读者也会定义自己的变量 jQuery 。

在这之前我们需要让它与其他框架协同工作,这就带来一个问题,如果我们都使用 $ 作为简写形式就会发生冲突,为此 jQuery 提供了一个 noConflit() 方法,该方法能够实现禁止 jQuery 框架使用这两个名字。为了实现这样的目的,jQuery 在框架的最前面,先使用 _$ 和 _jQuery 临时变量寄存 $ 和 jQuery 这两个变量的内容,当需要禁用 jQuery 框架的名字时,可以使用一个临时变量 _$ 和 _jQuery 恢复 $ 和 jQuery 这两个变量的实际内容。实现代码如下。

[html] view plain copy
  1. <scripttype="text/javascript">
  2. (function(){
  3. var
  4. window=this,
  5. undefined,
  6. _jQuery=window.jQuery,//暂存jQuery变量内容
  7. _$=window.$,//暂存$变量内容
  8. jQuery=window.jQuery=window.$=function(selector,context){
  9. returnnewjQuery.fn.init(selector,context);
  10. },
  11. quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
  12. isSimple=/^.[^:#\[\.,]*$/;
  13. jQuery.fn=jQuery.prototype={
  14. init:function(selector,context){//定义选择器构造器
  15. selector=selector||document;//设置默认值为document
  16. context=context||document;//设置默认值为document
  17. if(selector.nodeType){//如果选择符为节点对象
  18. this[0]=selector;//把参数节点传递给实例对象的数组
  19. this.length=1;//并设置实例对象的length属性,定义包含的元素个数
  20. this.context=selector;//设置实例的属性,返回选择范围
  21. returnthis;//返回当前实例
  22. }
  23. if(typeofselector==="string"){//如果选择符是字符串
  24. vare=context.getElementsByTagName(selector);//获取指定名称的元素
  25. for(vari=0;i<e.length;i++){//遍历元素集合,并把所有元素填入到当前实例数组中
  26. this[i]=e[i];
  27. }
  28. this.length=e.length;//设置实例的length属性,即定义包含的元素个数
  29. this.context=context;//设置实例的属性,返回选择范围
  30. returnthis;//返回当前实例
  31. }else{
  32. this.length=0;//否则,设置实例的length属性值为0
  33. this.context=context;//设置实例的属性,返回选择范围
  34. returnthis;//返回当前实例
  35. }
  36. },
  37. setOptions:function(options){
  38. this.options={//方法的默认值,可以扩展
  39. StartColor:"#000",
  40. EndColor:"#DDC",
  41. Step:20,
  42. Speed:10
  43. };
  44. jQuery.extend(this.options,options||{});//如果传递参数,则覆盖原默认参数
  45. },
  46. jquery:"1.3.2",//原型属性
  47. size:function(){//原型方法
  48. returnthis.length;
  49. }
  50. };
  51. jQuery.fn.init.prototype=jQuery.fn;//使用jQuery的原型对象覆盖init的原型对象
  52. jQuery.extend=jQuery.fn.extend=function(destination,source){//重新定义extend()函数
  53. for(varpropertyinsource){
  54. destination[property]=source[property];
  55. }
  56. returndestination;
  57. };
  58. })();
  59. </script>
至此,jQuery 框架的设计模式就初见端倪了,后面的工作就是根据应用需要或者功能需要,使用 extend() 函数不断扩展 jQuery 的工具函数和 jQuery 对象的方法。
目录
相关文章
|
Web App开发 XML JSON
【jQuery入门】为JavaScript而生,简化JavaScript操作的神技术
之前我们学习了这个JSON热门技术,在之后的学习中都会多多少少的牵扯到JSON相关的知识的,好多技术中也会用到JSON,所以如果你还不知道JSON建议先去看一下我的上一篇博客。
【jQuery入门】为JavaScript而生,简化JavaScript操作的神技术
|
开发框架 JavaScript 前端开发
jQuery 已“死”?为清除技术债,我们删掉了前端所有 jQuery 依赖
近期,英国公共部门信息网站 GOV.UK 前端开发主管 Matt Hobbs 宣布该公司删除了 jQuery 作为所有前端应用程序的依赖项,这意味着“在所有 13 个 FE 应用程序中,JS 大小减少了 32 KB(31% ~49% 之间)”。
121 0
jQuery 已“死”?为清除技术债,我们删掉了前端所有 jQuery 依赖
|
设计模式 JavaScript 前端开发
尚能饭否|技术越来越新,我对老朋友jQuery还是一如既往热爱
最近在搭建完善自己的博客,需要用到一些页面样式之类的,就特意问了一下女朋友一个问题,关于Web前端开发,jQuery现在过时了嘛?她毅然决然告诉我,那是我们前端现在的鄙视链。是的,不...
111 0
|
JavaScript 前端开发
JavaScript 技术篇-js代码触发dom元素绑定事件实例演示,jquery触发元素绑定事件方法
JavaScript 技术篇-js代码触发dom元素绑定事件实例演示,jquery触发元素绑定事件方法
454 0
JavaScript 技术篇-js代码触发dom元素绑定事件实例演示,jquery触发元素绑定事件方法
|
JavaScript 前端开发
JavaScript 技术篇-本地js文件里直接集成jQuery的方法,js文件不依赖html外部引用直接使用jquery方法
JavaScript 技术篇-本地js文件里直接集成jQuery的方法,js文件不依赖html外部引用直接使用jquery方法
159 0
JavaScript 技术篇-本地js文件里直接集成jQuery的方法,js文件不依赖html外部引用直接使用jquery方法
|
Web App开发 JavaScript 前端开发
Jquery 图片延迟加载技术
参考网址:http://code.ciaoca.com/jquery/lazyload/ 延迟加载能大大增加你网站的加载速度! 需要引入以下文件: $('img').lazyload(); 这些JQ文件的下载地址:http://code.ciaoca.com/jquery/lazyload/ 官方网址:http://appelsiini.net/projects/lazyload/ 建议将这些JQ文件引用放在HTML 文档的最下面,这句话很重要。
1492 0
|
前端开发 JavaScript .NET
一起谈.NET技术,跟ASP.NET MVC一起使用jQuery
  藉由ASP.NET MVC内置的扩展性,开发人员便可以使用第三方库,例如jQuery。在使用ASP.NET Webforms的时候,如果使用jQuery而不是ASP.NET AJAX,难度会比较大。
1077 0
|
JSON JavaScript 前端开发
前后台交互经常使用的技术汇总(后台:Java技术,前台:Js或者Jquery)
1:由于针对特定的前后台交互用到的知识总结,所以不大量贴代码,主要给出思路,方便自己以后脑补和技术总结,当然也希望可以帮助到别人。 后台Json和其他格式转化,之前总结过Json和对象,集合,字符串的转化或者互相转化,这里我想网上有很多demo。
2009 0