-
Notifications
You must be signed in to change notification settings - Fork 0
Description
underscore.js 三种调用方式
函数式调用
// index.js
const { map, filter } = require('underscore');
map([1, 2, 3], v => v + 1); // [2, 3, 4]
filter([1, 2, 3], v => v >= 2); //[2, 3]下面我们来看看 underscore.js v1.9.2 源码中, 关于 _ 函数部分
// underscore.js v1.9.2
// ...
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
_.each = _.forEach = () => { /* some code */};
_.map = () => {/* some code */}
//...通过这段代码,我们可以知道,underscore.js 源码里声明了一个名称为 _ 的函数,_.each、_.forEach 及 _.map 等等方法是 _ 函数(本质上也是对象)的实例属性,可以用普通对象描述,加以理解:
// underscore.js
const _ = {
each: () => {},
forEach: () => {},
map: () => {}
// ...
};
module.exports = _;
// index.js
const { map, filter } = require('underscore.js');
map([1, 2, 3], v => v + 1); // [2, 3, 4]
filter([1, 2, 3], v => v >= 2); //[2, 3]通常情况下,我们使用 underscore.js 里的方法,都是按照以上的方式进行使用。
原生 js 调用
// underscore.js v1.9.2
const _ = require('underscore');
const result = _([1,2,3]).map(v => v + 1) // [2, 3, 4]
// 经过 map 方法返回的结果是原生数组,则只能使用 js 语法中的数组方法进行。
const filterResult = _([1,2,3]).map(v => v + 1).filter(v => v >= 3) // [3, 4]_([1, 2, 3]).map() 是面向对象式的调用方式。在源码里声明了一个 _ 函数,当使用 new _(), 该 _ 函数作为构造函数。在 underscore 源码中:
// underscore.js v1.9.2
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// ..._(obj) 这样的调用形式中,即执行 new _(obj) ,创建了一个 _ 的实例, this 指向创建的实例对象,同时也创建它的原型对象(_.prototype)。
// underscore.js v1.9.2
_.mixin = function(obj) {
/*
_.functions(obj) 返回一个 _ 实例上的所有方法名组成的数组
*/
_.each(_.functions(obj), function(name) {
var func = (_[name] = obj[name]);
/*
将 _ 函数(对象)上的方法全部添加到 _.prototype 对象上
*/
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
/*
最后都要返回 _ 实例,才能启用链式调用
this 指向 _.prototype
*/
return chainResult(this, func.apply(_, args));
};
});
/*
_ 是一个类
*/
return _;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);_.mixin(_) 的作用是把 _ 函数(本质上是对象)上的方法全部添加到 _.prototype 对象上。再来看看 chainResult 函数。
// underscore.js v1.9.2
var chainResult = function(instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};chainResult(this, func.apply(_, args)) 中,this 指向的是 _ 类的实例,根据 chainResult 函数的定义可知, 如果 _ 实例的 _chain 属性为 false, 则直接返回 func.apply(_, args) 的运行结果,其结果一定是原生 js 数据类型。_ 实例的 _chain 属性为 true, 则返回 _{_wrapped: Array(length), _chain: true}。
// underscore.js v1.9.2
_.chain = function(obj) {
/*
这里的 _(obj) === new _(obj)
*/
var instance = _(obj);
// 设置 _chain 属性为 true
instance._chain = true;
// 返回创建的实例
return instance;
};只要执行 _(obj).chain() 一次,_chain 的值就会被永久设置成 true。
以 _([1,2,3]).map(v => v + 1) 形式使用,返回的结果是原生数组,则只能使用 js 语法中的数组方法。
// underscore.js v1.9.2
const _ = require('underscore');
// 经过 map 方法返回的结果是原生数组,则只能使用 js 语法中的数组方法进行。
const filterResult = _([1,2,3]).map(v => v + 1).filter(v => v >= 3) // [3, 4]
// 诸如 compact、where、reject 等方法接着以链式使用使用 underscore 所有方法的链式调用
// underscore.js v1.9.2
const _ = require('underscore');
_chain([1, 2, 3]).map(v => v + 1).filter(v => v >= 3) // _{_wrapped: Array(2), _chain: true}
_([1, 2, 3]).chain().map(v => v + 1).filter(v => v >=3) // _{_wrapped: Array(2), _chain: true}在 原生 js 调用 部分我讲到,只要调用一次 chain () 方法,就会将它的 _chain 设置为 true,之后每一次调用方法,返回值均为 _ 类的实例,所以它能以链式方式调用 underscore.js 里提供的所有方法。
// underscore.js v1.9.2
_.prototype.value = function() {
return this._wrapped;
};
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
_.prototype.toString = function() {
return String(this._wrapped);
};因为这四个方法没有返回 _ 类的实例,所以以链式方式调用这四个方法时,会终止链式。