前言
本人也是刚入坑的一枚小白,到现在差不多快一年了啊…
对于前端的同志来说,jQuery应该是再熟悉不过了吧,因为就目前来看,80%左右的网站或多或少的都直接或间接引用jQuery,可以毫不客气地说它目前来说最流行的,面向对象的JavaScript框架,因为平时使用频率很高,所以也就萌生了阅读源码的想法,并结合自己的目前知识,照着源码一点点的分析吧…
(ps:本人前端小白,也是最近才开始阅读源码,边读边写,所以如果有分析不对或者分析不当的地方,还请谅解,也会很乐意接收大家的批评和指正..)
下面进入正题吧
注
1. 本文分析jQuery版本为3.0.0版本,在官网上下载对应未压缩版即可(jQuery官网)
2. 转载请注明出处
ok;打开jQuery-3.0.0的源码我们发现,一开始就是这么一段代码
( function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
}( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
/******此处省略若干行*******/
}));
一开始看的时候,可能会找不到 function( window , noGlobal){} 这个函数的结束部分,其实,这个函数是jQuery的主体,贯穿整个jQuery的框架,我们所使用的所有的jQuery方法都是写在这个部分。
我们先抛开里面的代码不看,只看外部的框架,我们可以将其简化为
( function( global, factory ) {
/******此处省略若干行*******/
}( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
/******此处省略若干行*******/
}));
这种写法叫做IIFE (immediately-invoked function expression),也叫‘’立即执行函数‘’,‘’即时执行方法‘’等,意思就是函数会在创建完成的时候自动执行一次,一般来说写法有主要有两种写法:
- 第一种:就如同jQuery写法一样
( function( a , b , c ){
/***执行的代码***/
}( 参数1 , 参数2 , 参数3 ) );
- 第二种
( function( a , b , c ){
/***执行的代码***/
})( 参数1 , 参数2 , 参数3 );
这两种写法 参数1,参数2,参数3分别对应的匿名函数的 a , b , c;
ok,问题来了,为什么jQuery要采用IIFE?那么使用IIFE有什么好处?
因为在ES6以前,JS是没有块级作用域的概念,只有函数作用域,作为一种对块级作用域的模拟就只能用function模拟,就是为了使模块相互独立,降低模块间的耦合性,避免全局作用域的污染和全局变量的冲突。
举个例子
/**用IIFE创建一个闭包**/
var foo = (function(){
var txt = "hello";
return {
get: function(){
return txt
},
set: function( val ){
txt = val
}
}
})();
console.log( foo.get() ); // 'hello'
foo.set( 'world' );
console.log( foo.get() ); // 'world'
foo.txt; //'undefinded'
console.log( txt ) // 'ReferenceError : txt is not defined'
可以看出,我们就是可以通过 导出方法 从函数外部的外部去更改 改变函数内部变量的值,所以就可以利用这个特点来隔离作用域,模拟一种“私有”的效果,jQuery正是利用这一点,在我们调用jQuery代码时,保护jQuery内部变量。(ps:如果有不熟悉的同学,可以去看看作用域链和闭包的相关知识哈~)
好的,我们回到上面jQuery的部分,
( function( global, factory ) {
/******此处省略若干行*******/
}( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
/******此处省略若干行*******/
}));
在函数 参数 global 中 , jQuery采用了 一个三元运算符去赋值
typeof window !== "undefined" ? window : this
首先用 typeof 来判断 当前的 window 存不存在 , 如果存在 , 就将 window对象传入 , 否在就传入this对象。jQuery之所以这么写,是因为 global 如果是在浏览器环境就是 window , 如果不是在浏览器环境则是其他的全局对象,这样 传递this可以保存当前的上下文环境;
接下来理一理这一部分代码。
我直接截取了源码的一段
首先我们 看这个判断 :
if ( typeof module === "object" && typeof module.exports === "object" )
结合注释,我们 能大概看懂, 这一段判断是 为了 兼容 commonjs 或者 类似 commonjs 规范的一些框架;
了解过node.js的同学应该能看出来 module.exports 是 node.js 中用来创建模块的方法,那么可以理解为如果这个条件成立,那么 首先判断当前的环境是否支持 global.document 属性 , 也就是window.document 属性 , 如果支持 , 我们调用
`factory( global, true ) { /* jQuery code * / }`
将 jQuery 的方法绑定到当前模块上,
如果 window.document 不存在呢? 直接调下面的方法
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
该方法抛出一个异常 , 告诉用户 jQuery 需要 window.document 环境 , 并依旧返回 factory( w ) 方法(ps:其实 我也不知道 为什么 不支持 ,还要返回 factory( w ) 方法 , 0.0 );
如果
if ( typeof module === "object" && typeof module.exports === "object" )
这个条件不成立,也就是我们 一般 遇到的情况,(一般情况下,typeof module 都是 ‘undefined’),
这样就好办了,直接调用 factory( global ) 将 jQuery 方法引入到我们当前的执行环境中…
不知不觉也码了不少字了,开篇部分就先到这里吧,后面我继续学习,在继续更新吧~ :)