原文参考:http://www.splashnology.com/article/the-structure-of-jquery-dive-into-source-code/2517/
Jquery很流行了,那么它到底是怎么构造的,记得一位老师说过“结构是宝,一懂百了;结构不懂,一窍不通”。这句话深深地在我脑海里。。。。。
1 (function( window, undefined ) {
2 var document = window.document,
3 navigator = window.navigator,
4 location = window.location;
5
6 [...] // 主要的代码部分
7
8 window.jQuery = window.$ = jQuery;
9 })(window);
1 var jQuery = (function () {
2 var jQuery = function ( selector, context ) {
3 return new jQuery.fn.init( selector, context, rootjQuery );
4 };
5 // jQuery的映射,防止被重写
6 _jQuery = window.jQuery,
7
8 // $的映射,防止被重写
9 _$ = window.$,
10
11 rootjQuery,
12
13 [...]
14
15 rootjQuery = jQuery(document);
16
17 [...]
18
19 return jQuery;
20 })();
这里,可以看出jQuery的构造函数,要了解如何防止重写,需要进一步的查看jQuery.noConflict (),rootjQuery一个参考document的jQuery对象,对于rootjQuery没有很明白。
接下来是一系列的正则表达式,在浏览器检测,解析Json,和jQuery.trim中需要用到。现代的浏览器支持字符串的.trim方法,还有数组的indexof方法,jQuery进行了本地的实现。
1 trim = String.prototype.trim,
2 indexOf = Array.prototype.indexOf,
进入到jQuery最“神圣”的地方,这一块是对那些没有使用过的人来说最难的地方。所以我们需要保持清醒的头脑,jQuery原型的神奇之处也就藏在这里。你已经在核心中见过它了:
1 var jQuery = function( selector, context ) {
2 //jQuery对象实际上是init构造器的‘加强’
3 return new jQuery.fn.init( selector, context, rootjQuery );
4 },
这其实意味着,当你调用jQuery函数的时候,“jQuery.fn.init”被创建并且返回。这就是JavaScript函数式编程的体现。在下面的代码中,我们可以看到:
1 jQuery.fn = jQuery.prototype = {
2 constructor: jQuery,
3 init: function( selector, context, rootjQuery ) {
4 [...]
5 }
6 [...]
7 }
8
9 //在以后的实例中把 jQuery的原型给给init函数
10 jQuery.fn.init.prototype = jQuery.fn;
到这里,我们知道了jQuery.fn就是jQuery.prototype。这会帮助我们理清下面的东西。
同时,jQuery.fn.init.prototype也指向了jQuery.prototype(第10行代码的作用), jQuery.fn.init.prototype 构造器指向了jQuery(第10行代码使得使得jQuery.fn.init.prototype指向jQuery.fn, 而第2行中jQuery.fn的构造器是jQuery).
这就会得到一个有趣的结果:
1 $(document) instanceof jQuery; // true
2 $(document) instanceof jQuery.fn.init; // true
为了更好的理解这种行为,下面给了一个简单的例子:
1 var Init = function () {
2 console.log('[Init]');
3 };
4
5 var jQuery = function () {
6 console.log('[jQuery]');
7 return new Init();
8 };
9
10 Init.prototype = jQuery.prototype = {
11 constructor: jQuery
12 };
13
14 var $elem = jQuery(); // [jQuery] , [Init]
15
16 console.log( $elem instanceof jQuery ); // true
17 console.log( $elem instanceof Init ); // true
因此,所有的构造都在jQuery.fn.init这个对象的函数中,jQuery是jQuery.fn.init对象的一个工厂。
有很多方式使用jQuery函数
1 $(function () { alert('READY!') }); // 当加载一个dom元素时,函数将被执行
2 $(document.getElementById('test')); // link on the element
3 $('<div />'); // 创建新元素
4 $('<div />', { title: 'test' }); //创建带属性的新元素
5
6 // 支持所有想得到的和想不到的css选择器
7 $('#element'); // id为element的元素
8 $('.element', $previous ); // 在前面的$中找到所有class为element的元素
9 $("div[name=city]:visible:has(p)");
让我们看一看构造器中的代码,我们已经知道构造器就是jQuery.fn.init.下面给出的是伪代码:
1 init: function( selector, context, rootjQuery ) {
2 if ( !selector ) return this;
3
4 // 处理 $(DOM元素)
5 if ( selector.nodeType ) return this = selector;
6
7 // body元素只存在一个,找到它的优化方法
8 if ( selector === "body" && !context ) return this = document.body;
9
10 if ( jQuery.isFunction( selector ) ) {
11 return rootjQuery.ready( selector );
12 }
13
14 // 处理 HTML 字符串
15 if ( typeof selector === "string" ) {
16 // 验证一个匹配,并且在上下文中没有元素#id
17 if ( selector.match(quickExpr) ) {
18 if ( match[1] ) {
19 return createNewDomElement( match[1] );
20 } else {
21 return getById( match[2] )
22 }
23 } else {
24 return jQuery( context ).find( selector );
25 }
26 }
27 },
前4中处理条件很有清楚的--对于空的选择器,DOM元素选择器以及body字符串的处理和为DomReady做的函数处理。
一个有趣的点是,当扫描一行时,最先解析的是“快速正则表达式”,而这个式子的左边是匹配tag标记的,其后才是匹配id元素。
1 quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
只有request很复杂的情况下,才调用find方法,利用搜索引擎来寻找元素。Sizzle (Copyright The Dojo Foundation).
专业写JavaScript的人都知道,利用JS的原型创建class,很容易继承。
1 var MyClass = function () {
2 // 构造器
3 };
4
5 MyClass.prototype = {
6 // 原型
7 };
8
9 var instance = new MyClass();
10
11 // 我们可以继承class的原型,并且在新的特性也可以添加到它的所有实例中,及时是已经创建的实例
12
13 MyClass.prototype.plugin = function () {
14 console.log("He's alive!");
15 };
16
17 instance.plugin(); // He's alive!
同样的方法,我可以也可以继承标准的jQuery原型:
1 jQuery.prototype.plugin = function () {
2 // 这里是我的组件
3 };
正如我们上面提到了,fn就是一个到jQuery.prototype的链接,我们还可以这样简洁的写:
1 jQuery.fn.plugin = function () {
2 // 这里是我的组件
3 // 这里指向的是jQuery对象,方法调用也是来自jQuery对象
4 };
这个组件将会出现在那些已经实现或者以后将要实现的实例中。用下面的方法直接向对象添加静态属性:
1 jQuery.plugin = function () {
2 // 这里是我的组件
3 };
所以写组件的最好结构是:
1 new function (document, $, undefined) {
2
3 var privateMethod = function () {
4 // 私有方法, 只对组件用
5 };
6
7 $.fn.myPlugin = function () {
8
9 };
10
11 // 如果需要,一个没有绑定dom元素的方法
12 $.myPlugin = function () {
13
14 };
15
16 }(document, jQuery);
这种方法可以再jQuery的大多数组件中看到。例如, DatePicker.
在作者看来,jQuery的流行在于它的简单性和易用性,还有简短的名称,比如用css代替setStyle,attr代替setAttribute等。这种想法很简单,也迷住了很多人。
人们往往能看到很多jQuery的“克隆”们,或者是一些想法在其他语言中运用。简单也是有欺骗性的,它也不总是好的。所以在你把方法名称缩短之前一定要三思,因为有可能就是它们会造成不必要的错误。
ps:对jQuery的用法不熟悉,为了学习它的结构才写的这东西,有不正确的地方一定要指正。谢谢!