先过滤掉underscore内部各个工具函数的具体逻辑,只看源码库自己有什么内容。
underscore有两种调用方式:javascript
_.map([1, 2, 3], function(n){ return n * 2; });
_([1, 2, 3]).map(function(n){ return n * 2; });
_
是一个函数对象,api中的函数全都挂载到_
上,实现_.func
java
// 使用当即执行函数 (function () { // 定义全局对象 var root = typeof self == 'object' && self.self === self && self || typeof global == 'object' && global.global === global && global || this || {}; // 省略... // 建立_对象 var _ = function(obj) { // 若是参数是underscore的一个实例,就直接返回该参数 if (obj instanceof _) return obj; // 对应_(xxx)调用,若是new实例不是_的实例,就返回实例化_对象 if (!(this instanceof _)) return new _(obj); // 并将数据对象包装在实例对象上 this._wrapped = obj; }; // 省略... //注册在全局对象 root._=_; })();
上一步中,咱们建立了underscore实例,只能支持_.func
调用,若是要支持_(obj).func
,同时还要将func
注册在实例的prototype
上。试想下,若是每声明一个函数,就要绑定一次,那得用多…编程
在underscore中,使用mixin
将自定义函数添加到Underscore对象。api
// 用于返回排序后的数组,包含全部的obj中的函数名。 _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; _.mixin = function(obj) { // 遍历obj的函数,绑定在原型上 _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { // this._wrapped做为第一个参数传递,其余用户传递的参数放在后面。 var args = [this._wrapped]; push.apply(args, arguments); return chainResult(this, func.apply(_, args)); }; }); return _; }; // 执行混入 _.mixin(_);
大体流程是:数组
_(args).func(argument)
参数进行合并_
自身定义一系列函数,经过_.mixin()
绑定在了_.prototype
上,提升了代码的复用度。app
相似于 Java Stream 流式编程函数
在Javascript中,数据能够像是在管道中流通,咱们称之为,声明式编程/连接式调用。工具
data.filter(...).unique(...).map(...)
既然要知足连接式调用(Chaining)语法,须要知足两个条件学习
可能会想到,在每一个函数结尾return this;
,对,能用可是不必,除了要手动添加外,咱们也没法关闭链式。this
在underscore中,使用的是可选式实现连接编程,使用.chain()
来开启链式调用,而后使用.value()
获取函数的最终值。
关键函数:
// 开启链式调用 _.chain = function (obj) { //返回一个封装的对象. 在封装的对象上调用方法会返回封装的对象自己 var instance = _(obj) instance._chain = true //检测对象是否支持链式调用 return instance } // 辅助函数:链式中转 // 链式调用 将数据对象封装在underscore实例中 // 非链式调用 返回数据对象 var chainResult = function(instance, obj) { return instance._chain ? _(obj).chain() : obj; }; var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; _.prototype.value = function() { return this._wrapped; };
这次学习目标不是为了学习api,而是经过将其设计思想转换为本身的。经过以上几点,咱们能够大概实现一个简化版的underscore,
简化版:
(function (root) { var _ = function (obj) { if (!(this instanceof _)) { return new _(obj) } this.warp = obj } _.unique = function (arr, callback) { var result = [] var item for (var i = 0; i < arr.length; i++) { item = callback ? callback(arr[i]) : arr[i] if (result.indexOf(item) === -1) { result.push(item) } } return result } // 获取对象上的函数 _.functions = function (obj) { var result = [] for (let key in obj) { result.push(key) } return result } // 执行链式操做 _.chain = function (obj) { //数据源 var instance = _(obj) instance._chain = true //检测对象是否支持链式调用 return instance } //辅助函数 将数据包装为underscore实例 var ret = function (instance, obj) { if (instance._chain) { instance.warp = obj return instance } return obj } _.map1 = function (obj) { obj.push('123', 'hello') return obj } // 关闭链式调用 返回数据自己 _.prototype.value = function () { return this.warp } _.each = function (arr, callback) { var i = 0 for (; i < arr.length; i++) { callback.call(arr, arr[i]) } //console.log(arr) } // 检测静态方法 name 存放在数组中 // 遍历数组 给_.prototype进行注册 _.mixin = function (obj) { _.each(_.functions(obj), function (key) { var func = obj[key] //console.log(key) _.prototype[key] = function () { //console.log(this.warp) //数据源 //console.log(arguments) //callback // 进行参数合并 var args = [this.warp] Array.prototype.push.apply(args, arguments) return ret(this, func.apply(this, args)) } }) } _.mixin(_) root._ = _ })(this)
调用:
console.log(_.unique([1,2,3,1,2,3,'a','A'],function (item) { // 过滤大小写 return typeof item ==='string'?item.toLowerCase():item })) console.log(_([4,5,6,4,5,6,'b','B']).chain().unique(function (item) { // 过滤大小写 return typeof item ==='string'?item.toLowerCase():item }).map1().value())