2019年总结回顾

jQuery总体架构

jQuery使用上的几大特点

  1. $('#id')函数方式直接生成jQuery对象
  2. $('#id').css().html().hide()链式调用

关于链式调用,既在函数结尾return this;

无new函数的实现

下面是一个普通的函数,很显然,会陷入死循环;

1
2
3
4
5
6
var jQuery = function() {
return new jQuery();
}
jQuery.prototype = {
...
}

jQuery用一个init函数来代替直接new函数名的方式,还要考虑到jQuery中分离作用域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var jQuery = function () {
return new jQuery.prototype.init();
}
jQuery.prototype = {
constructor: jQuery,
init: function () {
this.jquery = 1.0;
return this;
},
jquery: 2.0,
each: function () {
console.log('each');
return this;
}
}
jQuery().jquery //1.0
jQuery.prototype.jquery //2.0
jQuery().each //error

上面的代码看似运行正常,但是问题出在jQuery().each //error,访问不到each函数上。实际上,new jQuery.prototype.init()返回的是init这个函数的实例,所以init函数中的this就没有意义!如果:

1
2
3
var jq = jQuery();
jq.__proto__ === jQuery.prototype;
jq.each === jQuery.prototype.each;

如果实现上面的proto的指向问题,原型函数调用问题就解决了,但实际上:

1
2
var jq = jQuery();
jq.__proto__ ===jQuery.prototype.init.prototype; //true

实际上,jq的proto是指向init函数原型的,所以,我们可以把jQuery.prototype.init.prototype = jQuery.prototype,这个时候,函数调用就顺理成章了,而且使用的都是引用,指向的都是同一个prototype对象

jQuery内部结构图

jQuery.fn,实际上是prototype的一个引用,指向jQuery.prototype;

1
2
3
4
5
6
var jQuery = function () {
return new jQuery.prototype.init();
}
jQuery.fn = jQuery.prototype {
...
}

为什么要用fn指向prototype?一个比较中肯的回答:简洁!你不觉得fn比prototype好写多了么
借用网上的一张图:

从图上可以看出,window对象有两个公共的接口,分别是$jQuery

1
window.jQuery = window.$ = jQuery

jQuery.extend方法是一个对象拷贝的方法,包括深拷贝

链式调用

实现方式,在没有返回值的原型函数后面添加return this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var jQuery = function () {
return new Jquery.fn.init();
}
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function () {
this.jQuery = 3.0;
return this;
},
each: function () {
console.log('each');
return this;
}
}
jQuery.fn.init.prototype = jQuery,fn;
jQuery().each().each();
// 'each'
// 'each'

extend

extendjQuery的一个重要函数,可以对jQuery本身的属性和方法进行扩张,也可以对原型的属性和方法进行扩展

extend函数的功能:

  1. 如果参数只有一个object,即表示将这个对象扩展到jQuery的命名空间中,也就是所谓的jQuery扩展
  2. 如果函数接受了多个object,则表示一种属性拷贝,将后面多个对象的属性全拷贝到第一个对象上,这其中,还包括深拷贝,即非引用拷贝,第一个参数如果是true,则表示深拷贝。
    1
    2
    3
    jQuery.extend(target); // jQuery的扩展
    jQuery.extend(target, obj1, obj2,...); //浅拷贝
    jQuery.extend(true, target, obj1, obj2,...) //深拷贝

以下是jQuery 3之后的extend函数源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;

// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;

// Skip the boolean and the 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++ ) {

// Only deal with non-null/undefined values
if ( ( options = arguments[ i ] ) != null ) {

// Extend the base object
for ( name in options ) {
copy = options[ name ];

// Prevent Object.prototype pollution
// Prevent never-ending loop
if ( name === "__proto__" || target === copy ) {
continue;
}

// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
src = target[ name ];

// Ensure proper type for the source value
if ( copyIsArray && !Array.isArray( src ) ) {
clone = [];
} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;

// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );

// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}

// Return the modified object
return target;
};

extend衍生的函数

jQuery.isFunction源码

1
2
3
jQuery.ifFunction = function (obj) {
return jQuery.type(obj) === "function";
}

这个是简单,但是这里是为了引出涉及到的另一个jQuery函数jQuery.type,这个函数用于类型判断。

为什么不用原生js的typeof?因为不好用!