underscore.js源码整体框架解析

7 篇文章 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 = true
  return 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值