当前位置: 代码迷 >> 综合 >> underscore.js源码整体框架解析
  详细解决方案

underscore.js源码整体框架解析

热度:18   发布时间:2023-09-20 15:57:00.0

源码框架

读一些库的源码时最头疼的其实不是里面各个函数的功能,而是整体结构框架,通常库的源码都很长,跟框架相关的代码并不是在一起放着的,导致你想明白起来就很困难。
我看过通过画图的方式去讲解框架的,但看完以后还是一头雾水,甚至越看越懵逼,各种箭头绕来绕去对理解框架是然并卵的。
身为一个程序员,代码其实是最直接的。
并且框架这种底层的东西,原理一定是很简单的。
即便是复杂如整个互联网的架构,每一层网络模型的原理也都是很简单的。
所以想理解框架,最好的方式就是把跟框架相关的代码提取并精炼出来,其他乱七八糟的东西一定是在这之上展开的。
以下就是underscore.js提炼出来的框架代码。
把这块代码放到一个js文件中当作新的库文件,就足以模拟整个underscore的框架了,完全可以独立运行的。
所有解析都在注释中。
注释中有源码所对应的行数。
版本为1.11.0 esm版。
部分代码我进行了改写,以便于大家的理解。

// underscore版本 1.11.0 esm版// 303行
// _其实是个构造函数
function _(obj) {
    // 这么写 就可以不用new去实例化了,而是像普通函数一样实例化,比较容易迷惑人!if (!(this instanceof _)) return new _(obj)// 缓存上一次链式调用的结果值this._wrapped = obj
}// 312行
// 单个增加原型方法,_构造函数生成的实例就可以调用了
_.prototype.value = function() {
        return this._wrapped;
};// 各种功能函数。。。
function fun1() {
    console.log('调用函数fun1')return 'res'
}// 878行
// 包装普通对象为可供链式调用的对象
function chain(obj) {
    var instance = _(obj)instance._chain = truereturn instance
}// 1728行
// 链式调用如果没有结束,则将上次结果进行再包装,供下次链式调用使用
function chainResult(instance, obj) {
    return instance._chain ? _(obj).chain() : obj
}// 1733行
// _上混入功能函数,文档上的方法基本都是_构造函数的静态方法
// _上的原型方法也会在这里挂载,为了链式调用时可以使用,因为chain方法的返回值就是_的实例 
function mixin(obj) {
    let funArr = Object.keys(obj)// 在_构造函数上增加各种静态方法funArr.forEach(name=> {
    _[name] = obj[name]		 })// 在_构造函数上增加各种原型方法,链式调用时使用funArr.forEach(name=> {
    const func = obj[name]_.prototype[name] = function() {
    // !!!这里就是为啥链式调用时不需要再传文档里的第一个参数// 因为this._wrapped缓存了上一次处理的结果值,在这里合并成新的参数传递给下一个链式调用函数var args = [this._wrapped, ...arguments]return chainResult(this, func.apply(_, args))}})// 将处理过的_构造函数返回,供export使用return _
}// 1772行
// 要导出的所有功能函数
var allExports = {
    fun1: fun1,chain: chain
}// 1923行
// 将要导出的功能函数混入到_ ,并起一个新名字,供导出用 
var _$1 = mixin(allExports)// 1929行
// 导出新名字
export default _$1

其中最难理解的部分就是链式调用相关的代码,如果抛开这块,就是贼简单的一个框架。
链式调用相关请仔细看注释。

使用方式

以上代码保存为module.js文件,在另一个js文件中引入,就可以使用了。

import _ from './module.js'let arg1 = 'arg1'_.fun1() // res// 链式调用
let res = _.chain(arg1).fun1().fun1().value()console.log(res)  // res

还看不懂的话只能去补补基础知识了,只能帮你到这啦。

总结

许多库其实用的都是类似这种的结构,对外导出的变量就是一个构造函数,挂载各类静态方法以及原型方法来扩充,因为对外暴露的变量通常就只有一个。
欢迎来我的b站空间逛逛
https://space.bilibili.com/395672451