JQuery源码解析–extend篇

来源:互联网 发布:发票作废 上传数据 编辑:程序博客网 时间:2024/06/08 17:59

JQuery源码解析–extend篇

@前端攻城狮LaoXu

目录

  • JQuery源码解析extend篇
    • 流程分析
    • JQuery源码部分
    • extend分析
      • extend传递一个参数
        • boolean类型
        • string类型
        • number类型
        • Object类型
        • Function类型
        • Array类型
      • extend传递两个参数
        • 决定性的参数arguments0
      • extend传递多个参数
        • 特殊情况的说明针对Boolean
    • extend特点

流程分析

这里写图片描述

JQuery源码部分

说明:JQuery版本–v1.11.3 jQuery JavaScript Library v1.11.3

jQuery.extend = jQuery.fn.extend

jQuery.extend = jQuery.fn.extend = function() {    var src, copyIsArray, copy, name, options, clone,        target = arguments[0] || {},        i = 1,        length = arguments.length,        deep = false;    // 在这里是处理深拷贝的方法    if ( typeof target === "boolean" ) {        deep = target;        // 跳过boolean和target值(不要去管deep的Boolean类型)        target = arguments[ i ] || {};        i++;    }    // 处理当target是一个字符串或者是其他的值时(也可能需要深拷贝处理)    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {        target = {};    }    // 如果只传递一个参数时,只是去扩展JQuery本身(为JQuery添加插件)    if ( i === length ) {        target = this;        i--;    }    for ( ; i < length; i++ ) {        // 仅处理 非空/未定义 的值        if ( (options = arguments[ i ]) != null ) {            // 扩展基本(目标/源)对象            for ( name in options ) {                src = target[ name ];                copy = options[ name ];                // 防止出现无限循环                if ( target === copy ) {                    continue;                }                //如果我们合并的是对象或者是数组,则采用递归的方式来遍历                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {                    if ( copyIsArray ) {                        copyIsArray = false;                        clone = src && jQuery.isArray(src) ? src : [];                    } else {                        clone = src && jQuery.isPlainObject(src) ? src : {};                    }                    // 对它们进行克隆,并且不去改变原始的对象                    target[ name ] = jQuery.extend( deep, clone, copy );                // 不要去引入未定义的值                } else if ( copy !== undefined ) {                    target[ name ] = copy;                }            }        }    }    // 返回修改后的对象    return target;};

extend分析

在分析代码之前先整理一下内部的变量值,变量值的具体说明如下:

var src, copyIsArray, copy, name, options, clone,        target = arguments[0] || {},// traget为传递的第一个参数或者为{}        i = 1,// i为数值1        length = arguments.length,// length 长度是传递的参数的长度        deep = false;// deep表示是否深拷贝

extend传递一个参数

boolean类型

  • 当第一个参数为boolean类型值为true时:

    1. 当第一个参数为true时,当前的变量target的值是传递过来的第一个参数(第3行),也就是boolean类型的值。在执行到 第9行 if ( typeof target === "boolean" ) 并进入到判断体内 ,在此内部会将deep设置为true。而target变量会将传递的第二个参数设置进去,但是并没有去传递第二个参数,则会默认设置为{},此时的i会进行++i的操作,i
    2. 代码继续向下执行会来到 18行 if ( typeof target !== "object" && !jQuery.isFunction(target) )的判断,此时的target是一个{},所以条件不成立,继续往下执行。
    3. 执行到 23行 if ( i === length ) 的判断时,此时的i已经变成2,而length是传递的参数的长度(1),同样条件也不成立。
    4. 代码继续向下执行到 28行 for ( ; i < length; i++ ),此时的i2,而length1,则for循环内的判断条件不成立。
    5. 最后代码将执行到 63行 return target;,此时的target{},因此将返回一个{}回来。
  • 当第一个参数为boolean类型值为false时:

    1. 当传递的参数为false的时候在 第3行target = arguments[0] || {}时,target的值将为{},只有 第18行 if ( typeof target !== "object" && !jQuery.isFunction(target) )和 第 23行 if ( i === length )条件成立执行完 18行 target变为{},在执行 23行 循环体内的方法后target值变为jQuery,i执行i--变为0
    2. 向下继续执行for循环,因为i0length1所以执行循环体中的方法。
    3. 继续向下执行到 第30行if ( (options = arguments[ i ]) != null ),取出第一个参数的值并赋值给options(boolean类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用in关键字循环去取出options中的值(optionsfalse时取出来name的为undefined),当nameundefined时直接跳出for循环执行一次循环。
    4. 最后执行完循环体后返回target(jQuery)

string类型

  • 当第一个参数为string类型时:
    1. 当第一个参数为string时,当前的变量target的值是传递过来的第一个参数(第3行),也就是string类型的值。在执行到 第9行 if ( typeof target === "boolean" )时,条件并不成立,继续往下执行。
    2. 继续向下执行来到 第18行 if ( typeof target !== "object" && !jQuery.isFunction(target) ),此时的targetstring类型的参数,条件成立,进入到条件体并将target设置为{}
    3. 代码继续向下执行将来到 第23行 if ( i === length ),此时的i1length同样也是1,所以条件成立,执行条件体内的代码,此时的target变成this(jQuery)i执行i--的操作变为0
    4. 当执行到 第28行 for ( ; i < length; i++ ),此时的i为0,length为1,循环体内的条件成立,代码进入到循环体内。
    5. 继续向下执行到 第30行if ( (options = arguments[ i ]) != null ),取出第一个参数的值并赋值给options(string类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用in关键字循环去取出options中的值(optionsstring时取出来name的为下标值)在执行 第33行 src = target[ name ];第34行 copy = options[ name ];srcundefinedsrc此时是去取this(jQuery)中的值),copy为每次循环后的参数。
    6. 当代码执行到 第42行 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) )时,deepfalsecopy为每次循环取出来的参数,而copyIsArrayfalse条件不成立,去执行else if中的方法,此时copy有值,所以进入到循环体内,并将copy中的值赋值给target[name]中去 (当为string时,name为下标值)。
    7. 最后执行完循环体后返回target(jQuery)

number类型

  • 当第一个参数为number类型
    1. 当传递的参数为number的时候,只有 第18行 if ( typeof target !== "object" && !jQuery.isFunction(target) )和 第 23行 if ( i === length )条件成立执行完 18行 target变为{},在执行 23行 循环体内的方法后target值变为jQuery,i执行i--变为0
    2. 向下继续执行for循环,因为i0length1所以执行循环体中的方法。
    3. 继续向下执行到 第30行if ( (options = arguments[ i ]) != null ),取出第一个参数的值并赋值给options(number类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用in关键字循环去取出options中的值(optionsnumber时取出来name的为undefined),当nameundefined时直接跳出for循环执行一次循环。
    4. 最后执行完循环体后返回target(jQuery)

Object类型

  • 当传递的参数为Object类型
    1. 当传递的参数为object时,只有第 23行 if ( i === length )条件成立,在执行 23行 循环体内的方法后target值变为jQuery,i执行i--变为0
    2. 向下继续执行for循环,因为i0length1所以执行循环体中的方法。
    3. 继续向下执行到 第30行if ( (options = arguments[ i ]) != null ),取出第一个参数的值并赋值给options(object类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用in关键字循环去取出options中的值(optionsobject时取出来name的为对应的属性名),先找到对应target上是否存在对应的属性值并赋值给src,在去找opations中对应的属性值并赋值给copy
    4. deep不为true时(表示深拷贝),为target添加相应的属性name并将copy中的值(通过name找到的属性值)赋值给target[name]中去。
    5. 最后返回target

Function类型

  • 当第一个参数为Function类型
    1. 当传递的参数为Function的时候,只有第 23行 if ( i === length )条件成立,在执行 23行 循环体内的方法后target值变为jQuery,i执行i--变为0
    2. 向下继续执行for循环,因为i0length1所以执行循环体中的方法。
    3. 继续向下执行到 第30行if ( (options = arguments[ i ]) != null ),取出第一个参数的值并赋值给options(function类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用in关键字循环去取出options中的值(optionsfunction时取出来name的为undefined),当nameundefined时直接跳出for循环执行一次循环。
    4. 最后执行完循环体后返回target(jQuery)

Array类型

  • 当第一个参数为Array类型时:
    1. 当第一个参数为array时,当前的变量target的值是传递过来的第一个参数(第3行),也就是string类型的值。在执行到 第9行 if ( typeof target === "boolean" )时,条件并不成立,继续往下执行。
    2. 继续向下执行来到 第18行 if ( typeof target !== "object" && !jQuery.isFunction(target) ),此时的targetarray类型的参数,条件不成立。
    3. 代码继续向下执行将来到 第23行 if ( i === length ),此时的i1length同样也是1,所以条件成立,执行条件体内的代码,此时的target变成this(jQuery)i执行i--的操作变为0
    4. 当执行到 第28行 for ( ; i < length; i++ ),此时的i为0,length为1,循环体内的条件成立,代码进入到循环体内。
    5. 继续向下执行到 第30行if ( (options = arguments[ i ]) != null ),取出第一个参数的值并赋值给options(array类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用in关键字循环去取出options中的值(optionsarray时取出来name的为下标值)在执行 第33行 src = target[ name ];第34行 copy = options[ name ];srcundefinedsrc此时是去取this(jQuery)中的值),copy为每次循环后的参数。
    6. 当代码执行到 第42行 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) )时,deepfalsecopy为每次循环取出来的参数,而copyIsArrayfalse条件不成立,去执行else if中的方法,此时copy有值,所以进入到循环体内,并将copy中的值赋值给target[name]中去 (当为array时,name为下标值)。
    7. 最后执行完循环体后返回target(jQuery)

extend传递两个参数

说明:根据上面整理出来的结果基本上可以知道extend的内部执行流程,那么在传递两个参数时就无非有一些比较特殊的情况,接下来主要对几种特殊的情况加以说明,如果有不懂的地方可以通过浏览器的调试工具去查看代码的具体流程。

决定性的参数arguments[0]

  • arguments[0](传递的第一个参数)有着举足轻重的作用,它直接决定了返回的结果。

    1. arguments[0]boolean类型的时候它将决定target{}还是this。并且会将第二个参数以对应的方式取出来存放到target中去。
    2. arguments[0]string/number类型的时候它都会返回一个{}值,而{}中的具体信息取决于第二个参数:
      • Boolean:返回空{}对象
      • String: 返回{}对象,并将stirng字符串(分割)存入对象中去,属性就是对于的下标值,属性值就是取出来的值。
      • Number:返回空{}对象
      • Object:返回Object对象
      • Function : 返回空{}对象
      • Array: 将Array中的值一一取出并存入到{}中去,属性就是对于的下标值,属性值就是取出来的值。
    3. arguments[0]object类型的时候它将会去合并后面的对象或者参数,最后返回合并后的对象,具体如下:

      • Boolean:返回第一个(arguments[0])对象
      • String:返回一个对象,并将stirng字符串(分割)存入arguments[0]的对象中去,属性就是对于的下标值,属性值就是取出来的值。
      • Number:返回第一个(arguments[0])对象
      • Object:返回一个对象,并将Object中的参数取出来并存入arguments[0]的对象中去,属性就是对于的下标值,属性值就是取出来的值。
      • Array:返回一个Array对象,将Array中的值一一取出并存入到Array数组中去,数组的下标就是对于的属性,数组中的值就是取出来的属性值。
      • Function:返回第一个(arguments[0])对象
    4. arguments[0]Array类型的时候它同样会去合并后面的对象或者参数,最后返回合并后的对象,但是在这里有一个非常特殊的情况,而个情况的关键点在于in关键字是否获取的是下标值,如果是下标值,它将直接去覆盖arguments[0]Array对于的下标值。

  • 通过断点的方式是可以看出arguments[0]决定这target的值到底是一个普通的对象还是jQuery对象,在使用extend方法的时候需要额外的注意这种情况下的比较特殊性,通常情况下都是一个普通对象,只有在boolean时是比较特殊的 *

extend传递多个参数

说明:相对于传递两个参数来说,传递多个参数的情况就比较简单明了,它直接返回一个合并后的对象(如果不是对象它将根据传递参数的类型进行in关键字的操作并转化成对象),很像JavaScript中的继承。当然它也有一些比较特殊的情况,这种情况适用于对象的继承,主要取决于第一个参数是否为true以及后面的参数是否为Object类型。(可以理解为拷贝类型的不同)

特殊情况的说明(针对Boolean)

  1. 如果第一个参数在传递不同的Boolean值的时候,你会发现,如果你去修改原有的Object对象时,再次输出extend返回的参数有着很大的区别。这是为什么?
    • 首先在进入到extend的时候target会去获取第一个参数,如果未获取到或者传递的为false的时候它将变成 {} (一个新的对象),
    • 如果在原有对象上进行拷贝赋值,此时就是一种浅拷贝,在我们去修改原有对象的同时,extend的对象中的数据也同样发生着改变。如果target被重新赋值一个新的对象{}时,此时再去进行拷贝赋值就是一种深拷贝,如果再去修改原有的对象的同时是不会影响到extend上对象的任何数据。(深拷贝和浅拷贝)

extend特点:

  1. 传递的参数都为Array类型的时候返回的一般为Array类型,如果第一个参数是false则返回的是{}(普通对象)
  2. 当传递两个参数,第一个参数为true时,则会为jQuery对象上添加。
  3. 当传递多个对象的时候,如果第一个参数为false,则为深拷贝,传递第一个参数为true时为浅拷贝,只针对于第二个对象参数和返回值得对比
原创粉丝点击