jQuery 源码学习之路一(整体架构--自运行)

研究jquery源码,能让我更深入的了解js的机制和开阔一下自己的思路。不想只知其然,而不知其所以然。
所以这里记录一下我的学习路线,和我对jquery源码的理解。这是我学习的路线,因为怕当时解读的有误,
我会分批次发布博文。若还有其他问题,会后期更正。

对对对,有必要的说一下,我是对jquery的 3.1.1 的版本进行源码解析的。

首先说一下,jquery的整体结构,那就是一个大的自运行的匿名函数。

1
2
3
4
5
6
7
8
9
( function( global, factory ) {
//jquery内部代码省略
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
//jquery内部代码省略
} );

上面的代码就是jquery代码的整个实现的基础。把里面所有的代码抽除,就变成下面这样:

1
(function(){})();

这样你是不是就很熟悉了呢,一个标准的自运行(立即调用)。这么做的目的就是利用了JavaScript函数作用域的特性,解决命名空间与变量污染的问题。

自运行以前在学习的时候,老师还教过一种写法:

1
!(function(){console.log("aa");})(); //控制台打印:aa;

还有几种写法我列举一下:

1
2
3
~(function(){console.log("bb");})(); //控制台打印:bb;
+(function(){console.log("cc");})(); //控制台打印:cc;
-(function(){console.log("dd");})(); //控制台打印:dd;

这几种也都是可以达到自运行的目的。

而且,自执行的函数表达式也可以像普通函数一样可以传参:

1
2
3
4
5
(function(a,b){
console.log(a); //1
console.log(b); //2
console.log(a+b); //3
})(1,2);

运行上面的代码,a,b对应的值分别是传值进来的 1 和 2。

然后再来看jq源码中的这个自运行的函数:

1
2
3
4
5
6
7
8
9
( function( global, factory ) {
//jquery内部代码省略
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
//jquery内部代码省略
} );

对,还是这句话,这里的 global 指的就是 window 对象,factory 指的就是function( window, noGlobal ) {} 这个函数。这里为啥要传window,就是为了要给 window 暴露一个 $/jQuery 的接口。

来两段代码,你就明白了:(这里我们先暂不考虑传入的第二个参数)

1
2
3
4
5
6
7
//假如说传参
(function(){
var jQ = "测试传值window";
})();
console.log(jQ); // jQ is not defined

为啥 jQ is not defined 了呢,因为 jQ 是一个局部变量呀,你这么直接去访问一个局部变量,肯定是 not defined 。

1
2
3
4
5
6
7
//假如说传参window
(function(g){
g.jQ = "测试传值window";
})(window);
console.log(jQ); //测试传值window

这样就可以了,打印出值了。其实就是简单粗暴的把 jQ 这个对象赋给 window ,这时候 jQ 也就是一个全局的变量了,所以就访问到了。

——————————————-华丽的分界线————————————————————–
最后我要说一下源码中的对 $ 这个的冲突处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$;
jQuery.noConflict = function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
};

jQuery.noConflict 这个方法达到的目的就是,当你引入其他库,也有对 $ 的引用时,可以释放jQuery 对 $ 的控制权。首先在 jQuery 自运行时用临时变量 _jQuery 存储了一下 window.jQuery 这个对象,此时window.jQuery 的值还是 undefined window.$ 指向的是之前实现 $ 的库,没有就是 undefined;在调用jQuery.noConflict方法时,就会移交对 $ 的控制权,如果 window.$ 是 jQuery 对象,那么 window.$ 就等于之前实现 $ 的库的那个对象(否则undefined)。最后再把 jQuery 对象返回出来。这段代码也是非常简单粗暴的呀。不知道会不会有人不明白, window.jQuery 一开始是undefined,那么后来是怎么又变成 jQuery 对象的呢,那就是 jQuery 自运行时的下面这段代码了。

1
2
3
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}

可以与 .ready() 方法结合起来使用,不需要为 jQuery 对象起别名,这项技术非常有效:

1
2
3
4
5
6
7
8
9
10
<script type="text/javascript" src="other_lib.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$.noConflict();
jQuery(document).ready(function($) {
// 使用 jQuery $ 的代码
});
// 使用其他库的 $ 的代码
</script>

附上一句, $.noConflict(true); 是将 $ 和 jQuery 的控制权都交还给原来的库。用之前请考虑清楚!

最后还要奉上我学习后仿 jq 的一段小代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function(g,fn){
fn(g);
})(window,function(a){
var yh = function(){
console.log("yuhong");
};
a.yh = yh ;
});
yh(); //控制台打印 yuhong

2017-11-10,明天就是双十一了,然而我并没有什么要买的。。。啦啦啦啦。。。。