zoukankan      html  css  js  c++  java
  • (92-176)

    原型定义

    jQuery.fn = jQuery.prototype = {
        // The current version of jQuery being used
        jquery: version,
    
        constructor: jQuery,
    
        // Start with an empty selector
        selector: "",
    
        // The default length of a jQuery object is 0
        length: 0,
    
        toArray: function() {
            return slice.call( this );
        },
    
        // Get the Nth element in the matched element set OR
        // Get the whole matched element set as a clean array
        get: function( num ) {
            return num != null ?
    
                // Return just the one element from the set
                ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
    
                // Return all the elements in a clean array
                slice.call( this );
        },
    
        // Take an array of elements and push it onto the stack
        // (returning the new matched element set)
        pushStack: function( elems ) {
    
            // Build a new jQuery matched element set
            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;
        },
    
        // Execute a callback for every element in the matched set.
        // (You can seed the arguments with an array of args, but this is
        // only used internally.)
        each: function( callback, args ) {
            return jQuery.each( this, callback, args );
        },
    
        map: function( callback ) {
            return this.pushStack( jQuery.map(this, function( elem, i ) {
                return callback.call( elem, i, elem );
            }));
        },
    
        slice: function() {
            return this.pushStack( 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] ] : [] );
        },
    
        end: function() {
            return this.prevObject || this.constructor(null);
        },
    
        // For internal use only.
        // Behaves like an Array's method, not like a jQuery method.
        push: push,
        sort: arr.sort,
        splice: arr.splice
    };

    1. jquery: version 版本信息, constructor: jQuery 原型的构造函数属性修改为函数jQuery,selector: "", 属性存放选择器,length存放jQuery对象的长度,将jQuery对象模仿成数组对象,使得可以像处理数组那样处理jQuery对象。

    2.将类数组转化为数组

    //模仿出一个类数组对象
    var a = { 0: 2, 1: 4, 2: 6,length:3}
    console.log(Array.prototype.slice.call(a));//[ 2, 4, 6 ]

    jQuery对象是一个类数组对象,我们可以用slice将其转化为数组。

    3.get方法

    var arr = [],
    slice = arr.slice;
    //提取出get,这个操作会覆盖原代码中的get    
    jQuery.fn.get =  function( num ) {
            return num != null ?
    
                // Return just the one element from the set
                ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
    
                // Return all the elements in a clean array
                slice.call( this );
    }
    var $div = $('div')
    //正数和0
    //这个情况下与[]访问符效果一样
    console.log($div.get(0) === $div[0])
    
    //负数位置
    console.log($div.get(-1))
    
    //为空时转化为将对象数组,
    console.log($div.get())

    这里先讲一下三目运算符[con]?[code] :[code];三目预算符等价于If(){}else(){}。

    if( a != null ){
        if( a<0 ){
            this[ num + this.length ]
        }else{
            this[ num ] 
        }
    }else{
        slice.call( this )
    }
    
    //等价于
    a != null ?( num < 0 ? this[ num + this.length ] : this[ num ] ) :slice.call( this );

    相比较,功能一样,代码的量缩小,可读性变差。自己取舍。

    在负数情况下,length+index  = 元素的位置。

    4.jQuery对象栈。

    var $ = {0:1,prev: Object.prototype}
    console.log($[0])
    $ = {0:2,prev:$}
    console.log($[0])
    $ = {0:3,prev:$}
    console.log($[0])
    
    //回溯
    $ = $.prev
    console.log($[0])

    在这个例子中,我们通过在新对象中存储对上一对象的引用,可以实现栈式的回溯访问。

    jQuery对象在遍历功能中运用了这一原理,在遍历操作后返回的是一个新的jQuery对象,这个新的对象存在对上一对象的引用,以实现链式的回溯访问。

    下面来看jQuery中的实现

    var slice = [].slice;
    jQuery.fn.pushStack = function( elems ) {
        // Build a new jQuery matched element set
        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;
    }
    jQuery.fn.slice = function() {
            return this.pushStack( slice.apply( this, arguments ) );
    }
    jQuery.merge = function( first, second ) {
            var len = +second.length,
                j = 0,
                i = first.length;
    
            for ( ; j < len; j++ ) {
                first[ i++ ] = second[ j ];
            }
    
            first.length = i;
    
            return first;
    }
    jQuery.fn.end = function() {
            return this.prevObject || this.constructor(null);
    }
    console.log($('li').slice(2,4).css('background-color','red').end());

    这里pushStack扮演的角色就是设置一个对上个对象引用的属性,因为它不是初始化的jQuery对象,我们还要设置一下它的上下文context, merge 的作用是合并对象设置length。

     end 的作用是返回上个对象,当没有这个属性就构造一个空jQuery对象(because  constructor: jQuery, line 96)。

    总得来说,遍历是为了对当前已存在jQuery对象筛选,采用栈的原因是方便我们在链式调用中回溯,这要比重新构造jQuery对象来得快的多。

    5.从代码中可以很清楚的看到jQuery.fn.each实际就是jQuery.each方法第一个参数传入this,因为这个方法在很早很频繁的使用,所以在这里就先来分析。

    //simple
    function isArraylike(obj){
        return obj.length - 1 in obj;
    }
    // args is for internal usage only
    jQuery.each = function( obj, callback, args ) {
            var value,
                i = 0,
                length = obj.length,
                isArray = isArraylike( obj );
    
            if ( args ) {
                if ( isArray ) {
                    for ( ; i < length; i++ ) {
                        value = callback.apply( obj[ i ], args );
    
                        if ( value === false ) {
                            break;
                        }
                    }
                } else {
                    for ( i in obj ) {
                        value = callback.apply( obj[ i ], args );
    
                        if ( value === false ) {
                            break;
                        }
                    }
                }
    
            // A special, fast, case for the most common use of each
            } else {
                if ( isArray ) {
                    for ( ; i < length; i++ ) {
                        value = callback.call( obj[ i ], i, obj[ i ] );
    
                        if ( value === false ) {
                            break;
                        }
                    }
                } else {
                    for ( i in obj ) {
                        value = callback.call( obj[ i ], i, obj[ i ] );
    
                        if ( value === false ) {
                            break;
                        }
                    }
                }
            }
    
            return obj;
    };
    
    var a1 = [1,2,3],
      a2 = {0:1,1:2,2:3,length:3},
      a3 = {a:1,b:2,c:3};
    jQuery.each(a1,function(i,el){
        console.log(i+':'+el)
    })
    jQuery.each(a2,function(i,el){
        console.log(i+':'+el)
    })
    jQuery.each(a3,function(i,el){
        console.log(i+':'+el)
    })

    情况分析:

    ~带参数情况

    ~数组及类数组情况

    ~对象情况

    ~如果返回false则终止遍历

    效率分析:

    ~for in 的效率比for(var i =0;i<arr.length;i++)低很多

    ~forEach不适用?

    这里的 isArray 实际指的是数组和类数组(这也是类数组对象的优点)。一个大的if..else将代码分成两个模块,代码基本一样,作者注释到带参数仅内部使用。先来讨论不带参数的情况。

    进行分支,然后当数组类数组时,用for循环遍历, callback.call( obj[ i ], i, obj[ i ] ) ,利用call方法,指定this为当前对象并调用回调函数,后面的参数依次是i位置,objec[i]当前对象。因为有一个false终止遍历功能,用判断然后break;。对象的情况一样,只是循环时采用for in。

    回到最初,含有参数时,与不带参数的情况基本一样,但关键处, callback.apply( obj[ i ], args ) 回调函数调用的参数完全不同,这里用的是apply方法。


    6.map和each很相似,但map的侧重点是对返回值处理,each是对每个对象执行一个函数。

    //因为我们将方法提取出来,很多辅助的方法访问不到,我们重新定义。
    var arr = [];
    concat = arr.concat;
    
    function isArraylike( obj ) {
        var length = obj.length,
            type = jQuery.type( obj );
    
        if ( type === "function" || jQuery.isWindow( obj ) ) {
            return false;
        }
    
        if ( obj.nodeType === 1 && length ) {
            return true;
        }
    
        return type === "array" || length === 0 ||
            typeof length === "number" && length > 0 && ( length - 1 ) in obj;
    }
    jQuery.fn.map = function( callback ) {
            return this.pushStack( jQuery.map(this, function( elem, i ) {
                return callback.call( elem, i, elem );
            }));
    };
    // arg is for internal usage only
    jQuery.map =  function( elems, callback, arg ) {
            var value,
                i = 0,
                length = elems.length,
                isArray = isArraylike( elems ),
                ret = [];
    
            // Go through the array, translating each of the items to their new values
            if ( isArray ) {
                for ( ; i < length; i++ ) {
                    value = callback( elems[ i ], i, arg );
    
                    if ( value != null ) {
                        ret.push( value );
                    }
                }
    
            // Go through every key on the object,
            } else {
                for ( i in elems ) {
                    value = callback( elems[ i ], i, arg );
    
                    if ( value != null ) {
                        ret.push( value );
                    }
                }
            }
    
            // Flatten any nested arrays
            return concat.apply( [], ret );
    };
    console.log($(':checkbox').map(function() {
        return this.id;
    }).get().join())

    情况分析:

    ~数组及类数组情况

    ~对象情况

    ~value不存在

    效率分析:

    ~for in 的效率比for(var i =0;i<arr.length;i++)低很多

    情况和each基本一样,关键地方, if ( value != null ) 判断值不存在的情况, this.pushStack 加入堆栈。


    这里有一个技巧,因为apply的第二个参数为数组,利用它可以将先前收集的数据数组(方便收集数据)当作数据处理,

    var arr = [];
    concat = arr.concat;
    var ret = [[1,2],[3,4],5];
    console.log([].concat(1,[2,3]))
    console.log([].concat(ret)) //不能深入合并
    console.log(concat.apply([],ret)) //可以

     7.slice先前讲过,下面主要来讲eq

    eq和get方法类同,区别有两个地方,一个是对不传入参数时及溢出的处理方式,一个是eq的结果将进行pushStack处理

    jQuery.fn.eq = function( i ) {
            var len = this.length,
                j = +i + ( i < 0 ? len : 0 );
            return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
    }
    console.log($('div').eq(0))

    因为两个方法判断部分相似,如果不考虑不传入参数及溢出的情况。可以直接调用get方法。

    jQuery.fn.eq = function(i){
        return this.pushStack([this.get(i)])
    }
    console.log($('div').eq(1))

    最后作者存储push,sort,splice数组方法。

  • 相关阅读:
    使用异或解题 —— 序列中仅出现一次的两个数
    使用异或解题 —— 序列中仅出现一次的两个数
    希尔排序
    java中接口的定义与实现
    erlang工作前新手学习指引路线
    NAT的全然分析及其UDP穿透的全然解决方式
    hibernate官方新手教程 (转载)
    git与svn的不同
    C#调用GDAL算法进度信息传递
    atitit. access token是什么??微信平台公众号开发access_token and Web session保持状态机制
  • 原文地址:https://www.cnblogs.com/winderby/p/4151252.html
Copyright © 2011-2022 走看看