正文
jQuery的$方法使用起来非常的多样式,接口实在太灵活了,有点违反设计模式的原则职责单一。但是用户却非常喜欢这种方式,因为不用记那么多名称,我只要记住一个$就可以实现许多功能,这个$简直就像个万能的魔术师,想要什么就变出来。其实当我们拆穿了这个魔术的表象,你会看到一个混乱的内部,jQuery内部做了太多的事了,而能够将这个混乱一一理清,并且毫无问题的运行起来,John Resig的能力不禁让人敬佩。
下面先来看看jQuery中的$方法(又叫jQuery)的几种用途或者调用方式:
1.$(document)
2.$(‘<div>’)
3.$(‘div’)
4.$(‘#test’)
5.$(function(){})
6.$("input:radio", document.forms[0]);
7.$(‘input’, $(‘div’))
8.$()
9.$("<div>", {
"class": "test",
text: "Click me!",
click: function(){ $(this).toggleClass("test"); }
}).appendTo("body");
10$($(‘.test’))
当我们执行一个$方法时,例如:$('div'),在控制台console.log($('div'))看看出来的是什么东西:
function () {我们看到一个对象有0?, length, (这不是数组吗?),preObject, selector这些属性,而且这是个实例对象,其隐藏属性__proto__指向一个原型对象,那个原型对象有一堆方法,那堆方法正是jQuery手册的方法。
现在就让我们进入$的世界,看看它究竟是何方神圣
在jQuery源码中看到jQuery(即$的定义方式),返回的是一个jQuery.fn.init这个构造函数的实例,这个jQuery方法就相当于是个简单的工厂方法,rootjQuery也是一个jQuery方法返回的实例,只是其参数是document,这里作为jQuery的根对象
var jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context, rootjQuery );
}
//...
rootjQuery = jQuery(document);
接下来往源码下面看:
jQuery.fn = jQuery.prototype = {
jQuery: core_version,
constructor: jQuery,
// 构造函数
init: function (selector, context, rootjQuery) {
//…
}
}
将jQuery的prototype对象的引用指向jQuery.fn,当两者其中一个发生改变,另一个也会随之改变。jQuery.fn相当于是jQuery.prototype的简写。
然后init这个构造函数了,$的万能钥匙就在这个构造函数里:
// 构造函数 init: function (selector, context, rootjQuery) { var match, elem; // 处理 $(""), $(null), $(undefined), $(false) // 返回的是jQuery()方法实例 if (!selector) { return this; } // 处理HTML字符串 if (typeof selector === 'string') { if (selector.charAt(0) === '<' && selector.charAt(selector.length - 1) === '>' && selector.length >= 3) { // 假设字符串以“<”开始且“>”结束 // 说明是HTML,略过正则检查 match = [null, selector, null]; } else { match = rquickExpr.exec(selector); } // 匹配HTML或者确保#id的上下文没被指定 if (match && (match[1] || !context)) { // 处理 $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // 向后兼容 jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // 处理 $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // if (jQuery.isFunction(this[match])) { this[match](context[match]); } else { this.attr(match, context[match]); } } } return this; // 处理 $(#id) } else { elem = document.getElementById(match[2]); // 检查parentNode,因为Blackberry 4.6 // 返回的节点不在document中 if (elem && elem.parentNode) { // 处理Opera返回的是name而不是id if (elem.id !== match[2]) { return rootjQuery.find(selector); } // 给this实例对象添加类似数组的属性 this.length = 1; this[0] = elem; } // 再添加上下文,选择器属性,最后返回this,结束函数 this.context = document; this.selector = selector; return this; } // 处理 $(expr, [$(...)]) } else if (!context || context.jQuery) { // 返回jQuery.fn.find()获取的匹配元素, // 该方法会使用jQuery.find方法(即Sizzle), // 然后通过jQuery.fn.pushStack和merge方法附加元素集及合并 return (context || rootjQuery).find(selector); // 处理 $(expr, context) // 即 $(context).find(expr) } else { return this.constructor(context).find(selector); } // 处理$(DOMElement) } else if (selector.nodeType) { this.context = this[0] = selector; this.length = 1; return this; // 处理$(function) // jQUery(document) ready的简写 } else if (jQuery.isFunction(selector)) { // 调用jQuery.fn.ready方法 return rootjQuery.ready(selector); } // 处理$($(...)) if (selector.selector !== undefined) { this.selector = selector.selector; this.context = selector.context; } // 返回伪数组对象 return jQuery.makeArray(selector, this); }