美文网首页
JQuery源码阅读(二)extend和补充内容

JQuery源码阅读(二)extend和补充内容

作者: Atlas_lili | 来源:发表于2019-04-06 20:12 被阅读0次

本来想看jQuery.makeArray,看到了jQuery.extend,其中扩充了好多方法属性,看来要优先一下。

源码声明makeArray.png

jQuery.extend

jQuery.extend = jQuery.fn.extend = function() {
  var options, name, src, copy, copyIsArray, clone,
    target = arguments[ 0 ] || {}, // 合并对象默认为空对象
    i = 1,
    length = arguments.length, // 参数个数
    deep = false; // 拷贝类型
  if ( typeof target === "boolean" ) {
    deep = target;
    target = arguments[ i ] || {};
    i++;
  }
  // Handle case when target is a string or something (possible in deep copy)
  if ( typeof target !== "object" && !isFunction( target ) ) {
    target = {};
  }
  // Extend jQuery itself if only one argument is passed
  if ( i === length ) {
    target = this;
    i--;
  }
  for ( ; i < length; i++ ) {
    // do arguments[i]
  }
  return target;
};

isFunction

  1. target是布尔值:作为拷贝类型,target到下一个参数。
  2. target非对象非函数:作为拷贝类型,target到下一个参数。
  3. i越界,i--,target赋为调用对象。
  4. 遍历剩余参数,返回target。

遍历的时候做了什么呢?

for ( ; i < length; i++ ) {
  if ( ( options = arguments[ i ] ) != null ) {
    for ( name in options ) {
      src = target[ name ];
      copy = options[ name ];
      if ( target === copy ) {
        continue;
      }
      // Recurse if we're merging plain objects or arrays
      if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
        ( copyIsArray = Array.isArray( copy ) ) ) ) {
      //  深拷贝
      } else if ( copy !== undefined ) {
        target[ name ] = copy;
      }
    }
  }
}

注意这一段if ( target === copy ) {continue;}当容器和要拷贝的项是相同引用时,就跳过这一项。浅拷贝的操作就是将拷贝项对到容器的属性中。其中有一段引人注目赋值语句的返回值就是语句的右值

深拷贝段
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
    ( copyIsArray = Array.isArray( copy ) ) ) ) {
    if ( copyIsArray ) {
        copyIsArray = false;
        clone = src && Array.isArray( src ) ? src : [];
    } else {
        clone = src && jQuery.isPlainObject( src ) ? src : {};
    }
    // Never move original objects, clone them
    target[ name ] = jQuery.extend( deep, clone, copy );
}

对于冲突属性,递归将这两个属性传入extend。
JQ的合并对象竟然是用递归实现的,非递归的手动压栈应该性能更棒!

isFunction

var isFunction = function isFunction( obj ) { 
      // 判断参数是不是函数,避免DOM节点参数的特殊情况。
      return typeof obj === "function" && typeof obj.nodeType !== "number";
  };

除了直观的typeof,规避一种特殊的情况DOM节点在老IE判为function
现在我们可以做对象合并了,就能聊更多扩充方法了。

jQuery.isPlainObject

jQuery.extend( {
  isPlainObject: function( obj ) {
    var proto, Ctor;
    if ( !obj || toString.call( obj ) !== "[object Object]" ) {
      return false;
    }
    proto = getProto( obj );
    // Objects with no prototype (e.g., `Object.create( null )`) are plain
    if ( !proto ) {
      return true;
    }
    // Objects with prototype are plain iff 
    // they were constructed by a global Object function
    Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
    return typeof Ctor === "function" &&
      fnToString.call( Ctor ) === ObjectFunctionString;
  }
} );

toString getProto hasOwn fnToString ObjectFunctionString

判断是否是基类生成对象或无原型的纯净对象

toString fnToString hasOwn getProto ObjectFunctionString
var class2type = {};

var toString = class2type.toString;

var hasOwn = class2type.hasOwnProperty;

var fnToString = hasOwn.toString;

看到这两个函数都是熟悉的面孔。toString就是Object基类原型的toString,fnToString是函数的toString,hasOwn是一个监测是否是对象本身属性(非原型)的函数。

var ObjectFunctionString = fnToString.call( Object );

ObjectFunctionString 不是别的就是'function Object() { [native code] } '这样一个字符串。

var getProto = Object.getPrototypeOf;

是一个返回参数的原型的函数。

所以jq判断一个对象的纯净考虑的方面:toString是不是Object、有没有原型、原型里的constructor是不是Object()

jQuery.makeArray

jQuery.extend( {
    makeArray: function( arr, results ) {
        var ret = results || [];
        if ( arr != null ) {
            if ( isArrayLike( Object( arr ) ) ) {
                jQuery.merge( ret,
                    typeof arr === "string" ?
                    [ arr ] : arr
                );
            } else {
                push.call( ret, arr );
            }
        }
        return ret;
    }
} );

push = arr.push

Object()传入字符串返回的类数组对象
results 可以作为合并类数组的数组容器,非类数组直接调用push函数。

isArrayLike
function isArrayLike( obj ) {
    // Support: real iOS 8.2 only (not reproducible in simulator)
    // `in` check used to prevent JIT error (gh-2145)
    // hasOwn isn't used here due to false negatives
    // regarding Nodelist length in IE
    var length = !!obj && "length" in obj && obj.length,
        type = toType( obj );
    if ( isFunction( obj ) || isWindow( obj ) ) {
        return false;
    }
    return type === "array" || length === 0 ||
        typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}

又有许多的生面孔。。。。。。一个一个来
toType
主要为了规避null和function的特殊显示

function toType( obj ) {
    if ( obj == null ) {
        return obj + "";
    }
    // Support: Android <=2.3 only (functionish RegExp)
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[ toString.call( obj ) ] || "object" :
        typeof obj;
}

isWindow
判断参数是不是window对象

var isWindow = function isWindow( obj ) {
    return obj != null && obj === obj.window;
};

现在回顾判断类数组的思路,是不是狭义的对象,length属性是否是自然数,length-1是不是在参数结构内。

jQuery.merge

jQuery.extend( {
    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;
    }
} );

如果作为一个数组拼接的方法,原生拼接的方法就可以(concat、push),我想到了jq对象,拼接DOM集合。重要的区别:该方法改变第一参数


至此,前面挖的坑多数都填上了,至于后面的顺序我准备参照某大佬的顺序。
补充一点,第一篇的find(),找到了。

jQuery.find = Sizzle;

引用一下大佬的话:find 函数实际上是 Sizzle,已经单独出来一个项目,被在 jQuery 中直接使用,将在下章介绍 jQuery 中的 Sizzle 选择器。

相关文章

网友评论

      本文标题:JQuery源码阅读(二)extend和补充内容

      本文链接:https://www.haomeiwen.com/subject/snqkiqtx.html