JS代码执行的环境
-
浏览器:PC端、移动端 「webkit、gecko、trident、blink…」(window)
-
Hybrid混合APP开发:把H5页面嵌入都native app(IOS/安卓)的webview中「webkit」(window)
-
node:一个基于v8引擎,渲染和解析JS的环境(没有window,全局对象global)
-
小程序
-
…
jQuery部分源码剖析
//(function(){}()) 等价 (function(){})()
//jQuery源码中自执行函数 (function(){}())
//首先分析一下这个自执行函数的形参和实参
(function(global, factory){
//区分环境
}(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
//具体代码
}))
/*
第一个实参:typeof window !== "undefined" ? window : this 结果记为A
如果A===window:说明是浏览器、webview中运行
如果是在Node环境下运行,A可能是Global,也可能是当前模块
第一个实参是为了区分js代码执行环境,使用typeof判断的好处是,即使当前环境没有window也不会报错,因为typeof 一个不存在的变量,其结果是undefined
*/
/*------------------------------------*/
(function (global, factory) {
/*区分环境,不同环境下执行回调函数factory(window, noGlobal)
浏览器环境下执行这个函数
window -> window
noGlobal -> undefined
webpack环境下导入执行
window -> window
noGlobal -> true
*/
// module 和 module.exports 属于 CommonJS模块规范 例如:Node、webpack
if (typeof module === "object" && typeof module.exports === "object") {
//只有window才有document
// 既支持CommonJS规范,也有window 例如:webpack工程化环境
/* =>module.exports=jQuery;
外部使用方式:
import $ from 'jquery' $->jQuery
let $=require('jquery') $->jQuery
*/
module.exports = global.document ?
factory(global, true) :
function (w) {
if (!w.document) {
//如果没有window就抛出异常,说明jQuery必须在有window的环境下运行
throw new Error("jQuery requires a window with a document");
}
return factory(w);
};
} else {
// 不支持CommonJS规范的 例如:浏览器环境
// global->window
//外部使用方式 <script src='jquery.min.js'></script>
factory(global);// window
}
}(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
//获取数组原生方法
var deletedIds = [];
var slice = deletedIds.slice;
var concat = deletedIds.concat;
var push = deletedIds.push;
var indexOf = deletedIds.indexOf;
//获取对象原生方法
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var support = {};
var version = "1.11.3";
/*
我们在外面拿到的不是一个真正的jQuery实例,而是一个jQuery.fn.init实例
var jQuery = function (selector, context) {
return new jQuery(selector, context);//不能直接返回自己的实例,会形成"死递归"
};
jQuery();
*/
var jQuery = function (selector, context) {
return new jQuery.fn.init(selector, context);
};
// 给jQuery的原型进行重定向(jQuery是自己定义的一个函数,重定向没有什么损失)
jQuery.fn = jQuery.prototype = {
jquery: version,
constructor: jQuery,// 重定向的原型没有constructor,所以手动增加了一个constructor
//类数组转数组,原生写法:[].slice.call(this)
toArray: function () {
return slice.call(this);
},
//取得其中一个匹配的元素。 num表示取得第几个匹配的元素。从0开始,返回的是原生DOM对象。
get: function (num) {
return num != null ?
// Return just the one element from the set
(num < 0 ? this[num + this.length] : this[num]) :
// Return all the elements in a clean array
slice.call(this);
},
//...
}
/*扩展jQuery的功能
// 往jQuery自己身上拓展方法
jQuery.extend({
sss: function () {
console.log('我是拓展的方法');
}
});
// 往jQuery的原型上拓展方法
jQuery.fn.extend({
fff: function () {
console.log('我是拓展的方法');
}
})
*/
jQuery.extend = jQuery.fn.extend = function() {
//...
}
/*
前面已经将jQuery原型进行重定向,并且jQuery.fn = jQuery.prototype
*/
var init = jQuery.fn.init = function (selector, context) {
// 当$执行时new的是当前这个函数
// init函数里的代码就是获取dom的
}
init.prototype = jQuery.fn; // 把jQuery的原型赋值给init的原型
/* 冲突处理:转让$和jQuery的使用权 */
//暂存用户当前的jQuery、$变量
var _jQuery = window.jQuery;
var _$ = window.$;
jQuery.noConflict = function (deep) {
if (window.$ === jQuery) {
window.$ = _$;//转让$的使用权
}
if (deep && window.jQuery === jQuery) {
window.jQuery = _jQuery;//转让jQuery的使用权
}
return jQuery;//返回jQuery供用户接收(用户可自定义使用jQuery功能的变量名)
};
/* 暴露API */
if (typeof define === "function" && define.amd) {
define("jquery", [], function () {
return jQuery;
});
}
if (typeof noGlobal === "undefined") {
// 给window增加键值对
window.jQuery = window.$ = jQuery;
}
return jQuery;
}))