晚安,我是若川。这是自学源标识符总体构架系列产品首篇。总体构架这词汇好似有点儿大,Licharre即使是源标识符总体内部结构吧,主要就是自学是标识符总体内部结构,不思量其它并非主旋律的具体内容表达式的同时实现。该文自学的是装箱资源整合后的标识符,并非实际库房中的分拆的标识符。
自学源标识符总体构架系列产品该文如下表所示:
1.若川:自学 jQuery 源标识符总体构架,打造出属于他们的 js C#2.若川:自学underscore源标识符总体构架,打造出属于他们的表达式式程式设计C#3.若川:自学 lodash 源标识符总体构架,打造出属于他们的表达式式程式设计C#4.若川:自学 sentry 源标识符总体构架,打造出属于他们的后端极度监视SDK5.若川:自学 vuex 源标识符总体构架,打造出属于他们的状况管理工作库6.若川:自学 axios 源标识符总体构架,打造出属于他们的允诺库7.若川:自学 koa 源标识符的总体构架,Saramonkoa卷心菜数学模型基本上原理和co基本上原理
钟爱的听众能点选写作。 其它源标识符方案中的有:express、vue-router、redux、 react-redux
源标识符类该文,通常写作量不高。早已有潜能看懂的,他们就看了。不该看,不肯看的就不能去看源标识符。
因此我的该文,尽可能写得让想看源标识符又不晓得是不是看的听众看不懂。
尽管那时基本上不见得采用jQuery了,但jQuery盛行10十多年的JS库,却是有必要性自学它的源标识符的。也能学着打造出属于他们的js类库,求职者复试时能添色许多。
本该文自学的是v3.4.1版。 unpkg.com源标识符门牌号:https://unpkg.com/[email protected]/dist/jquery.js
f=”https://github.com/jquery/jquery”>jQuery github库房
自继续执行非官方表达式
(function(global, factory){
})(typeof window !== “underfined” ? window: this, function(window, noGlobal){
});
外界访问不到里面的变量和表达式,里面能访问到外界的变量,但里面定义了他们的变量,则不能访问外界的变量。 非官方表达式将标识符包裹在里面,防止与其它标识符冲突和污染全局环境。 关于自继续执行表达式并非很了解的听众能参看这篇该文。[译] JavaScript:立即继续执行表达式表达式(IIFE)
浏览器环境下,最后把$ 和 jQuery表达式挂载到window上,因此在外界就能访问到$和jQuery了。
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
// 其中`noGlobal`参数只有在这里用到。
支持多种环境下采用 比如 commonjs、amd规范
commonjs 规范支持
commonjs同时实现 主要就代表 nodejs
// global是全局变量,factory 是表达式
( function( global, factory ) {
// 采用严格模式
“use strict”;
// Commonjs 或者 CommonJS-like 环境
if ( typeof module === “object” && typeof module.exports === “object” ) {
// 如果存在global.document 则返回factory(global, true);
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 );
}
// Pass this if window is not defined yet
// 第一个参数判断window,存在返回window,不存在返回this
} )( typeof window !== “undefined” ? window : this, function( window, noGlobal ) {});
amd 规范 主要就代表 requirejs
if ( typeof define === “function” && define.amd ) {
define( “jquery”, [], function() {
return jQuery;
} );
}
cmd 规范 主要就代表 seajs
很遗憾,jQuery源标识符里没有暴露对seajs的支持。但网上也有一些方案。这里就不具体内容提了。毕竟那时基本上不用seajs了。
无 new 构造
前述上也是能 new的,因为jQuery是表达式。而且和不用new效果是一样的。 new显示返回对象,因此和直接调用jQuery表达式作用效果是一样的。 如果对new操作符具体内容做了什么不明白。能参看我之前写的该文。
复试官问:能否模拟同时实现JS的new操作符
源标识符:
var
version = “3.4.1”,
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// 返回new之后的对象
return new jQuery.fn.init( selector, context );
};
jQuery.fn = jQuery.prototype = {
// jQuery当前版
jquery: version,
// 修正构造器为jQuery
constructor: jQuery,
length: 0,
};
init = jQuery.fn.init = function( selector, context, root ) {
// …
if ( !selector ) {
return this;
}
// …
};
init.prototype = jQuery.fn;
jQuery.fn === jQuery.prototype; // true
init = jQuery.fn.init;
init.prototype = jQuery.fn;
// 也是
jQuery.fn.init.prototype === jQuery.fn; // true
jQuery.fn.init.prototype === jQuery.prototype; // true
关于这个笔者画了一张jQuery原型关系图,所谓一图胜千言。
jQuery-v3.4.1原型关系图<sciprt src=”https://unpkg.com/
[email protected]/dist/jquery.js”>
</script>
console.log({jQuery});
// 在谷歌浏览器控制台,能看到jQuery表达式下挂载了很多静态属性和方法,在jQuery.fn 上也挂着很多属性和方法。
Vue源标识符中,也跟jQuery类似,继续执行的是Vue.prototype._init方法。
function Vue (options) {
if (!(this instanceof Vue)
) {
warn(Vue is a constructor and should be called with the `new` keyword);
}
this._init(options);
}
initMixin(Vue);
function initMixin (Vue) {
Vue.prototype._init = function (options) {};
};
核心表达式之一 extend
用法:
jQuery.extend( target [, object1 ] [, objectN ] ) Returns: Object
jQuery.extend( [deep ], target, object1 [, objectN ] )
jQuery.extend API jQuery.fn.extend API
看几个例子: (例子能我放到在线编辑标识符的jQuery.extend例子codepen了,能直接运行)。
// 1. jQuery.extend( target)
var result1 = $.extend({
job: 后端开发工程师,
});
console.log(result1, result1, result1.job); // $表达式 加了一个属性 job // 后端开发工程师
// 2. jQuery.extend( target, object1)
var result2 = $.extend({
name: 若川,
},
{
job: 后端开发工程师,
});
console.log(result2, result2); // { name: 若川, job: 后端开发工程师 }
// deep 深拷贝
// 3. jQuery.extend( [deep ], target, object1 [, objectN ] )
var result3 = $.extend(true, {
name: 若川,
other: {
mac: 0,
ubuntu: 1,
windows: 1,
},
}, {
job: 后端开发工程师,
other: {
mac: 1,
linux: 1,
windows: 0,
}
});
console.log(result3, result3);
// deep true
// {
// “name”: “若川”,
// “other”: {
// “mac”: 1,
// “ubuntu”: 1,
// “windows”: 0,
// “linux”: 1
// },
// “job”: “后端开发工程师”
// }
// deep false
// {
// “name”: “若川”,
// “other”: {
// “mac”: 1,
// “linux”: 1,
// “windows”: 0
// },
// “job”: “后端开发工程师”
// }
结论:extend函数既能同时实现给jQuery表达式能同时实现浅拷贝、也能同时实现深拷贝。能给jQuery上添加静态方法和属性,也能像jQuery.fn(也是jQuery.prototype)上添加属性和方法,这个功能归功于this,jQuery.extend调用时this指向是jQuery,jQuery.fn.extend调用时this指向则是jQuery.fn。
浅拷贝同时实现
晓得这些,其实同时实现浅拷贝却是比较容易的:
// 浅拷贝同时实现
jQuery.extend = function(){
// options 是扩展的对象object1,object2…
var options,
// object对象上的键
name,
// copy object对象上的值,也是是需要拷贝的值
copy,
// 扩展目标对象,可能并非对象,因此或空对象
target = arguments[0] || {},
// 定义i为1
i = 1,
// 定义实参个数length
length = arguments.length;
// 只有一个参数时
if(i === length){
target = this;
i–;
}
for(; i < length; i++){
// 并非underfined 也并非null
if((options = arguments[i]) != null){
for(name in options){
copy = options[name];
// 防止死循环,continue 跳出当前此次循环
if ( name === “__proto__” || target === copy ) {
continue;
}
if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// 最后返回目标对象
return target;
}
深拷贝则主要就是在以下这段标识符做判断。可能是数组和对象引用类型的值,做判断。
if ( copy !== undefined ) {
target[ name ] = copy;
}
为了方便听众调试,标识符同样放在jQuery.extend浅拷贝标识符同时实现codepen,可在线运行。
深拷贝同时实现
$.extend = function(){
// options 是扩展的对象object1,object2…
var options,
// object对象上的键
name,
// copy object对象上的值,也是是需要拷贝的值
copy,
// 深拷贝新增的四个变量 deep、src、copyIsArray、clone
deep = false,
// 源目标,需要往上面赋值的
src,
// 需要拷贝的值的类型是表达式
copyIsArray,
//
clone,
// 扩展目标对象,可能并非对象,因此或空对象
target = arguments[0] || {},
// 定义i为1
i = 1,
// 定义实参个数length
length = arguments.length;
// 处理深拷贝情况
if ( typeof target === “boolean” ) {
deep = target;
// Skip the boolean and the target
// target目标对象开始后移
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
// target不等于对象,且target并非表达式的情况下,强制将其赋值为空对象。
if ( typeof target !== “object” && !isFunction( target ) ) {
target = {};
}
// 只有一个参数时
if(i === length){
target = this;
i–;
}
for(; i < length; i++){
// 并非underfined 也并非null
if((options = arguments[i]) != null){
for(name in options){
copy = options[name];
// 防止死循环,continue 跳出当前此次循环
if ( name === “__proto__” || target === copy ) {
continue;
}
// Recurse if were merging plain objects or arrays
// 这里deep为true,并且需要拷贝的值有值,并且是纯粹的对象
// 或者需拷贝的值是数组
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
// 源目标,需要往上面赋值的
src = target[ name ];
// Ensure proper type for the source value
// 拷贝的值,并且src并非数组,clone对象改为空数组。
if ( copyIsArray && !Array.isArray( src ) ) {
clone = [];
// 拷贝的值并非数组,对象不是纯粹的对象。
} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
// clone 赋值为空对象
clone = {};
} else {
// 否则 clone = src
clone = src;
}
// 把下一次循环时,copyIsArray 需要重新赋值为false
copyIsArray = false;
// Never move original objects, clone them
// 递归调用他们
target[ name ] = jQuery.extend( deep, clone, copy );
// Dont bring in undefined values
}
else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// 最后返回目标对象
return target;
};
为了方便听众调试,这段标识符同样放在jQuery.extend深拷贝标识符同时实现codepen,可在线运行。
深拷贝衍生的表达式 isFunction
判断参数是否是表达式。
var isFunction = function isFunction( obj ) {
// Support: Chrome <=57, Firefox <=52
// In some browsers, typeof returns “function” for HTML <object> elements
// (i.e., `typeof document.createElement( “object” ) === “function”`).
// We dont want to classify *any* DOM node as a function.
return typeof obj === “function” && typeof obj.nodeType !== “number”;
};
深拷贝衍生的表达式 jQuery.isPlainObject
jQuery.isPlainObject(obj) 测试对象是否是纯粹的对象(通过 “{}” 或者 “new Object” 创建的)。
jQuery.isPlainObject({}) // true
jQuery.isPlainObject(“test”) // false
var getProto = Object.getPrototypeOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call( Object );
jQuery.extend( {
isPlainObject: function( obj ) {
var proto, Ctor;
// Detect obvious negatives
// Use toString instead of jQuery.type to catch host objects
// !obj 为true或者 不为[object Object]
// 直接返回false
if ( !obj || toString.call( obj ) !== “[object Object]” ) {
return false;
}
proto = getProto( obj );
// Objects with no prototype (e.g., `Object.create( null )`) are plain
// 原型不存在 比如 Object.create(null) 直接返回 true;
if ( !proto ) {
return true;
}
// Objects with prototype are plain iff they were constructed by a global Object function
Ctor = hasOwn.call( proto, “constructor” ) && proto.constructor;
// 构造器是表达式,并且 fnToString.call( Ctor ) === fnToString.call( Object );
return typeof Ctor === “function” && fnToString.call( Ctor ) === ObjectFunctionString;
},
});
extend表达式,也能他们删掉写一写,算是jQuery中一个比较核心的表达式了。而且用途广泛,能内部采用也能,外部采用扩展 插件等。
链式调用
jQuery能够链式调用是因为一些表达式继续执行结束后 return this。 比如 jQuery 源标识符中的addClass、removeClass、toggleClass。
jQuery.fn.extend({
addClass: function(){
// …
return this;
},
removeClass: function(){
// …
return this;
},
toggleClass: function(){
// …
return this;
},
});
jQuery.noConflict 很多js库都会有的防冲突表达式
jQuery.noConflict API
用法:
<script>
var $ = 我是其它的$,jQuery不要覆盖我;
</script>
<script src=”./jquery-3.4.1.js”>
</script>
<script>
$.noConflict();
console.log($); // 我是其它的$,jQuery不要覆盖我
</script>
jQuery.noConflict 源标识符
var
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$;
jQuery.noConflict = function( deep ) {
// 如果早已存在$ === jQuery;
// 把已存在的_$赋值给window.$;
if ( window.$ === jQuery ) {
window.$ = _$;
}
// 如果deep为 true, 并且早已存在jQuery === jQuery;
// 把已存在的_jQuery赋值给window.jQuery;
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
// 最后返回jQuery
return jQuery;
};
总结
全文主要就通过Saramon了jQuery总体内部结构,自继续执行非官方表达式、无new构造、支持多种规范(如commonjs、amd规范)、核心表达式之extend、链式调用、jQuery.noConflict等方面。
重新梳理下文中自学的源标识符内部结构。
// 源标识符内部结构
( 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 ) {
var version = “3.4.1”,
// Define a local copy of jQuery
jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context );
};
jQuery.fn = jQuery.prototype = {
jquery: version,
constructor: jQuery,
length: 0,
// …
};
jQuery.extend = jQuery.fn.extend = function() {};
jQuery.extend( {
// …
isPlainObject: function( obj ) {},
// …
});
init = jQuery.fn.init = function( selector, context, root ) {};
init.prototype = jQuery.fn;
if ( typeof define === “function” && define.amd ) {
define( “jquery”, [], function() {
return jQuery;
} );
}
jQuery.noConflict = function( deep ) {};
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
});
能自学到jQuery巧妙的设计和构架,为他们所用,打造出属于他们的jsC#。 相关标识符和资源放置在github blog中,需要的听众能自取。
下一篇该文可能是自学underscorejs的源标识符总体构架。
听众发现有不妥或可改善之处,欢迎评论指出。另外觉得写得不错,能点赞、评论、转发,也是对笔者的一种支持。
扩展写作
chokcoco: jQuery- v1.10.2 源标识符解读
chokcoco:【深入浅出jQuery】源码Saramon–总体构架
songjz :jQuery 源标识符系列产品(一)总体构架
关于
作者:常以若川为名混迹于江湖。后端路上 | PPT爱好者 | 所知甚少,唯善学。
个人博客掘金专栏
segmentfault后端视野专栏
知乎后端视野专栏
github blog,相关源标识符和资源都放在这里,求个star^_^~
可能比较有趣