深入解析Underscore.js源码架构

Underscore.js是很有名的一个工具库,我也经常用他来处理对象,数组等,本文会深入解析Underscore源码架构,跟大家一起学习下他源码的亮点,然后模仿他写一个简单的架子来加深理解。他的源码通读下来,我觉得他的亮点主要有如下几点:

不需要new的构造函数

同时支持静态方法调用和实例方法调用

支持链式调用

本文的例子已经上传到GitHub,同一个repo下还有我全部的博文和例子,求个star:

https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/Architecture/Underscore

外层是一个自执行函数

Underscore外层就是一个自执行函数,在自执行函数里面将_挂载到了window上。这是很多第三方库惯用的套路。如果你还不知道怎么入手看源码,不知道入口在哪里,或者看不懂他的外层结构,请看从架构入手轻松读懂框架源码:以jQuery,Zepto,Vue和lodash-es为例,这篇文章详细讲解了怎么入手看源码。本文主要讲解Underscore源码架构里面的亮点,怎么入手就不再赘述了。

不用new的构造函数

我们在使用第三方库的时候,经常需要先拿一个他们的实例,有些库需要用new来显式的调用,比如原生的Promise,有些库不需要new也可以拿到实例对象,比如jQuery。不用new就返回一个实例原生JS肯定是不支持的,有这些特性的库都是自己封装了一层的。不同的库在封装的时候也有不同的思路,下面我们来讲讲其中两种方案。

jQuery的方案

之前我在另一篇文章从架构入手轻松读懂框架源码:以jQuery,Zepto,Vue和lodash-es为例中详细讲解了jQuery是怎么实现不用new就返回一个实例的。另外还模仿jQuery的这种方案实现了我自己的一个工具库:学以致用:手把手教你撸一个工具库并打包发布,顺便解决JS小数计算不准问题。这里贴一段我工具库文章的代码简单回顾下这种方案:

// 首先创建一个fc的函数,我们最终要返回的其实就是一个fc的实例 // 但是我们又不想让用户new,那么麻烦 // 所以我们要在构造函数里面给他new好这个实例,直接返回 function FractionCalculator(numStr, denominator) { // 我们new的其实是fc.fn.init return new FractionCalculator.fn.init(numStr, denominator); } // fc.fn其实就是fc的原型,算是个简写,所有实例都会拥有这上面的方法 FractionCalculator.fn = FractionCalculator.prototype = {}; // 这个其实才是真正的构造函数,这个构造函数也很简单,就是将传入的参数转化为分数 // 然后将转化的分数挂载到this上,这里的this其实就是返回的实例 FractionCalculator.fn.init = function(numStr, denominator) { this.fraction = FractionCalculator.getFraction(numStr, denominator); }; // 前面new的是init,其实返回的是init的实例 // 为了让返回的实例能够访问到fc的方法,将init的原型指向fc的原型 FractionCalculator.fn.init.prototype = FractionCalculator.fn; // 调用的时候就不用new了,直接调用就行 FractionCalculator(); Underscore的方案

jQuery的方案是在构造函数里面new了另外一个对象,然后将这个对象的原型指向jQuery的原型,以便返回的实例能够访问jQuery的实例方法。目的是能够达到的,但是方案显得比较冗长,Underscore的方案就简洁多了:

function _(){ if(!(this instanceof _)) { return new _(); } } // 调用的时候直接_()就可以拿到实例对象 const instance = _(); console.log(instance);

上面代码的输出是:

image-20200318155826651

可以看到constructor指向的是_(),说明这真的是一个_的实例,我们来分析下代码执行流程:

调用_(),里面的this指向外层的作用域,我们这里是window,因为window不是_的实例,会走到if里面去。关于this指向,如果你还不是很明白,请看这篇文章。

if里面会调用new _(),这会拿到一个实例对象,并将这个对象return出去。new _()也会调到_()方法,但是因为使用new调用,里面的this指向的就是new出来的实例,所以if进不去,执行结束。

Underscore巧妙应用了this的指向,通过检测this的指向来判断你是new调用的还是普通调用的,如果是普通调用就帮你new一下再返回。

同时支持静态方法和实例方法

用过Underscore的朋友应该有注意到,对于同一个方法来说,Underscore既支持作为静态方法调用,也支持作为实例方法调用,下面是官方的例子:

_.map([1, 2, 3], function(n){ return n * 2; }); // map作为静态方法调用 _([1, 2, 3]).map(function(n){ return n * 2; }); // map作为实例方法调用

当我们把方法作为静态方法调用的时候,需要处理的数据就是第一个参数;当把他作为实例方法调用的时候,待处理数据是作为参数传给构造函数的。下面我们来讲讲这是怎么实现的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppxgf.html