jQuery原型技术分解

起源----原型继承

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

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

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

var jQuery = function(){

//函数体

}

</script>

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

所有类都有最基本的功能,如继承、派生和重写等。javascript很奇特,它通过所有函数绑定一个prototype属性,由这个属性指向一个原型对象,原型对象中可以定义类的继承属性和方法,所以,对于上面的空类,可以继续扩展原型。(继承请参见js学习笔记---类和模块

<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”>

var jQuery = function(){}

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.10.01”  //原型属性

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

return this.length;

}

}

</script>

生命------返回实例

上例中的代码相信很多人都写过,但是只有John Regis一个人把它发展成了jQuery这样牛X的框架。

上面定义了两个原型成员,这个框架基本的样子就出来啦,但是该如何调用jquery属性和size()方法呢?

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

<script language=”javascript” type = “text/javascript”>

var my$ = new $();

alert(my$.jquery);

alert(my$.size());

</script>

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

$().jquery

$().size();

也就是说,我们应该把jQuery看做一个类,同时也应该把它视为一个普通函数,并让这个函数返回值为jQuery类的实例。因此如下

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

var $=jQuery = function(){

return  new jQuery();

}

jQuery.fn = jQuery.prototype = {

jquery:”1.10.01”  //原型属性

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

return this.length;

}

}

//调用

alert(my$.jquery);

alert(my$.size());

</script>

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

回一下,当使用var my$ = new$(); 创建jQuery类的实例时,this关键字就指向对象my$,因此my$实例对象就获得了jQuery.prototype包含的原型属性或方法,这些 方法内this关键字就会自动指向my$实例对象。因此可以使用一个工厂方法来创建一个实例,这个方法放在jQuery.prototype原型对象中, 然后在jQuery函数中返回这个原型方法的调用。代码如下:

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

var $ = jQuery = function(){

return  jQuery.fn.init(); //调用原型方法init();

}

jQuery.fn = jQuery.prototype= {

init:function(){

return this;

}

,jquery:”1.10.01

,size:function(){

return  this.length;

}

}

//调用

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

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

</script>

学步-----分隔作用域

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

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

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

var $ = jQuery = function(){

return  jQuery.fn.init(); //调用原型方法init();

}

jQuery.fn = jQuery.prototype= {

init:function(){

this.length=0;

this.test = function(){

return this.length;

}

return this;

}

,jquery:”1.10.01

,length:1

,size:function(){

returnthis.length;

}

}

//调用

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

alert($().test());     //调用方法,返回0

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

</script>

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

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

var $=jQuery=function(){

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

}

</script>

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

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

var $=jQuery=function(){

return new  jQuery.fn.init();

}

jQuery.fn =jQuery.prototype = {

init:function(){

this.length = 0;

this.test= function(){

return this.length;

}

},

jquery:”1.10.01”,

length:1,

size:function(){

return this.length;

}

}

alert($().jquery);    //调用属性,返回 undefined

alert($().test());     //调用方法,返回0

alert($().size());     //调用方法,返回抛出异常

</script>

生长-----跨域访问

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

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

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

var $=jQuery=function(){

return new  jQuery.fn.init();

}

jQuery.fn =jQuery.prototype = {

init:function(){

this.length = 0;

this.test= function(){

return this.length;

}

},

jquery:”1.10.01”,

length:1,

size:function(){

return this.length;

}

}

jQuery.fn.init.prototype =jQuery.fn; //使用jQuery的原型对象覆盖init的原型对象。

alert($().jquery);    //调用属性,返回 1.10.01

alert($().test());     //调用方法,返回0

alert($().size());     //调用方法,0

</script>

成熟------选择器

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

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

<div></div>

<div></div>

<div></div>

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

var $=jQuery=function(){

return new jQuery.fn.init();

}

jQuery.fn = jQuery.prototype = {

init:function(selector,context){

selector = selector|| document;

context= context || document;

if(selector.nodeType){

this[0] = selector;

this.length = 1;

this.context = selector;

return this;

}

if(typeof selector = “string”){

var e = context.getElementByTagName(selector);

for(vari=0;i<e.length;i++){

this[i] = e[i];

}

this.length = e.length;

this.context = context;

return this;

}else{

this.length = 0;

this.context = context;

return this;

}

},

jquery:”1.10.01”,

length:1,

size:function(){

return this.length;

}

}

jQuery.fn.init.prototype =jQuery.fn; //使用jQuery的原型对象覆盖init的原型对象。

alert($(“div”).size());    //返回3

</script>

延续----迭代器

在jQuery框架中,jQuery对象是一个很奇怪的概念,具有多重身份:

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

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

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

<script language=”javascript” type = “text/javascript”>

var jquery= {

name:”jquery”,

value:”1.2.3”

};

jquery[0] = “jquery”;

jquery[1] = “1.2.3”;

alert(jquery.name);  //返回”jquery”

alert(jquery[0]); //返回”jquery”

</script>

jQuery对象的结构是按这种形式设计的。可以说,jQuery对象就是对象和数组的混合体,但是它不拥有数组的方法,因为它的数组结构是人为附加的,也就说它不是Array类型的数据,而是Object类型的数据。

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

所以jQuery对象是不允许直接操作,只有分别读取它包含的每一个DOM元素,才能够实现各种操作。

延续-----功能扩展

根据一般设计习惯,如果要为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({

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框架源代码中去筛选和删除。

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

//jQuery功能扩展函数

jQuery.fn.extend = jQuery.extend =function(obj){

for(var prop in obj) this[prop] =obj[prop];

return this;

}

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

jQuery.extend = jQuery.fn.extend =function(){

//定义复制操作的目标对象

var target = arguments[0]||{},i=1,length=arguments.length,deep = false,options;

//获取是否深度复制处理

if(typeof target ===”boolean”){

deep = target;

target = arguments[1] || {};

//跳出布尔值和目标对象

i = 2;

}

if(typeof target!=”object” &&!jQuery.isFunction(target)) target = {};

if(length==i){

target = this;

--i;

}

for(;i<length;i++){

//若参数不为null,则进行处理

if((options=arguments[i])!=null){

//extend the baseobject

for(var name in options){ //遍历参数对象

var src =target[name],copy = options[name];

if(target===copy)continue;

//递归运算

if(deep&& copy && typeof copy===”object” && !copy.nodeType)

target[name]= jquery.extend(deep,

//不要复制原对象

src || (copy.length!=null?[]:{}),copy);

else if(copy!==undefined)

target[name] = copy;

}

return target;

}

}

}

延续----参数处理

对于参数不固定,传递参数的最好办法是对象直接量。

命名空间

var jQuery =function(){}

jQuery=function(){}

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

window.jQuery =function(){};

在全局作用域中,变量和Window对象的属性可以是相等的,也是可以是互通的,但是 当在其他环境中(如局部作用域中),则它们是不相等的,也是无法互通的,因此如果希望jquery具有类似$.method();调用的能力,就需要将 jQuery调协为Window对象的一个属性,所以你就会看到jQuery框架中是这样定义的。

var jQuery = window.jQuery = window.$ =function(selectio,context){

return new jQuery.fn.init(selector,context);

}

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

(function(){

function f1(){

return “f1()”;

}

})();

function f2(){

return “f2()”;

}

alert(f2());  //返回”f2()”

alert(12());  //抛出异常,禁止访问

</script>

实际上,上面的匿名函数是所谓的闭包,闭包是javascript函数中一个最核心的概念。当然,$和jQuery名字并非是jQuery框架的专利,其 他一些经典框架中也会用到$名字,为此jQuery提供了一个noConfilit()方法,该方法能够实现禁止jQuery框架使用这两个名字。为了实 现这样的目的,jQuery在框架的最前面,先使用_$和_jQuery临时寄存这两个变量的内容,当需要禁用jQuery框架的名字时,可以使用一个临 时变量_$和_jQuery恢复和jQuery这两个变量的实际内容。如下代码:

(function(){

var window = this,

undefined,

_jQuery = window.jQuery,

_$ = window.$,

jQuery = window.jQuery = window.$ =function(selector,context){

return newjQuery.fn.init(selector,context);

};

jQuery.fn = jQuery.prototype = {

init:function(selector,context){}

}

})()

jQuery原型技术分解的更多相关文章

  1. OpenStack 实现技术分解 (7) 通用库 — oslo_config

    目录 目录 前文列表 扩展阅读 osloconfig argparse cfgpy class Opt class ConfigOpts CONF 对象的单例模式 前文列表 OpenStack 实现技 ...

  2. 细说jQuery原型的创建和实现原理,并用实例简单模仿

    在解析jQuery实现机理之前,我们先总结一下几点知识,这些都是我学习路上遇到的坑,我跌倒过很多次,现在把它补上: 1)自定义构造函数,如下例: function person(){ this.nam ...

  3. jQuery源码解析对象实例化与jQuery原型及整体构建模型分析(一)

    //源码剖析都基于jQuery-2.0.3版本,主要考虑到兼容IE 一.关于jQuery对象实例化的逻辑: 整个jQuery程序被包裹在一个匿名自执行行数内: (function(window,und ...

  4. OpenStack 实现技术分解 (5) 应用开发 — 使用 OpenStackClients 进行二次开发

    文件夹 文件夹 前文列表 參考阅读 前言 OpenStackClients 使用 OpenStackClients 获取 project_client object 的 demo 调用 project ...

  5. 1-1 课程导学 & 1-2 项目需求分析,技术分解.

    1-1 课程导学 1-2 项目需求分析,技术分解. 要有一定的dart基础,了解安卓和ios的一些普通的开发

  6. 2016/2/26 jQuery的技术 1,安装 2,语法选择器$ 事件触发 3,常用jQuery函数

    在<网页制作Dreamweaver(悬浮动态分层导航)>中,运用到了jQuery的技术,轻松实现了菜单的下拉.显示.隐藏的效果,不必再用样式表一点点地修改,省去了很多麻烦,那么jQuery ...

  7. JQ实现选项卡(jQuery原型插件扩展)

    下边分为两个版本,一种是点击切换选项(index.js),一种是滑过切换选项(index1.js) HTML文件: jq使用jquery-1.11.3.js版本 <!DOCTYPE html&g ...

  8. OpenStack 实现技术分解 (6) 通用库 — oslo_log

    目录 目录 前文列表 扩展阅读 日志级别 oslolog 初始化设置 DEMO oslolog 的相关配置项 oslolog 的日志级别 oslolog 的使用技巧 推荐使用 LOGdebug 的地方 ...

  9. Openstack 实现技术分解 (4) 通用技术 — TaskFlow

    目录 目录 前文列表 扩展阅读 简介 基本概念 实现样例 最后 前文列表 Openstack 实现技术分解 (1) 开发环境 - Devstack 部署案例详解 Openstack 实现技术分解 (2 ...

随机推荐

  1. java中单例设计模式

    在java中创建单例的方式主要有三种:饿汉式.懒汉式.登记式.以下内容均是摘抄自 http://blog.csdn.net/jason0539/article/details/23297037/ 一. ...

  2. ASCII,Unicode 和通用方式

    ASCII码 字符char,字符指针char*,字符数组char a[]; 例如:char a='A'; char* pC="beijing"; char aC[]="b ...

  3. zencart 具体页面调用规则: $body_code变量解析

    zencart $body_code变量解析 修改centerColumn 可以修改中间产品方框的大小 2.2.5 .BODY文件在这个文件生效 require($body_code) include ...

  4. 上传数据插件 Easy Populate 遇到问题 ?langer=remove

    批量上传,出现如下错误提示: ADD NEW PRODUCT FAILED! – Model: no200 – SQL error. Check Easy Populate error log in ...

  5. java获取硬盘ID以及MAC地址

    http://blog.csdn.net/coolwzjcool/article/details/6698327 版权声明:本文为博主原创文章,未经博主允许不得转载. 为了达到软件注册,或者说软件和电 ...

  6. eclipse proxy

    -Dorg.eclipse.ecf.provider.filetransfer.excludeContributors=org.eclipse.ecf.provider.filetransfer.ht ...

  7. javascript 函数 add(1)(2)(3)(4)实现无限极累加 —— 一步一步原理解析

    问题:我们有一个需求,用js 实现一个无限极累加的函数, 形如 add(1) //=> 1; add(1)(2)  //=> 2; add(1)(2)(3) //=>  6; add ...

  8. php 注意点

    1.如果一个方法可静态化,就对它做静态声明.速率可提升至4倍. 2.echo 比 print 快. 3.使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接. 4.在执行for循环之前确定 ...

  9. JQuery中的mouseover和mouseenter的区别

    mouseover和mouseout是一对:mouseenter和mouseleave是一对. 相同点:都是鼠标经过就触发事件 不同点: 给外盒子一个经过触发事件,但是mouseover会在鼠标经过外 ...

  10. Android init.rc文件浅析

    Android init.rc文件浅析 分类: Android2012-04-13 18:00 13149人阅读 评论(2) 收藏 举报 androidservicepathactionsocketc ...