在 JavaScript 中,每个函数对象都有一个默认的属性 prototype,称为函数对象的原型成员,这个属性指向一个对象,称为函数的原型对象,当我们每定义了一个函数的时候,JavaScript 就创建了一个对应的原型对象,也就是说,当我们定义一个函数的时候,实际上得到了两个对象,一个函数对象,一个原型对象。原型对象是一个特殊的对象,函数的 prototype 成员指向它的原型对象。
可以通过函数对象的 prototype 成员取得这个原型对象的引用。
下面定义一个函数对象 Person,然后通过 prototype 来取得它的原型对象。然后在它的原型对象上定义了一个方法。
function Person() { } Person.prototype.showPerson = function() { alert( "Person Object."); } var alice = new Person(); alice.showPerson();?这个原型对象上定义的成员将用来共享给所有通过这个函数创建的对象使用。
每个对象也都有一个原型成员 prototype,通过 new 函数创建的对象会通过函数的 prototype 找到函数的原型,然后将自己的原型指向这个对象。对于不是通过函数创建的对象实例和原型对象,它们的原型会被设置为 Object 函数的原型对象。
Object 函数对象是 JavaScript 中定义的顶级函数对象,在 JavaScript 中所有的对象都直接或者间接地使用 Object 对象的原型。当访问对象的属性或者方法的时候,如果对象本身没有这个属性或者方法,那么,JavaScript 会检查对象的 prototype 对象是否拥有这个属性或者方法,如果有,则作为对象的属性或者方法返回,如果没有,那么将通过原型对象的 prototype 继续进行检查,直到原型对象为 Object 函数的原型对象为止。
但是 prototype 是一个特殊的属性,在大多数的浏览器上,例如 IE 浏览器,都不能直接访问对象的 prototype 成员。返回的结果为 undefined。不能赋予对象一个新的原型,只能通过创建它的函数来确定对象的原型。
函数对象的原型有一个特殊的用途,就是通过函数 new 创建出来的对象,会自动将函数对象的原型赋予新创建出的对象的原型。这样,如果为某个函数设置了原型对象,那么,所有通过这个函数创建的对象将拥有同样的原型对象。通过这个方法,可以使这些对象共享相同的属性或者方法,来模拟类型的概念。
?
在 jQuery 中,我们经常使用的 $() 函数就是定义在 window 对象上的 $() 函数。
1 |
jQuery = window.jQuery = window.$ = function ( selector, context )?
|
2 |
{ |
3 |
???? // The jQuery object is actually just the init constructor 'enhanced'
|
4 |
???? return new jQuery.fn.init( selector, context );
|
5 |
} |
这个函数实际上通过 new jQuery.fn.init( selector, context )来完成,也就是通过 init 函数创建了一个对象。
下面重新指定了函数 init 的原型对象。所以 init 函数的原型对象就是 fn 对象。
1 |
// Give the init function the jQuery prototype for later instantiation |
2 |
jQuery.fn.init.prototype = jQuery.fn; |
这样所有通过 $ 创建出来的对象都将共享 fn 对象上的成员。因此,jQuery 对象都有了类似 attr 、html 等等方法了。
?
?
?
---------------------分割线----------------------------
?
?
细致的讲:
?
用过javascript的朋友都会明白,在javascript脚本中到处都是函数,函数可以把独立功能封装在一个函数包中。函数也可以实现类,这个类是面线对象编程中最基本的概念。也是最高抽象,定义一个类就相当于制作了一个模型,然后借助这个模型复制很多个实例。 对象:数据+操作这些数据的方法类:具有类似功能的对象的集就是类 所有类都有最基本的功能:继承、派生、重写 Javascript通过所有函数绑定一个prototype属性,这个属性指向一个原型对象,原型对象中可以定义类的属性和方法
- var?$=jQuery=function(){} ??
- jQuery.fn=jQuery.prototype={ ??
- ????jQuery:"1.3.2", ???
- ????size:function(){ ??
- ????????return?this.length; ??
- ????} ??
- };??
也许你会采用如下方法调用:
- var?my$=new?$();//实例化 ??
- alert(my$.jQuery);//调用属性,返回1.3.2??
但是,jQuery不是这样调用的:
- $().jQuery;??
如果你这样些代码:
- alert($().jQuery);??
浏览器会显示错误,那jQuery是怎么做的呢 你会发现jQuery没有使用new运算符将jQuery实例化,而是直接调用jQuery函数然后在这个函数后直接调用jQuery的原型方法。怎么实现的呢? 猜测下:我们应给把jQuery看作一个类,同时也要把他视为一个普通的函数,并让这个函数返回jQuery的实例。
- var?$=jQuery=function(){ ??
- ????return?new?jQuery();//返回jQuery的实例 ??
- } ??
- jQuery.fn=jQuery.prototype={ ??
- ????jQuery:"1.3.2", ??
- ????size:function(){ ??
- ????????return?this.length; ??
- ????} ??
- }; ??
- ??
- alert($().jQuery);??
但是浏览器会弹出“ttack overflow at line 8”内存溢出的错误,说明程序出现了死循环引用 回一下,当使用var my$=new $();创建jQuery类的实例时,this关键字就指向my$对象,所以my$对象就获得了jQuery.prototype包含的原型属性和方法,这些方法的关键字就会自动指向my$实例对象。(this关键字总是指向类的实例) 因此我们可以做如下的尝试:在jQuery中使用一个工厂方法来创建一个实例,把这个方法放在原型对象jQuery.prototype中,然后在jQuery函数中返回这个原型方法的调用。
- var?$=jQuery=function(){ ??
- ????return?jQuery.fn.init();//返回原型方法init(); ??
- } ??
- jQuery.fn=jQuery.prototype={ ??
- ????init:function(){//在初始化原型方法中返回实例的引用 ??
- ?????return?this; ??
- ????}, ??
- ????jQuery:"1.3.2", ??
- ????size:function(){ ??
- ????????return?this.length; ??
- ????} ??
- }; ??
- ??
- alert($().jQuery);??
这时就可以成功的显示1.3.2的预期结果了。 继续思考:init()方法返回的是this关键字,该关键字引用的是jQuery类的实例,如果在init函数中继续使用this关键字this。这个this怎么处理呢? 例如:jQuery原型对象包含一个length属性,同时init()从一个普通的函数转变成一个构造器,它可以包含一个length属性和test()方法。运行该实例我们可以看到this引用了init函数作用域所在的对象,此时它访问length属性时,返回0.而this关键字也能够访问上一级对象jQuery.fn对象的作用域,$().jQuery返回“1.3.2”。但是调用$().size()方法时,返回的是0而不是1。
- 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.3.2", ??
- ????length:1, ??
- ????size:function(){ ??
- ????????return?this.length; ??
- ????} ??
- }; ??
- ??
- alert($().jQuery); ??
- alert($().test()); ??
-
alert($().size());??
我们可以看到jQuery是通过下面的方法调用init初始化构造函数的:
- var?$=jQuery=function(){ ??
- ????return?new?jQuery.fn.init();//返回原型方法init(); ??
- }??
这样就可以把init构造器中的this关键字和jQuery.fn中的关键字this分隔开来,避免了相互混淆。但是问题是:无法反问jQuery.fn对象的属性和方法!
- 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.3.2", ??
- ????length:1, ??
- ????size:function(){ ??
- ????????return?this.length; ??
- ????} ??
- }; ??
- ??
- alert($().jQuery);//返回undefined ??
- alert($().test());//返回0 ??
-
alert($().size());//抛出异常??
如何做到既能分割初始化构造器函数与jQuery对象的作用域,又能够在返回的实例中访问jQuery原型对象呢?
- var?$=jQuery=function(){ ??
- ????return?new?jQuery.fn.init();//返回原型方法init(); ??
- } ??
- jQuery.fn=jQuery.prototype={ ??
- ????init:function(){//在初始化原型方法中返回实例的引用 ??
- ????????this.length=0; ??
- ????????this.test=function(){ ??
- ????????????return?this.length; ??
- ????????}; ??
- ????????return?this; ??
- ????}, ??
- ????jQuery:"1.3.2", ??
- ????length:1, ??
- ????size:function(){ ??
- ????????return?this.length; ??
- ????} ??
- }; ??
- jQuery.fn.init.prototype=jQuery.fn; ??
- alert($().jQuery); ??
- alert($().test()); ??
- alert($().size());??
jQuery巧妙的通过原型传递解决了这个问题,它把jQuery.fn传递给jQuery.fn.init.prototype,也就用jQuery的原型对象覆盖init构造器的圆形对象,从而实现跨域访问
?
?
?
?