zoukankan      html  css  js  c++  java
  • jQuery源码学习7——实例成员

    选择器部分的代码实在很复杂,过后再看jQuery.init用到了一些实例方法,因此先看一下实例方法再回过头看init

    源码中jQuery构造函数定义完之后添加的实例成员有:

    jquery size get each index attr css text wrap append prepend before after end find clone filter not add is domManip pushStack

    (1)jquery:存储当前jQuery的版本

    (2)size:存储当前实力化对象的length属性,感觉应该是存放选取到的元素的个数

    (3)get

        get: function( num ) {
            if ( num && num.constructor == Array ) {
                this.length = 0;
                [].push.apply( this, num );
                return this;
            } else
                return num == undefined ?
                    jQuery.map( this, function(a){ return a } ) :
                    this[num];
        },

    if里面很明显是数组的情况,刚开始实在搞不清楚什么时候能进这个分支

    看了好久才发现在jQuery构造函数定义的时候里面调用get方法时会传入数组:

        // Watch for when an array is passed in
        this.get( a.constructor == Array || a.length && !a.nodeType && a[0] != undefined && a[0].nodeType ?
            // Assume that it is an array of DOM Elements
            jQuery.merge( a, [] ) :
    
            // Find the matching elements and save them for later
            jQuery.find( a, c ) );

    在这个if分支里面先初始化了length属性为0

    接下来的这句有点意思:[].push.apply( this, num )

    其实是把数组push方法内部的this强制改为了当前环境下的this

    而apply的第二个参数是一个数组形式,所以把document放入数组中就是num

    最后this就变成了

        {
            "0":document,
            "length":1
        }

    如果num传入的就是数字类型的话那就好办了

    直接走else分支返回对应的对象

    如果直接$(".div1").get()就会得到$(".div1")选中的元素的数组

    (4)each 实际上是调用了静态方法$.each 第一个参数是选择器选中的元素

    (5)index

        index: function( obj ) {
            var pos = -1;
            this.each(function(i){
                if ( this == obj ) pos = i;
            });
            return pos;
        },

    得到传入的obj在当前选中的元素集合中(this)的位置

    由于each方法在定义的时候改变了其传入的参数中的this的指向,所以each的函数参数的函数体里面的this指向的是当前遍历的元素

    (6)attr

        attr: function( key, value, type ) {
            // Check to see if we're setting style values
            return key.constructor != String || value != undefined ?
                this.each(function(){
                    // See if we're setting a hash of styles
                    if ( value == undefined )
                        // Set all the styles
                        for ( var prop in key )
                            jQuery.attr(
                                type ? this.style : this,
                                prop, key[prop]
                            );
                    
                    // See if we're setting a single key/value style
                    else
                        jQuery.attr(
                            type ? this.style : this,
                            key, value
                        );
                }) :
                
                // Look for the case where we're accessing a style value
                jQuery[ type || "attr" ]( this[0], key );
        },

    这个方法的用法貌似有以下几种

    $(".div").attr("abc")            //设置有.div类的abc属性      //直接走else 而且type不存在所以就调用jQuery.attr($(".div")[0],"key")得到其key属性

    $(".div").attr("abc","123")          //设置有.div类的abc属性值为"123"  //走if 内层判断走else 设置单个属性值

    $(".div").attr({"abc":"123","index":"456"}) //设置多个属性            //走if 内层判断走if 设置多个属性值

    此外在源码中还发现一种用法:

    $(".div").attr("style","100px; height:100px; background:red;","curCSS") //设置style属性 走if 内层判断走else 设置多个样式值

    而且貌似最后一个参数type和设置style属性有关

    (7)css 直接调用了attr方法

    (8)text

        text: function(e) {
            e = e || this;
            var t = "";
            for ( var j = 0; j < e.length; j++ ) {
                var r = e[j].childNodes;
                for ( var i = 0; i < r.length; i++ )
                    t += r[i].nodeType != 1 ?
                        r[i].nodeValue : jQuery.fn.text([ r[i] ]);
            }
            return t;
        },

    这个text方法貌似只能获取一个元素里面的值而不能设置

    目测参数e得是类数组对象,而且这个数组对象里面的每一项都是DOM元素

    所以希望通过$("#div .div").text(123)来将#div下的.div标签里面的值改为123是不太可能的

    如果HTML是

        <div id="div1">
            <div class="div1">abc</div>
        </div>

    那通过$("#div1").text()得到的将是#div1里面所有文本节点拼接起来的字符串

    但是个人感觉一般来讲,这个字符串没什么太大意义

    (9)wrap

        wrap: function() {
            var a = jQuery.clean(arguments);
            return this.each(function(){
                var b = a[0].cloneNode(true);
                this.parentNode.insertBefore( b, this );
                while ( b.firstChild )
                    b = b.firstChild;
                b.appendChild( this );
            });
        },

    wrap方法貌似是将选择器选到的元素都用传入的DOM对象包围

    例如,HTML是

        <div id="div1">
            <div class="div1"></div>
            <div class="div1"></div>
            <div class="div1"></div>
            <div class="div1"></div>
        </div>
        <div class="div2"></div>

    执行$("#div1 .div1").wrap($(".div2"));之后就得到了

        <div id="div1">
            <div class="div2"><div class="div1"></div></div>
            <div class="div2"><div class="div1"></div></div>
            <div class="div2"><div class="div1"></div></div>
            <div class="div2"><div class="div1"></div></div>
        </div>
        <div class="div2"></div>

    由于wrap内部调用了$.clean来处理传进来的参数

    因此$.clean能处理的参数形式wrap也能处理

    但是传进来的参数一定不能是带有文本节点的,即不能传进来类似

    <div>
        abc
    </div>

    因为wrap内部拿到这个DOM参数对象之后会一直遍历到这个DOM元素的最里层

    在最里层append调用wrap方法的选择器中的对象

    如果传入文本节点,肯定没办法append了

    (10)domManip append prepend before after

    append prepend before after都是调用了domManip,所以这是一个核心方法

        domManip: function(args, table, dir, fn){
            var clone = this.size() > 1;
            var a = jQuery.clean(args);
            
            return this.each(function(){
                var obj = this;
                
                if ( table && this.nodeName == "TABLE" && a[0].nodeName != "THEAD" ) {
                    var tbody = this.getElementsByTagName("tbody");
    
                    if ( !tbody.length ) {
                        obj = document.createElement("tbody");
                        this.appendChild( obj );
                    } else
                        obj = tbody[0];
                }
    
                for ( var i = ( dir < 0 ? a.length - 1 : 0 );
                    i != ( dir < 0 ? dir : a.length ); i += dir ) {
                        fn.apply( obj, [ clone ? a[i].cloneNode(true) : a[i] ] );
                }
            });
        },

    再看它的调用形式

        append: function() {
            return this.domManip(arguments, true, 1, function(a){
                this.appendChild( a );
            });
        },

    参数arguments应该是一个形如["<div><div>"]或者[oDiv]或者[$("#div")]的数组

    再通过clean方法的处理变成一个纯粹的数组

    第二个参数true/false代表是否对<table>做处理

    dir代表domManip里面的循环是正序还是倒序

    最后一个参数function的话是对当前调用domManip的this实例化对象选择到的元素集合的各项做的操作

    从append里面调用domManip的最后一个参数的情况来看

    这个函数将来在调用的时候一定会通过apply或call改变其内部this的指向

    因为这个function直接调用的话,this必然指向window

    但是window是没有appendChild这个方法的

    因此this的指向一定会改成遍历到的DOM对象

    再看domManip的源码

    这个domManip的第一行就让我废了半天劲去看clone这个变量到底有什么用

    结果也没有发现,目前猜测是为了兼容低版本浏览器

    因为clone里面存储的是要执行append的jQuery对象选择到的元素的长度

    而这个clone变量到了最后遍历数组a,将a中的每个元素都添加到每个元素下面的时候才用到

    如果clone是true的话(即选择器至少2个元素)就将待添加的节点新克隆一个再append进去

    如果clone是false的话(即选择器只有1个元素)就将待添加的节点直接append进去

    经过测试没什么区别

    中间对args中的表格元素的情况作了处理

    再回头来看append和prepend

    append内部调用的是appendChild方法,是直接往后面添加的

    而prepend内部调用的是insertBefore方法,是往最前面添加的

    挨个添加时append顺序添加就可以了,所以参数dir为1

    而prepend的时候要想按顺序添加上去就需要先将最后一个元素insertBefore到最前面

    再将倒数第二个元素insertBefore到最前面

    以此类推,知道添加完

    before和after也是差不多的

    (11)end 字面上看起来好像是获取选择器中所有元素的最后一个元素,不过直接调用的话会报错,从源码中也很容易看出错误的原因:this.stack没有值

    所以目测end方法是要和其他方法配合使用

    (12)pushStack

    pushStack是实例化方法里面继domManip之后的另一个重要的核心方法

        pushStack: function(a,args) {
            var fn = args && args[args.length-1];
    
            if ( !fn || fn.constructor != Function ) {
                if ( !this.stack ) this.stack = [];
                this.stack.push( this.get() );
                this.get( a );
            } else {
                var old = this.get();
                this.get( a );
                if ( fn.constructor == Function )
                    return this.each( fn );
                this.get( old );
            }
    
            return this;
        }

     分析了一下pushStack的调用方式,大概见到以下几种:

    find方法中:this.pushStack([oDiv],[".div1"]);

    clone方法中:this.pushStack([oDiv],[true]);

    filter方法中:this.pushStack([oDiv]);

    not方法中:this.pushStack([oDiv],".div1");以及this.pushStack([oDiv],document.getElementById("div8"));

    add方法中:this.pushStack([oDiv],[[$("#div1"),$("#div2")]]);

    通过以上分析,发现pushStack的功能正如它的名字——入栈

    每次在一个jQuery对象上进行add find filter等操作时,都会把当前的jQuery对象push到stack属性中

    stack属性值中的最后一项就是最近一次操作的效果

    不过里面调用的this.get(0)和this.get(a)不太清楚是做什么的,而且经过this.get(0)这么一处理,上面的not调用方式也是有问题的

    从pushStack这个方法的else分支中发现貌似第二个参数还可以传入函数

    但是这种方式目前还没有见过在哪里用到过

    至此为止的话,最开始初始化的这些实例化方法基本上都分析的差不多了

  • 相关阅读:
    使用阿里的EasyExcel实现表格导出功能
    推荐一款实用的java工具包---Hutool
    MySQL(二)锁机制【表级锁、页面锁、行级锁】
    MySQL(一)存储引擎
    使用redis的increment()方法实现计数器功能
    Redis缓存浅析
    Dubbo服务介绍
    SpringMVC工作执行流程详解
    GC垃圾回收机制----GC回收算法(GC机制必会知识点)
    数据结构之常见的数据结构
  • 原文地址:https://www.cnblogs.com/zhaohuiziwo901/p/4972432.html
Copyright © 2011-2022 走看看