吃完午饭,觉得有点发困,想起了以后我们的产品可能要做到各种浏览器的兼容于是乎不得不清醒起来!我们的web项目多数是依赖于Jquery的。据了解,在Jquery的2.0版本以后对IE的低端版本浏览器不再支持(IE 5,6,7,8)这样我们要做到兼容的话可能要调整当前jquery中的源代码。我使用NuGet中安装了最新的2.1.4版本。我们在项目中最最熟悉的就是使用JQ的选择器($(...)),说做就做,马上给大家简单粗矿的解释下。
那么我们常用的大概是这样几种形式:
$("#ID")
$(".Class")
$("<div></div>")
$("div")
$(this)
那么在之前我的文章中已经交代了$(...)是如何获得到当前对象后能够调用到自己的方法,在这里不多说。
Jquery是在它原型链的init()方法中将参数传入,代码如下:
var
// Use the correct document accordingly with window argument (sandbox)
document = window.document, version = "2.1.4", // Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
},
那么继续向下看,在Jquery 2.1.4源码中第2735行看到了init中的实现过程。
如果在JQ的选择器中你给定格式是下面的几种,如$(""), $(null), $(undefined), $(false)那么JQ就直接将直接return this;不会执行接下来的操作。
那么参数如果是第一个代码块中的形式并且为字符串则执行如下代码:
if ( typeof selector === "string" ) {
if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ]; } else {
match = rquickExpr.exec( selector );
} // Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) { 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
) );//....未全部列出
首先来看match 是在init的开始阶段声明的变量,目的就是来记录当前JQ传入的参数。
1.如果进入这个分支->if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
那么JQ的选择器中传入的就是标签的形式如:$("<div></div>"),所以match应该就是这种结构:match = [ null, <div></div>, null ];。
2.如果跳到else中则执行:
match = rquickExpr.exec( selector );
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
显而易见,这样处理之后selector满足如$("#oo")这种形式,那么match = [ "#div", null, "div"];。
3.如果非以上两种形式那么可能为$(".Class")或更复杂的选择器,这样match就会返回null。
***如果对此有问题,则正则表达式的exec方法***
接下来我们继续关注Jquery 2.1.4源码中第2785行:
} else {
elem = document.getElementById( match[2] ); if ( elem && elem.parentNode ) {
// Inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
} this.context = document;
this.selector = selector;
return this;
}
这是 if(match[1]) {的else分支,观察上下文可知这段else就是处理$("#oo")这种形式,通过Id找到对象并返回,简单的原生JS很好理解。接下来让我们的目光转向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
) ); // HANDLE: $(html, props)
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] ); // ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
} return this;
这段代码就是为传入参数为Dom节点工作的,作为一个前端的程序员,我们对$的merge方法再熟悉不过了,它在外部暴漏的功能就是合并数组;那么向下请仔细看它却可以合并JSON。我们先暂时不理会它为什么要合并JSON,先考虑这样一个问题,代码如下:
$("span").css("background","red");
也许要是你来实现,你一定会这样想,很简单的创建两个span标签然后循环遍历后改变css属性。代码的实现大致是这样:
for(;i<xxx.length;i++){
xxx[i].style.background = "red";
}
但是看了$("span")对象结构后我却十分惊奇,JQuery竟然遍历的是一个JS对象(JSON),我们都知道JS对象是不能遍历的,但如果我们把对象的属性设置为特殊的形式那么就让遍历成为了可能,代码如下:
this={
0:span,
1:span,
2:span,
length:3
} for(;i<this.length;i++){
this[i]//
}
到这里大家是不是觉得眼前一亮,JQuery巧妙的运用了语言结构,来实现这里并最后将对象返回再去调用其它方法,真是做的十分给力。镜头拉回来让我们看看$.merge()中的处理:
jQuery.merge( this, jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
这里meger()方法的功能就是将$.parseHTML()方法生成的数组合并到this的JSON中去。而parseHTML的第三个参数true以将HTML脚本标签添加到组中并生效。
1.***第二个参数如果传入为空那么就是当前的文档document,当然也可以是iframe***
2.***meger和parseHTML方法的实现,以后有时间会继续讲解***
最后让我们看下 if(match[1])中的最后处理,代码如下:
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] ); // ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
}
$("<span>1</span>", { id: 123, style: "" });
针对上面<span>1</span>标签例子,Jquery约定第一个string参数为单标签,第二为对象参数那么就会进入上面的代码块中将对象中提到的属性加入到该对象标签中。
至于为什么是单标签可以在rsingleTag指向的正则表达式中找到答案。
到了这里我并没有觉得松了一口气,却因初涉Jquery源码被其的“博大精深”把心提到了“嗓子眼”啊。以后有机希望能够继续给朋友们分享Jquery的“灵魂”,以上内容如有错误之处请各位大侠果断指出,小弟不胜感激!么么哒。