• jquery源码笔记(三): jquery.prototype的一些方法 和属性 init


    jquery.fn = jquery.prototype = {  添加实例属性和方法,

          jquery: 版本,

          constructor: 修正指向问题

          init(): 初始化 和 参数管理

          selector:存储选择字符串

          length: this 对象的长度

          toArray(): 转数组

          get(): 转原生集合

          pushStack(): jquery 对象入栈

          each()  :  便利集合

          ready():DOM加载的接口

          slice(): 集合的截取

          first(): 集合的第一项

          last(): 集合的最后一项

          eq(): 集合的指定项

          map(): 返回新集合

          end(): 返回集合的前一个状态

          push():  (内部使用)

          sort():  (内部使用)

          splice(): (内部使用)

    }

    jQuery选择器接口:

    jquery.prototype  的 init:

    jQuery.fn.init的功能是对传进来的selector参数进行分析,进行各种不同的处理,然后生成jQuery对象。

    类型(selector)

    处理方式

    DOM元素

    包装成jQuery对象,直接返回

    body(优化)

    从document.body读取

    单独的HTML标签

    document.createElement

    HTML字符串

    document.createDocumentFragment

    #id

    document.getElementById

    选择器表达式

    $(…).find

    函数

    注册到dom ready的回调函数


    我们来分解一个表达式

    // A simple way to check for HTML strings
    // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
    // Strict HTML recognition (#11290: must start with <)
         rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/,

    作者的解释呢很简单,一个简单的检测HTML字符串的表达式

    分解:

    1. 通过选择|分割二义,匹配^开头或者$结尾

    • ^(?:s*(<[wW]+>)[^>]*
    • #([w-]*))$

    2. ^(?:s*(<[wW]+>)[^>]*

    • (?:pattern) : 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用
    • s* : 匹配任何空白字符,包括空格、制表符、换页符等等 零次或多次 等价于{0,}
    • (pattern) : 匹配pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,使用 $0$9 属性
    • [wW]+ : 匹配于'[A-Za-z0-9_]'或[^A-Za-z0-9_]' 一次或多次, 等价{1,}
    • (<[wW]+>) :这个表示字符串里要包含用<>包含的字符,例如<p>,<div>等等都是符合要求的
    • [^>]* : 负值字符集合,字符串尾部是除了>的任意字符或者没有字符,零次或多次等价于{0,},

    3. #([w-]*))$

    • 匹配结尾带上#号的任意字符,包括下划线与-

    4. 还要穿插一下exec方法

    • 如果执行exec方法的正则表达式没有分组(没有括号括起来的内容),那么如果有匹配,他将返回一个只有一个元素的数组,这个数组唯一的元素就是该正则表达式匹配的第一个串;如果没有匹配则返回null。
    • exec如果找到了匹配,而且包含分组的话,返回的数组将包含多个元素,第一个元素是找到的匹配,之后的元素依次为该匹配中的第一、第二...个分组(反向引用)

    所以综合起来呢大概的意思就是:匹配HTML标记和ID表达式(<前面可以匹配任何空白字符,包括空格、制表符、换页符等等)


    一个简单的测试:

    var s = /(userd)+([d-h]{2})./;
        var str = "cuser2ffffcdas32userc";
        alert(s.exec(str));        //["user2ff",'user2','ff']      user2ff  满足正则表达式,   user2 符合第一个括号里的   ff符合第二个括号的
    
        var s2 = /(userd).|([d-h]{2})./;
        var str2 = "cffc2user4c";
         alert(s2.exec(str2));    //['ffc',null,'ff']          ffc 满足整个表达式,  第一个括号没找到,所以是null  ,ff符合第二个括号

    jQuery选择器接口

    API

    image

    jQuery是总入口,选择器支持9种方式的处理

    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’))

    init: function( selector, context, rootjQuery ) {
            var match, elem;
            // HANDLE: $(""), $(null), $(undefined), $(false)  如果参数是这4个,直接返回,
            if ( !selector ) {
                return this;
            }
            // Handle HTML strings  如果参数是 字符串 例如 :   $("#id") $(".ccc") $("div") $('<li>')   $('<div>')  $('<li>2</li><li>33</li>') $('<li>hello')
            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 如果参数 以'<' 开头,  以'>' 结尾,即标签,如: $('<li>') $('<div>') $('<li>1</li><li>2</li>')
                    match = [ null, selector, null ];     //match = [null,'<li>',null]    [null,'<div>',null]  [null,'<li>1</li><li>2</li>',null]
                } else {
                    match = rquickExpr.exec( selector );    
                }
                //$('#id') 对应的 match = ['#id',null,'id'] $('<li>hello') 对应的 match = ['<li>hello','<li>',null];
                 $('.ccc') $('div') 因为匹配不到,所以对应的 match = null;
        if ( match && (match[1] || !context) ) {  //满足 match是真, 说明是 插入标签 或者 id

    处理 插入标签:

    // HANDLE: $(html) -> $(array)
                    if ( match[1] ) {        //如果match[1] 是真 ,说明插入的是标签,(标签的match第二个不是null)
                        context = context instanceof jQuery ? context[0] : context;  //如果context 是jquery对象,转化为js对象,
    
                        parseHTML(string, 根节点, boolean)方法:把字符串转成一个节点数组,第二个参数是根节点,第三个表示是否对script也进行转换,
                        true 表示 进行转换,false 不进行
                         praseHTML("<li>1</li><li>2</li><li>3</li>",document,true)  ---->  ['li','li','li']
    
                         merge()方法:将数组进行合并,这里可以将数组合并到json中:var s = {0:'a',1:'b',length:2}, 
                                     进行 merge(s,['c','d'])后,{0:'a',1:'b',2:'c',3:'d',length:4};
    
                        // scripts is true for back-compat
                        jQuery.merge( this, jQuery.parseHTML(
                            match[1],
                            context && context.nodeType ? context.ownerDocument || context : document,
                            true
                        ) );
    
    
                        这里是 插入标签 并添加属性的方式:只针对但标签,<li></li><li></li> 这个就不行,不是单标签,有2个li
                             例如:$("<li>",{title:'hi',html:"这是个li"})
                        // HANDLE: $(html, props)
                        if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
                            //满足单标签 并且 第二个参数 是一个面向字面量对象,即json格式的
                            for ( match in context ) {
                                // Properties of context are called as methods if possible
                                if ( jQuery.isFunction( this[ match ] ) ) {    //判断是不是函数,如果是函数,例如: html
                                    this[ match ]( context[ match ] );        执行 html("这是个li");
    
                                // ...and otherwise set as attributes
                                } else {        
                                    this.attr( match, context[ match ] );        添加个属性:attr('title','hi');
                                }
                            }
                        }
    
                        return this;

    传入上下文:

    context && context.nodeType ? context.ownerDocument || context : document

    ownerDocument和 documentElement的区别

    • ownerDocument是Node对象的一个属性,返回的是某个元素的根节点文档对象:即document对象
    • documentElement是Document对象的属性,返回的是文档根节点
    • 对于HTML文档来说,documentElement是<html>标签对应的Element对象,ownerDocument是document对象

    具体请看API手册

    jQuery.merge( first, second ) 合并两个数组内容到第一个数组。

    jQuery.parseHTML

    使用原生的DOM元素的创建函数将字符串转换为一组DOM元素,然后,可以插入到文档中。

    str = "hello, <b>my name is</b> jQuery.",
    html = $.parseHTML( str ),

    image

    源码:

    复制代码
    parseHTML: function( data, context, keepScripts ) {
        if ( !data || typeof data !== "string" ) {
            return null;
        }
        if ( typeof context === "boolean" ) {
            keepScripts = context;
            context = false;
        }
        context = context || document;
        var parsed = rsingleTag.exec( data ),
            scripts = !keepScripts && [];
        // Single tag
        if ( parsed ) {
            return [ context.createElement( parsed[1] ) ];
        }
        parsed = jQuery.buildFragment( [ data ], context, scripts );
        if ( scripts ) {
            jQuery( scripts ).remove();
        }
        return jQuery.merge( [], parsed.childNodes );
    },
    复制代码

    匹配一个独立的标签

    rsingleTag = /^<(w+)s*/?>(?:</1>|)$/,
    • ^<(w+)s*/?>  : 以<开头,至少跟着一个字符和任意个空白字符,之后出现0或1次/>
    • (?:</1>|)$        : 可以匹配<、一个/或者空白并以之为结尾

          这样如果没有任何属性和子节点的字符串(比如'<html></html>'或者'<div></div>'这样)会通过正则的匹配,当通过正则的匹配后则会通过传入的上下文直接创建一个节点:

    只是单一的标签:

    if ( parsed ) {
            return [ context.createElement( parsed[1] ) ];
     }

    而未通过节点的字符串,则通过创建一个div节点,将字符串置入div的innerHTML:

    parsed = jQuery.buildFragment( [ data ], context, scripts );

    它会把传入的复杂的html转为文档碎片并且存储在jQuery.fragments这个对象里。这里要提一下,document.createDocumentFragment()是相当好用的,可以减少对dom的操作.

    创建一个文档碎片DocumentFragment

    • 如果要插入多个DOM元素,可以先将这些DOM元素插入一个文档碎片,然后将文档碎片插入文档中,这时插入的不是文档碎片,而是它的子孙节点;相比于挨个插入DOM元素,使用文档碎片可以获得2-3倍的性能提升;
    • 如果将重复的HTML代码转换为DOM元素,可以将转换后的DOM元素缓存起来,下次(实际是第3次)转换同样的HTML代码时,可以直接缓存的DOM元素克隆返

    当一个HTML比一个没有属性的简单标签复杂的时候,实际上,创建元素的处理是利用了浏览器的innerHTML 机制。

    复制代码
    1 tmp = tmp || fragment.appendChild( context.createElement("div") );
    2 
    3 // Deserialize a standard representation
    4 tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase();
    5 wrap = wrapMap[ tag ] || wrapMap._default;
    6 tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
    复制代码
    • 特别说明,jQuery创建一个新的<div>元素,并且设置innerHTML属性为传入的HTML代码片段。当参数是一个单标签,就像 $('<img />') or $('<a></a>'),jQuery将使用javasrcipt原生的 createElement()函数创建这个元素。
    • 当传入一个复杂的html,一些浏览器可能不会产生一个完全复制HTML源代码所提供的DOM。正如前面提到的,jQuery使用的浏览器.innerHTML属性来解析传递的HTML并将其插入到当前文档中。在此过程中,一些浏览器过滤掉某些元素,如<html><title>, 或 <head>的元素。其结果是,被插入元素可能不是传入的原始的字符串。
    • 不过,这些被过滤掉的标签有限的。有些浏览器可能不完全复制所提供的HTML源代码生成DOM。例如,Internet Explorer的版本8之前转换所有链接的href属性为绝对URL路径,和Internet Explorer第9版之前,不增加一个单独的兼容层的情况下,将无法正确处理HTML5元素。
    • 为了确保跨平台的兼容性,代码片段必须是良好的。标签可以包含其他元素,但需要搭配的结束标记

     如果第一个参数(HTML字符串)为一个空的单标签,且第二个参数context为一个非空纯对象

    var jqHTML = $('<div></div>', { class: 'css-class', data-name: 'data-val' });
     
    console.log(jqHTML.attr['class']); //css-class
    console.log(jqHTML.attr['data-name']); //data-val

    处理id:

    // HANDLE: $(#id)
                    } else {
                        elem = document.getElementById( match[2] );  //通过js的原生方法,获取id元素
    
                        // Check parentNode to catch when Blackberry 4.6 returns
                        // nodes that are no longer in the document #6963
                        if ( elem && elem.parentNode ) {    //黑莓有个bug,elem不存在,但是能找到,所以为了准确,再判断elem的父节点是否存在
                            // Inject the element directly into the jQuery object
                            this.length = 1;
                            this[0] = elem;
                        }
    
                        this.context = document;
                        this.selector = selector;
                        return this;
                    }

    匹配模式三:$(.className)    $(.className, context)

    如果第一个参数是一个.className,jQuery对象中拥有class名为className的标签元素,

        // HANDLE: $(expr, $(...))
                } else if ( !context || context.jquery ) {    //当执行上下文不存在(第二个参数不存在)或者上下文是jquery对象
                    return ( context || rootjQuery ).find( selector );
    
                // HANDLE: $(expr, context)
                // (which is just equivalent to: $(context).find(expr)
                } else {                                    //当上下文不是jquery对象,this.constructor 就是jquery, 将他转化为jquery对象
                    return this.constructor( context ).find( selector );
                }
                                                                最终都是$(document).find(...);

    匹配 节点模式:  $(this)  $(document)

    } else if ( selector.nodeType ) {    //不管是什么节点,都有nodeType 类型,如果不是节点,就不可能有nodeType 
                this.context = this[0] = selector;
                this.length = 1;
                return this;

    $(jQuery对象)   $(函数function)

    } else if ( jQuery.isFunction( selector ) ) {  //判断是不是函数,$(function(){ })
                return rootjQuery.ready( selector );
            }
                        简写 方式 最终 都是  document.ready(function(){ });
    
                例如: $( $("#id") )  和 $("#id") 是一样的
            if ( selector.selector !== undefined ) {    //判断是不是jquery对象,如果是jquery对象,那么他的selector就不是undefined
                this.selector = selector.selector;
                this.context = selector.context;
            }

    转成json 内部调用的时候:和merge()差不多

    return jQuery.makeArray( selector, this );

    jQuery 构造器

         由此可见,从本质上来说,构建的jQuery对象,其实不仅仅只是dom,还有很多附加的元素,用数组的方式存储,当然各种组合有不一样,但是存储的方式是一样的

    总的来说分2大类:

    • 单个DOM元素,如$(ID),直接把DOM元素作数组传递给this对象
    • 多个DOM元素,集合形式,可以通过CSS选择器匹配是有的DOM元素,过滤操作,构建数据结构

    CSS选择器是通过jQuery.find(selector)函数完成的,通过它可以分析选择器字符串,并在DOM文档树中查找符合语法的元素集合


        toArray: function() {
            return core_slice.call( this );
        },
    
        get: function( num ) {
            return num == null ?
    
                // Return a 'clean' array
                this.toArray() :
    
                // Return just the object
                ( num < 0 ? this[ this.length + num ] : this[ num ] );
        },
    
        pushStack: function( elems ) {      //     $('div').pushStack($('span')).css('background','red');  div和span放在栈中,div先进,span后进,但是先进后出
                                            // 所以后面的sapn背景是红色
                                            //end()方法,就是找到栈的上一层
            // Build a new jQuery matched element setting
            var ret = jQuery.merge( this.constructor(), elems );
    
            // Add the old object onto the stack (as a reference)
            ret.prevObject = this;
            ret.context = this.context;
    
            // Return the newly-formed element set
            return ret;
        },
    
        each: function( callback, args ) {
            return jQuery.each( this, callback, args );
        },
    
        ready: function( fn ) {
            // Add the callback
            jQuery.ready.promise().done( fn );
    
            return this;
        },
    
        slice: function() {    //返回新的包装集
            return this.pushStack( core_slice.apply( this, arguments ) );
        },
    
        first: function() {
            return this.eq( 0 );
        },
    
        last: function() {
            return this.eq( -1 );
        },
    
        eq: function( i ) {
            var len = this.length,
                j = +i + ( i < 0 ? len : 0 );
            return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
        },
    
        map: function( callback ) {
            return this.pushStack( jQuery.map(this, function( elem, i ) {
                return callback.call( elem, i, elem );
            }));
        },
    
        end: function() {
            return this.prevObject || this.constructor(null);
        },
    
        // For internal use only.
        // Behaves like an Array's method, not like a jQuery method.
        push: core_push,
        sort: [].sort,
        splice: [].splice
     $("li","ul")   //选择ul中的li
        $(".cc","#test")    //选择id是test中的class是cc   后面是前面的父元素
  • 相关阅读:
    网络安全分析
    java实现 洛谷 P1464 Function
    java实现 洛谷 P1464 Function
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1014 Cantor表
    java实现 洛谷 P1540 机器
    java实现 洛谷 P1540 机器
  • 原文地址:https://www.cnblogs.com/a-lonely-wolf/p/5701327.html
Copyright © 2020-2023  润新知