在这部分中,主要是添加一些JQ的方法和属性,如:
JQuery:2.0.3 JQ版本
constructor:JQuery 重新指向JQ构造函数
init(): 初始化和参数管理的方法。
selector:存储选择字符串
length:this对象的长度
toArray():转换数组的方法
未完
代码解析:
这部分的代码都包含在
jQuery.fn = jQuery.prototype = { };
首先是对JQ版本的赋值和重指向:
jquery: core_version, constructor: jQuery,
这里需要住的是重指向,看如下代码:
function Obj() { } Obj.prototype.age = 10; var o1 = new Obj(); console.log(o1.constructor); //function Obj(){} function ObjNew() { } ObjNew.prototype = { age:10 } var o1New = new ObjNew(); console.log(o1New.constructor); //function Object(){}
运行代码可以看到,第二种通过字面量赋值的方式,对象上的constructor会丢失。因为这种方式是将原型上的对象进行覆盖操作,而不是添加。所以在JQ源码中需要重新指定一下。
INIT方法:
init方法是JQ最先执行的方法,通过这段代码进行调用:
jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor ‘enhanced‘ return new jQuery.fn.init( selector, context, rootjQuery ); },
init方法会接收三个参数,分别是:
selector:$()括号中的第一个参数。如:"#id" ".class" "<li>" document function()等
context:执行的上下文
rootJquery:JQ的根对象。
然后定义变量,并检查selector是否为空也就是对 $(""),$(null),$(undefind),$(false) 进判断。
var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; }
通过校验之后,接着是判断selector的类型:
if ( typeof selector === "string" ) { //实现代码 } else if ( selector.nodeType ) { //实现代码 } else if ( jQuery.isFunction( selector ) ) { //实现代码 }
依次对字符串、节点、函数进行判断,并分别进行了单独的处理
先对字符串中的代码进行解析:
if ( selector.charAt(0) === "<" && selector.charAt( 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 ); }
首先对字符串进行判断,如果传入的selector是"<li>"或"<li>1</li><li>2</li>"这种形式想要创建节点的,则 match=[null,"<li>",null]或则 match=[null,"<li>1</li><li>2</li>",null]
否则进行正则匹配,例如:$("#id"),$(".class"),$("div") 这种形式的。
match=null;//$(".class") $("div") $("#div div.class")
match=["#id",null,"id"] //$("#id")
match=["<li>hello","<li>",null] //$("<li>hello")
这里先对JQ的内部执行有一个说明,例如:$("li").css("background","red") 这段代码,对页面上的所有li的背景颜色赋值。那么这句代码的两部分分别做了什么处理呢。
首先 $("li") 其实是选择了页面上所有的li,并返回了一个this对象存放这些li的节点,
然后通过这个对象在对.css方法进行调用,在css方法内部的实现类似于:
for(var i=0;i<this.length;i++){ this[i].style.background="red"; }
了解完这个过程在看下面的代码:
if ( match && (match[1] || !context) ) { }
这个判断首先判断math不为null,并且match[1]有值,那么这就代表创建标签的语句满足条件,或者context为空,context为空代表是选择id,因为id没有上下文,所以满足这个条件的有:
$("<li>"),$("#id")
接下来在条件里进一步判断:
if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { //判断是创建标签还是id } else { //id执行这 }
在这个条件里又将创建标签和查找id区分。
先看创建标签的源码:
context = context instanceof jQuery ? context[0] : context; // scripts is true for back-compat jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) );
这段代码中,先将context赋值,在创建标签时,有是可能需要第二参数,这个第二个参数也就是执行上下文,例如:$("<li>",document) 一般很少这样使用,但是当页面中有iframe时,想在iframe中创建,那么第二参数设置为iframe后,就在iframe中创建了。
context instanceof jQuery ? context[0] : context 这句目的就是将context赋值为原生的节点,当我传递参数时,可能会:
1、$("<li>",document)
2、$("<li>",$(document))
这两种形式,同过这判断是否用第二种形式传入,如果是,则将原生的document对象赋值。
然后用到两个方法:jQuery.merge和jQuery.parseHTML方法。
先说一下jQuery.parseHTML方法,代码如下:
var str = "<li>1</li><li>2</li><li>3</li><li>4</li><li>5</li>"; var arr = jQuery.parseHTML(str, document, true); console.log(arr);
执行结果为,如图:
可以看到这个方法是将字符串转换为数组的形式。需要特别注意的最后一个参数,默认为false,为false代表不可以插入script代码,为true则代表可以。
再看下jQuery.merge方法
这个方法常用的功能就是将两个数组合并,如:
var arry1 = ["a", "b"]; var arry2 = ["c", "d"]; var arry3 = jQuery.merge(arry1, arry2); console.log(arry3); //["a", "b", "c", "d"]
但这里的this对象是个json对象,通过他也可以进行合并。并返回jq想要的json格式。如:
var arry1 = { 0: "a", 1: "b", length:2 }; var arry2 = ["c", "d"]; var arry3 = jQuery.merge(arry1, arry2); console.log(arry3);
结果如下:
接下来处理特殊的形式,也是很少使用的一种创建标签的方式,代码如下:
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 ] ); } } }
这种匹配的是:$("<li>",{title:"hello",html:"aaaaaaa"}) 后面有个json对象当参数的方式。
如果是这种方式的话,那么会循环这个json对象,先判断json里的属性是否是jq自带的方法,如果是,则直接调用方法,否则,进去else,用jq的attr方法为这个标签加一个属性。
到这里创建标签的方式就介绍完了。下面介绍一下传入id形式的代码。
elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 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;
代码很简单,但需要注意一下判断,这是为了最黑莓浏览器的兼容,因为在黑莓4.6版本的浏览器中,当删除节点之后,还可以用js代码查找到这个节点,
所以需要进行一下父节点的判断,因为任何节点都会有父节点。
接下来也是返回一个JQ需要的特殊json格式。赋值长度为1,第一个对象是当前查找到的对象。然后把上下文赋值document,赋值selector。
到这里已经把创建标签和id查找的源码分析完了,其他复杂查找的代码会进入下面的代码:
} else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); }
这段代码的判断就是要保证$("ul",document).find("li") $("ul",$(document)).find("li") 这两种形式,都会执行:jQuery(document).find();这个方法。
到这就把当参数是字符串传入时,要执行的代码解析完了,下面分析传入节点或者方法时要执行的代码。