您的位置:首页 > Web前端 > JQuery

JQuery源码解析–extend篇

2017-11-26 09:42 120 查看

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
时:

当第一个参数为
true
时,当前的变量
target
的值是传递过来的第一个参数(第3行),也就是
boolean
类型的值。在执行到 第9行
if ( typeof target === "boolean" )
并进入到判断体内 ,在此内部会将
deep
设置为
true
。而
target
变量会将传递的第二个参数设置进去,但是并没有去传递第二个参数,则会默认设置为
{}
,此时的
i
会进行
++i
的操作,
i


代码继续向下执行会来到 18行
if ( typeof target !== "object" && !jQuery.isFunction(target) )
的判断,此时的target是一个
{}
,所以条件不成立,继续往下执行。

执行到 23行
if ( i === length )
的判断时,此时的
i
已经变成
2
,而
length
是传递的参数的长度(
1
),同样条件也不成立。

代码继续向下执行到 28行
for ( ; i < length; i++ )
,此时的
i
2
,而
length
1
,则
for
循环内的判断条件不成立。

最后代码将执行到 63行
return target;
,此时的
target
{}
,因此将返回一个
{}
回来。

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

当传递的参数为
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


向下继续执行
for
循环,因为
i
0
length
1
所以执行循环体中的方法。

继续向下执行到 第30行
if ( (options = arguments[ i ]) != null )
,取出第一个参数的值并赋值给
options
(
boolean
类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用
in
关键字循环去取出
options
中的值(
options
false
时取出来
name
的为
undefined
),当
name
undefined
时直接跳出
for
循环执行一次循环。

最后执行完循环体后返回
target(jQuery)


string类型

当第一个参数为string类型时:

当第一个参数为
string
时,当前的变量
target
的值是传递过来的第一个参数(第3行),也就是
string
类型的值。在执行到 第9行
if ( typeof target === "boolean" )
时,条件并不成立,继续往下执行。

继续向下执行来到 第18行
if ( typeof target !== "object" && !jQuery.isFunction(target) )
,此时的
target
string
类型的参数,条件成立,进入到条件体并将
target
设置为
{}


代码继续向下执行将来到 第23行
if ( i === length )
,此时的
i
1
length
同样也是
1
,所以条件成立,执行条件体内的代码,此时的
target
变成
this(jQuery)
i
执行
i--
的操作变为
0


当执行到 第28行
for ( ; i < length; i++ )
,此时的i为0,length为1,循环体内的条件成立,代码进入到循环体内。

继续向下执行到 第30行
if ( (options = arguments[ i ]) != null )
,取出第一个参数的值并赋值给
options
(
string
类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用
in
关键字循环去取出
options
中的值(
options
string
时取出来
name
的为下标值)在执行 第33行
src = target[ name ];
和 第34行
copy = options[ name ];
src
undefined
src
此时是去取
this(jQuery)
中的值),
copy
为每次循环后的参数。

当代码执行到 第42行
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) )
时,
deep
false
copy
为每次循环取出来的参数,而
copyIsArray
false
条件不成立,去执行
else if
中的方法,此时
copy
有值,所以进入到循环体内,并将
copy
中的值赋值给
target[name]
中去 (当为
string
时,
name
为下标值)。

最后执行完循环体后返回
target(jQuery)


number类型

当第一个参数为number类型

当传递的参数为
number
的时候,只有 第18行
if ( typeof target !== "object" && !jQuery.isFunction(target) )
和 第 23行
if ( i === length )
条件成立执行完 18行
target
变为
{}
,在执行 23行 循环体内的方法后target值变为
jQuery
,
i
执行
i--
变为
0


向下继续执行
for
循环,因为
i
0
length
1
所以执行循环体中的方法。

继续向下执行到 第30行
if ( (options = arguments[ i ]) != null )
,取出第一个参数的值并赋值给
options
(
number
类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用
in
关键字循环去取出
options
中的值(
options
number
时取出来
name
的为
undefined
),当
name
undefined
时直接跳出
for
循环执行一次循环。

最后执行完循环体后返回
target(jQuery)


Object类型

当传递的参数为Object类型

当传递的参数为
object
时,只有第 23行
if ( i === length )
条件成立,在执行 23行 循环体内的方法后target值变为
jQuery
,
i
执行
i--
变为
0


向下继续执行
for
循环,因为
i
0
length
1
所以执行循环体中的方法。

继续向下执行到 第30行
if ( (options = arguments[ i ]) != null )
,取出第一个参数的值并赋值给
options
(
object
类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用
in
关键字循环去取出
options
中的值(
options
object
时取出来
name
的为对应的属性名),先找到对应
target
上是否存在对应的属性值并赋值给
src
,在去找
opations
中对应的属性值并赋值给
copy


deep
不为
true
时(表示深拷贝),为
target
添加相应的属性
name
并将
copy
中的值(通过
name
找到的属性值)赋值给
target[name]
中去。

最后返回
target


Function类型

当第一个参数为Function类型

当传递的参数为
Function
的时候,只有第 23行
if ( i === length )
条件成立,在执行 23行 循环体内的方法后target值变为
jQuery
,
i
执行
i--
变为
0


向下继续执行
for
循环,因为
i
0
length
1
所以执行循环体中的方法。

继续向下执行到 第30行
if ( (options = arguments[ i ]) != null )
,取出第一个参数的值并赋值给
options
(
function
类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用
in
关键字循环去取出
options
中的值(
options
function
时取出来
name
的为
undefined
),当
name
undefined
时直接跳出
for
循环执行一次循环。

最后执行完循环体后返回
target(jQuery)


Array类型

当第一个参数为Array类型时:

当第一个参数为
array
时,当前的变量
target
的值是传递过来的第一个参数(第3行),也就是
string
类型的值。在执行到 第9行
if ( typeof target === "boolean" )
时,条件并不成立,继续往下执行。

继续向下执行来到 第18行
if ( typeof target !== "object" && !jQuery.isFunction(target) )
,此时的
target
array
类型的参数,条件不成立。

代码继续向下执行将来到 第23行
if ( i === length )
,此时的
i
1
length
同样也是
1
,所以条件成立,执行条件体内的代码,此时的
target
变成
this(jQuery)
i
执行
i--
的操作变为
0


当执行到 第28行
for ( ; i < length; i++ )
,此时的i为0,length为1,循环体内的条件成立,代码进入到循环体内。

继续向下执行到 第30行
if ( (options = arguments[ i ]) != null )
,取出第一个参数的值并赋值给
options
(
array
类型),并判断当前的第一个参数是否为空,此时第一个参数有值,进入到判断体内。使用
in
关键字循环去取出
options
中的值(
options
array
时取出来
name
的为下标值)在执行 第33行
src = target[ name ];
和 第34行
copy = options[ name ];
src
undefined
src
此时是去取
this(jQuery)
中的值),
copy
为每次循环后的参数。

当代码执行到 第42行
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) )
时,
deep
false
copy
为每次循环取出来的参数,而
copyIsArray
false
条件不成立,去执行
else if
中的方法,此时
copy
有值,所以进入到循环体内,并将
copy
中的值赋值给
target[name]
中去 (当为
array
时,
name
为下标值)。

最后执行完循环体后返回
target(jQuery)


extend传递两个参数

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

决定性的参数
arguments[0]

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

arguments[0]
boolean
类型的时候它将决定
target
{}
还是
this
。并且会将第二个参数以对应的方式取出来存放到
target
中去。

arguments[0]
string/number
类型的时候它都会返回一个
{}
值,而
{}
中的具体信息取决于第二个参数:

Boolean
:返回空
{}
对象

String
: 返回
{}
对象,并将
stirng
字符串(分割)存入对象中去,属性就是对于的下标值,属性值就是取出来的值。

Number
:返回空
{}
对象

Object
:返回
Object
对象

Function
: 返回空
{}
对象

Array
: 将
Array
中的值一一取出并存入到
{}
中去,属性就是对于的下标值,属性值就是取出来的值。

arguments[0]
object
类型的时候它将会去合并后面的对象或者参数,最后返回合并后的对象,具体如下:

Boolean
:返回第一个(
arguments[0]
)对象

String
:返回一个对象,并将
stirng
字符串(分割)存入
arguments[0]
的对象中去,属性就是对于的下标值,属性值就是取出来的值。

Number
:返回第一个(
arguments[0]
)对象

Object
:返回一个对象,并将
Object
中的参数取出来并存入
arguments[0]
的对象中去,属性就是对于的下标值,属性值就是取出来的值。

Array
:返回一个
Array
对象,将
Array
中的值一一取出并存入到
Array
数组中去,数组的下标就是对于的属性,数组中的值就是取出来的属性值。

Function
:返回第一个(
arguments[0]
)对象

arguments[0]
Array
类型的时候它同样会去合并后面的对象或者参数,最后返回合并后的对象,但是在这里有一个非常特殊的情况,而个情况的关键点在于
in
关键字是否获取的是下标值,如果是下标值,它将直接去覆盖
arguments[0]
Array
对于的下标值。

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

extend传递多个参数

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

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

如果第一个参数在传递不同的
Boolean
值的时候,你会发现,如果你去修改原有的
Object
对象时,再次输出
extend
返回的参数有着很大的区别。这是为什么?

首先在进入到
extend
的时候
target
会去获取第一个参数,如果未获取到或者传递的为
false
的时候它将变成
{}
(一个新的对象),

如果在原有对象上进行拷贝赋值,此时就是一种浅拷贝,在我们去修改原有对象的同时,
extend
的对象中的数据也同样发生着改变。如果
target
被重新赋值一个新的对象
{}
时,此时再去进行拷贝赋值就是一种深拷贝,如果再去修改原有的对象的同时是不会影响到
extend
上对象的任何数据。(深拷贝和浅拷贝

extend特点:

传递的参数都为
Array
类型的时候返回的一般为
Array
类型,如果第一个参数是
false
则返回的是
{}
(普通对象)

当传递两个参数,第一个参数为
true
时,则会为
jQuery
对象上添加。

当传递多个对象的时候,如果第一个参数为
false
,则为深拷贝,传递第一个参数为
true
时为浅拷贝,只针对于第二个对象参数和返回值得对比
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jquery 源码 extend 函数