jQuery源码个人心得(1)
jquery版本v1.8.3
首先整体看jQuery的代码组织结构
(function( window, undefined ) { ...... window.jQuery = window.$ = jQuery; })( window );
通过执行匿名函数,将jQuery引入到全局作用域window名下。
关于这里为何要将window对象传入,个人觉得:
1.就是要绑定jQuery对象到window对象(要将jQuery对象暴露到全局作用域,以执行匿名函数的方式,可以不用传递window对象就将其暴露到全局作用域吗?)
通过函数返回jQuery对象应该也可以实现此功能,代码如下:
var jQuery = (function( ) { ...... return jQuery; })( ); var $ = jQuery;
作者传递window对象肯定还另有其他原因。下面是参考网上的帖子:
[url]http://hi.baidu.com/tang_guangyao/item/8aaae79d88454c9b82d2950d
[/url]
2.将window对象作为匿名函数的形参,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window考虑如果jQuery中对window对象的属性访问很频繁,这个开销就必须考虑了。
3.另外,由于window作为函数的形参,所有在代码压缩的时候就能够得到进一步的优化,例如,将window用a代替
(function( a, undefined ) { ...... a.jQuery = a.$ = jQuery; })( window );
天晓得作者当时是否考虑到代码压缩这一点!
另外一个让人奇怪的地方就是,匿名函数定义了一个undefined形参,估计很多和我一样的新人在开始看源码时心情一样的,这是在干嘛呀!
在自调用匿名函数的作用域内,确保undefined是真的未定义。因为undefined能够被重写,赋予新的值。
上面的原因很费解,运行下面的代码你发现undefined依然被改变了。
function test(undefined) { console.log(undefined); undefined='a'; console.log(undefined); console.log(typeof undefined); console.log(window.undefined === undefined); } test();
这里其实作者的意思是undefined这个全局属性没法保证正确。如果直接使用undefined。有可能undefined的值已经被修改过了。
这里给匿名函数定义一个不会传递实参的形参。目的是确保这个undefined变量是真的未定义。至于匿名函数内部,作者确保不会修改函数的形参undefined就是了。
undefined能够修改的根源在于undefined是一个全局属性,在ECMASCRIPT3中undefined时可读写的,导致undefined有可能在使用前被认为修改。但是在ECMASCRIPT5规范中undefined已经定义为只读了。[P44javascript权威指南]
曾经看过一篇文章,里面有一句话“倘若JohnResig个人不再维护jQuery,还有谁能够看懂他的这一坨坨”;
JohnResig大牛的世界岂能被一般人看穿,不过jQuery的代码的却很晦涩.
jQuery对象从何而来,见下面
jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); },
我们每次$('xxx')的时候其实都在newjQuery.fn.init。
接下来
jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { ...... } ...... }
这里要补充一下,是函数都会有一个prototype属性,是对象都会有一个__init__属性,指向构建该对象的对象的原型,比较绕
再往下看
jQuery.fn.init.prototype = jQuery.fn;
三段代码,真要看明白,必须对js的基础概念有一定的理解。
首先就是原型式继承的理解,关于原型式继承的详细介绍可以参考网上更专业的文章,原型式继承核心的一个东西就是prototype属性,
前文说过,函数就会有一个prototype属性,通过在prototype属性上添加属性和方法,就可以为以该函数为构造函数的对象扩充属性和方法。
returnnewjQuery.fn.init(selector,context,rootjQuery);这一句代码就有很大的迷惑性。千万别掉进这个代码中。
第一段代码中其实就是定义了一个变量jQuery变量,并将一个函数赋予这个变量
你可以在解释器中执行如下代码:
varj=function(){returnnewj.fn.init();}
你会看到j仅仅一个函数,并且也不存在语法错误。
接着看第二段代码,是函数就会有prototype属性,这里对jQuery这个函数的prototype属性进行了扩充,如同大家扩充String对象的indexOf方法一样
这里的扩充是希望达到类似的效果
vars=newString('abc');s.indexOf('a');
同理
varj=newjQuery();j.xxx();
但一般我们不这么用,我们这样用$('id');对应到代码一中就是newjQuery.fn.init('id');
别急这里就有另外一个问题啦,那就是如何给jQuery对象自我扩充的能力呢?
请看第三段代码。
这里我们将jQuery的属性fn赋予了jQuery.fn.init.prototype,而jQuery.fn.init就是我们调用jQuery对象时new的那个对象,
这样,我们在扩充jQuery的fn属性时就间接地扩充了jQuery对象的能力。