jQuery初始化

来源:互联网 发布:phpcms 模板使用php 编辑:程序博客网 时间:2024/06/09 18:51
  • jQuery(selector, context)构造器
    前面介绍了jQuery内部全局变量和构造函数,从正则表达式和核心函数的定义中可以看出,jQuery在框架封装上追求的是简洁、实用。在构造jQuery对象中,同样可以看到jQuery的风格。首先,看下jQuery是如何构造对象?jQuery.fn.init(selector, context, rootjQuery)这个函数具有三个参数,那么jQuery的使用者只能传递两个参数,因为jQuery(seletcor, context)构造器只提供了selector、context这两个形参。从selector、context字面上理解为选择器、上下文。
  • jQuery选择器
    jQuery的api中选择器提供了五中结构:#id、element、.class、*和selector1,selector2,…,selectorN联合形式。先来看下jQuery.init初始化中源码
jQuery.fn = jQuery.prototype = {        // The current version of jQuery being used        jquery: core_version,        constructor: jQuery,        init: function(selector, context, rootjQuery) {            var match, elem;            // HANDLE: $(""), $(null), $(undefined), $(false)            if (!selector) {                return this;            }            // Handle HTML strings            if (typeof selector === "string") {                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);                }                // Match html or make sure no context is specified for #id                if (match && (match[1] || !context)) {                    // HANDLE: $(html) -> $(array)                    if (match[1]) {                        context = context instanceof jQuery ? context[0] : context;                        var aaa = jQuery.parseHTML(                            match[1],                            context && context.nodeType ? context.ownerDocument || context : document,                            true                        )                        // scripts is true for back-compat                        jQuery.merge(this, aaa);                        // 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;                        // HANDLE: $(#id)                    } else {                        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;                    }                    // HANDLE: $(expr, $(...))                } 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);                }                // HANDLE: $(DOMElement)            } else if (selector.nodeType) {                this.context = this[0] = selector;                this.length = 1;                return this;                // HANDLE: $(function)                // Shortcut for document ready            } else if (jQuery.isFunction(selector)) {                return rootjQuery.ready(selector);            }            if (selector.selector !== undefined) {                this.selector = selector.selector;                this.context = selector.context;            }            return jQuery.makeArray(selector, this);        },... ...

jQuery.prototype.init函数体可以拆分为几个if结构:!selector、typeof selector === “string”和selector.selector !== undefined、selector.nodeType和jQuery.isFunction( selector )。
- !selector

// HANDLE: $(""), $(null), $(undefined), $(false)    if (!selector) {        return this;    }

内容一目了然,就是传入:”“、null、undefined和false形式的选择器时,返回一个空的jQuery对象。
- selector.selector !== undefined

if (selector.selector !== undefined) {         this.selector = selector.selector;         this.context = selector.context;    }    //将selector中的属性按数组下标形式组装到this对象属性中,如this[0]、this[1]、this[2]...    return jQuery.makeArray(selector, this);

如果选择器是一个对象,并且该selector对象包含属性selector,则将该对象包装成jQuery对象,同时由jQuery.makeArray工具方法返回selector和this属性的[]。

jQuery.extend({    ... ...    // results is for internal usage only    makeArray: function(arr, results) {        var ret = results || [];        if (arr != null) {            //如果Object(arr)为类数组对象,那么将arr合并到ret返回数组中           if (isArraylike(Object(arr))) {               jQuery.merge(ret,                     typeof arr === "string" ? [arr] : arr);           } else {                //否则,[].push(arr)直接存入数组中               core_push.call(ret, arr);           }       }       return ret;    },    /*     * 将数组second合并到first数组中,并返回first,也就是first结构会改变。     */    merge: function(first, second) {        var l = second.length,            i = first.length,            j = 0;        if (typeof l === "number") {            for (; j < l; j++) {                first[i++] = second[j];            }        } else {           while (second[j] !== undefined) {               first[i++] = second[j++];           }        }        first.length = i;        return first;     },    ... ...});/** -  判断obj是否为数组的依据是length属性和类型,处理window对象外都可以组装成数组。 -  对于每一个页面都只能有一个window对象。 */function isArraylike(obj) {    var length = obj.length,        type = jQuery.type(obj);    if (jQuery.isWindow(obj)) {       return false;    }    if (obj.nodeType === 1 && length) {       return true;    }    return type === "array" || type !== "function" &&        (length === 0 ||           typeof length === "number" && length > 0 && (length - 1) in obj);}   
  • selector.nodeType
    如果选择器为HTMLElement元素,则将该元素包装为jQuery对象,并返回。此时,this[0]属性和this.context都设置为selector,并且this.length为1,也就是说jQuery对象是一个isArraylike检查为true的类数组对象。
  • jQuery.isFunction( selector )
    如果选择器为函数,则将该函数作为ready事件的绑定函数,也就是document.onload的加载处理器。
  • typeof selector === “string”
    字符类型的选择器可以分为#id、<*>类型的选择器,同时context有无对元素定位也是不同。所以将该类型的选择器切分为几个分支结构:
  • 单标签的html(<input>、 <img>和<link>等等)
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 ]; // 如果selector是aa<....>这种形式的标签, 则html字符检查正则表达式/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/ } else {     // 这个正则表达式可以检查<...>html字符,和#...这种Id字符,并且match匹配只会有两种  [<aa>dd,<aa>,undefined]或[#dd,undefined,dd]      match = rquickExpr.exec( selector ); }
  • <*>*</*>、#id选择器
// Match html or make sure no context is specified for #id,如果match有匹配结果,则进行处理if ( match && (match[1] || !context) ) {    // HANDLE: $(html) -> $(array),如果匹配的是html元素选择器,如<div>、aa<div>、......    if ( match[1] ) {  //判断当前Element上下文是否jQuery上下文,如果是则去[0]的元素       context = context instanceof jQuery ? context[0] : context;  //返回Element元素数组       var aaa = jQuery.parseHTML(match[1],       context && context.nodeType ? context.ownerDocument || context : document, true)//合并aaa到this上下文中        // scripts is true for back-compat        jQuery.merge( this,  aaa);        // 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;       // HANDLE: $(#id),如果选择器是按id来进行,那么调用document原生getElementById来实现    } else {       elem = document.getElementById( match[2] );       // Check parentNode to catch when Blackberry 4.6 returns       // nodes that are no longer in the document #6963,   //因为在黑莓4.6版本中存在Element节点没有parentNode属性情况,所以这里单独处理       if ( elem && elem.parentNode ) {          // Inject the element directly into the jQuery object          this.length = 1;          this[0] = elem;        }//如果元素没有parentNode,那么this对象中就没有元素,而只是一个空的jQuery对象        this.context = document;        this.selector = selector;       return this;     }// Match html or make sure no context is specified for #id,如果match有匹配结果,则进行处理    if ( match && (match[1] || !context) ) {       // HANDLE: $(html) -> $(array),如果匹配的是html元素选择器,如<div>、aa<div>、......       if ( match[1] ) { //判断当前Element上下文是否jQuery上下文,如果是则去[0]的元素       context = context instanceof jQuery ? context[0] : context;//返回Element元素数组           var aaa = jQuery.parseHTML(match[1],               context && context.nodeType ? context.ownerDocument || context : document, true)//合并aaa到this上下文中            // scripts is true for back-compat            jQuery.merge( this,  aaa);            // 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;// HANDLE: $(#id),如果选择器是按id来进行,那么调用document原生getElementById来实现} else {elem = document.getElementById( match[2] );// Check parentNode to catch when Blackberry 4.6 returns        // nodes that are no longer in the document #6963,//因为在黑莓4.6版本中存在Element节点没有parentNode属性情况,所以这里单独处理        if ( elem && elem.parentNode ) {        // Inject the element directly into the jQuery object            this.length = 1;             this[0] = elem;         }//如果元素没有parentNode,那么this对象中就没有元素,而只是一个空的jQuery对象         this.context = document;         this.selector = selector;         return this;      }// HANDLE: $(expr, $(...)),如果为selector指定了context(jQuery对象),那么直接调用find方法筛选对象}

这一块包含的内容可以从context有没有进行区分,因为从match = rquickExpr.exec( selector )开始,就像match数组的match[1]有没有值进行判断<div>和#id选择器。首先!context为真、match[1]为真时document.createElement(*)创建HTMLElement元素,也就是$(<div>)创建元素。其次!context为真、match[1]为假时document.getElementById(match[2])先获取元素,然后包装成jQuery对象。然后,当!context为真、match为false,也即形如div、.class、parent>child和selector1…这种选择器,由( context || rootjQuery ).find( selector )筛选出符合条件的元素。最后,构造this.constructor( context ).find( selector )jQuery对象,同时查找符合selector条件的元素。
从源码中看到jQuery.prototype原型中修正了constructor构造器,这样做的目的是因为jQuery.prototype原型重新赋值了Object字面量{},那么jQuery的constructor就不在指向本身,而是指向了Object,所以通过人为的给{}添加constructor属性,并让它指向jQuery来修正constructor。
- 结论
从上面看出,jQuery初始化函数init中实现了jQuery选择器selector,对于简单的element、#id和<\w\W+>形式的元素选择,可以直接包装jQuery对象返回,此外jQuery对象本身是一个类数组的对象,它方招数组下标访问形式this[0]、this[1]… … 和this.length属性,可以for(var)下标遍历,而不只是传统对象{}的foreach遍历。

原创粉丝点击