16

学习underscore源码整体架构,打造属于自己的函数式编程类库

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzI1NTcxOTQ1Nw%3D%3D&%3Bmid=2247490299&%3Bidx=2&%3Bsn=4c55896bddc0d4fa5f15d496304099ab
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

上一篇文章写了 jQuery整体架构 ,学习 jQuery 源码整体架构,打造属于自己的 js 类库

虽然看过挺多 underscore.js 分析类的文章,但总感觉少点什么。这也许就是 纸上得来终觉浅,绝知此事要躬行 吧。于是决定自己写一篇学习 underscore.js 整体架构的文章。

本文章学习的版本是 v1.9.1unpkg.com 源码地址:https://unpkg.com/[email protected]/underscore.js

虽然很多人都没用过 underscore.js ,但看下官方文档都应该知道如何使用。

从一个官方文档 _.chain 简单例子看起:

看例子中可以看出,这是支持链式调用。

读者也可以顺着文章思路,自行打开下载源码进行调试,这样印象更加深刻。

链式调用

_.chain 函数源码:

这个函数比较简单,就是传递 obj 调用 _() 。但返回值变量竟然是 instance 实例对象。添加属性 _chain 赋值为 true ,并返回 intance 对象。但再看例子,实例对象竟然可以调用 reverse 方法,再调用 value 方法。猜测支持 OOP (面向对象)调用。

带着问题,笔者看了下定义 _ 函数对象的代码。

_ 函数对象 支持  OOP

如果参数 obj 已经是 _ 的实例了,则返回 obj 。如果 this 不是 _ 的实例,则手动 new_(obj) ; 再次 new 调用时,把 obj 对象赋值给 _wrapped 这个属性。也就是说最后得到的实例对象是这样的结构 {_wrapped:'参数obj',} 它的原型 _(obj).__proto___.prototype ;

如果对这块不熟悉的读者,可以看下以下这张图(之前写面试官问: JS的继承 画的图)。 riuIjmr.png!web

继续分析官方的 _.chain 例子。这个例子拆开,写成三步。

思考问题: reverse 本是 Array.prototype 上的方法呀。为啥支持链式调用呢。搜索 reverse ,可以看到如下这段代码:

并将例子代入这段代码可得(怎么有种高中做数学题的既视感^_^):

if((name==='shift'||name==='splice')&&obj.length===0)deleteobj[0]; 提一下上面源码中的这一句,看到这句是百思不得其解。于是赶紧在 github 中搜索这句加上 "" 双引号。表示全部搜索。

搜索到两个在官方库中的 ISSUE ,大概意思就是兼容IE低版本的写法。有兴趣的可以点击去看看。

I don't understand the meaning of this sentence.

why delete obj[0]

基于流的编程

至此就算是分析完了链式调用 _.chain()_ 函数对象。这种把数据存储在实例对象 {_wrapped:'',_chain:true} 中, _chain 判断是否支持链式调用,来传递给下一个函数处理。这种做法叫做 基于流的编程

最后数据处理完,要返回这个数据怎么办呢。 underscore 提供了一个 value 的方法。

顺便提供了几个别名。 toJSONvalueOf 。_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;

还提供了 toString 的方法。

这里的 String()newString() 效果是一样的。可以猜测内部实现和 _ 函数对象类似。

细心的读者会发现 chainResult 函数中的 _(obj).chain() ,是怎么实现实现链式调用的呢。

_(obj) 是返回的实例对象 {_wrapped:obj} 呀。怎么会有 chain() 方法,肯定有地方挂载了这个方法到 _.prototype 上或者其他操作,这就是 _.mixin()

_.mixin 挂载所有的静态方法到  _.prototype , 也可以挂载自定义的方法

_.mixin 混入。但侵入性太强,经常容易出现覆盖之类的问题。记得之前 Reactmixin 功能, Vue 也有 mixin 功能。但版本迭代更新后基本都是慢慢的都不推荐或者不支持 mixin

_mixin(_) 把静态方法挂载到了 _.prototype 上,也就是 _.prototype.chain 方法 也就是 _.chain 方法。

所以 _.chain(obj)_(obj).chain() 效果一样,都能实现链式调用。

关于上述的链式调用,笔者画了一张图,所谓一图胜千言。

2qmEnaB.jpg!web

_.mixin 挂载自定义方法

挂载自定义方法:举个例子:

_.functions(obj)

_.functions_.methods 两个方法,遍历对象上的方法,放入一个数组,并且排序。返回排序后的数组。

underscore.js 究竟在  _ 和  _.prototype 挂载了多少方法和属性

再来看下 underscore.js 究竟挂载在 _函数对象 上有多少静态方法和属性,和挂载 _.prototype 上有多少方法和属性。

使用 forin 循环一试遍知。看如下代码:

根据这些,笔者又画了一张图 underscore.js 原型关系图,毕竟一图胜千言。

uuIJFjB.jpg!web

整体架构概览

匿名函数自执行

这样保证不污染外界环境,同时隔离外界环境,不是外界影响内部环境。

外界访问不到里面的变量和函数,里面可以访问到外界的变量,但里面定义了自己的变量,则不会访问外界的变量。匿名函数将代码包裹在里面,防止与其他代码冲突和污染全局环境。关于自执行函数不是很了解的读者可以参看这篇文章。[译] JavaScript:立即执行函数表达式(IIFE)

root 处理

支持 浏览器环境nodeWebWorkernode vm微信小程序

导出

关于 root处理导出 的这两段代码的解释,推荐看这篇文章冴羽:underscore 系列之如何写自己的 underscore,讲得真的太好了。笔者在此就不赘述了。总之, underscore.js 作者对这些处理也不是一蹴而就的,也是慢慢积累,和其他人提 ISSUE 之后不断改进的。

支持 amd 模块化规范

_.noConflict 防冲突函数

源码:

使用:

总结

全文根据官网提供的链式调用的例子, _.chain([1,2,3]).reverse().value(); 较为深入的调试和追踪代码,分析链式调用( _.chain()_(obj

).chain() )、 OOP 、基于流式编程、和 _.mixin(_)_.prototype 挂载方法,最后整体架构分析。学习 underscore.js 整体架构,利于打造属于自己的函数式编程类库。

文章分析的源码整体结构。

下一篇文章可能是学习 lodash 的源码整体架构。

读者发现有不妥或可改善之处,欢迎评论指出。另外觉得写得不错,可以点赞、评论、转发,也是对笔者的一种支持。

了方便进行探讨和交流,我为大家建立了一个读者群,一起学习,一起进步。

3u6vM3n.jpg!web

:heart:爱心三连击

1.看到这里了就点个在看支持下吧,你的 「在看」 是我创作的动力。

2.关注公众号 达达前端「每天为您分享原创或精选文章」

3.特殊阶段,带好口罩,做好个人防护。

4.添加微信【xiaoda0423】,拉你进 技术交流群 一起学习

扫码关注公众号,订阅更多精彩内容。

aQbm6nv.png!web

好文章,我 在看


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK