underscore 源码解读:Object Functions 相关源码拾遗


Why underscore
git

最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了个人 2016 计划中。github

阅读一些著名框架类库的源码,就好像和一个个大师对话,你会学到不少。为何是 underscore?最主要的缘由是 underscore 简短精悍(约 1.5k 行),封装了 100 多个有用的方法,耦合度低,很是适合逐个方法阅读,适合楼主这样的 JavaScript 初学者。从中,你不只能够学到用 void 0 代替 undefined 避免 undefined 被重写等一些小技巧 ,也能够学到变量类型判断、函数节流&函数去抖等经常使用的方法,还能够学到不少浏览器兼容的 hack,更能够学到做者的总体设计思路以及 API 设计的原理(向后兼容)。数组

以后楼主会写一系列的文章跟你们分享在源码阅读中学习到的知识。浏览器

欢迎围观~ (若是有兴趣,欢迎 star & watch~)您的关注是楼主继续写做的动力函数

Main学习

今天把源码解读部分 Object Functions 更新完毕。this

若是你有心,可能就会发现楼主以前的解读系列文章说的都是 Object 上的扩展方法,也就是源码中 Object Functions 部分。underscore 为 5 种类型添加了扩展方法,分别是 Object -> Array -> Collection -> Function -> Utility,这也正是楼主的源码解读顺序(并非源码顺序)。其中,Object 上的扩展方法多达 38 个,方法多并不表明代码多,好比类型检测,两行代码就能够搞定好几个方法,而上一篇中说的 _.isEqual 方法,却要百来行去实现。今天是 Object Functions 部分的最后一篇,咱们来看看楼主认为的几个没被解读过的可是却有意思的方法的源码。(其实不少方法使用简单,实现也很是简单,有兴趣的同窗能够本身扒下源码)prototype

_.pick

首先来看看 _.pick 方法,该方法传入一个对象,而后删选对象的键值对,返回一个对象副本。

直接来看例子:

_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');

=> {name: 'moe', age: 50}

_.pick({name: 'moe', age: 50, userid: 'moe1'}, ['name', 'age']);

=> {name: 'moe', age: 50}

_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {

  return _.isNumber(value);

});

=> {age: 50}

一目了然,第一个参数 obj 是对象,第二个参数能够是一系列的 key 值,也能够是数组(数组中含 key),也能够是迭代函数,咱们根据 key 值,或者迭代函数来过滤 obj 中的键值对,返回新的对象副本。

若是让我来设计,估计会根据参数来判断类型,而后写几个 if-else,每一个 if-else 分支里的内容毫无关联。可是 underscore 的写法简直美妙,将几种状况转为了一种。

// 若是第二个参数是函数

if (_.isFunction(oiteratee)) {

  keys = _.allKeys(obj);

  iteratee = optimizeCb(oiteratee, context);

}

首先 if-else 是不可避免的,若是传入的第二个参数是 function,那么就是传入迭代函数了,根据 context(this)返回新的迭代函数(optimizeCb 我之后会讲,就是规定了迭代函数中的 this 指向,不是很重要,这里能够选择性忽略)。

若是第二个参数不是函数,则后面的 keys 多是数组,也多是连续的几个并列的参数。这里咱们要用到 underscore 中另外一个重要的内部方法,flatten,它的做用是将嵌套的数组展开,这个方法我之后会分析,这里知道它的做用就能够了。

else {

  // 若是第二个参数不是函数

  // 则后面的 keys 多是数组

  // 也多是连续的几个并列的参数

  keys = flatten(arguments, false, false, 1);

  // 也转为 predicate 函数判断形式

  // 将指定 key 转化为 predicate 函数

  iteratee = function(value, key, obj) { return key in obj; };

  obj = Object(obj);

}

也转为和传入迭代函数同样的形式,就能够用一个方法判断了,并且 keys 变量在两种状况下的意义是不一样的,真的很是巧妙。这点令我思考良多,不少时候的代码冗余,其实大概是本身代码能力太差了吧,打死我也想不到这样作。

_.create

_.create 方法很是简单,根据你给的原型(prototype),以及一些 own properties,构造新的对象返回。

举个简单的例子:

var Person = function() {};

Person.prototype = {

  show: function() {

    alert(this.name);

  }

};

var me = _.create(Person.prototype, {name: 'hanzichi'});

console.log(me);

// Object {name: "hanzichi"}

//   name: "hanzichi"

//   __proto__: Object

//     show: function()

其实 me 变量就是一个拥有 name 做为 own properties,且用 Person 函数构造的对象。

若是浏览器支持 ES5,咱们能够用 Object.create():

var Person = function() {};

Person.prototype = {

  show: function() {

    alert(this.name);

  }

};

var me = Object.create(Person.prototype);

_.extendOwn(me, {name: 'hanzichi'});

console.log(me);

若是不支持 ES5,咱们能够新定义一个构造函数,将该构造函数的 prototype 赋值为已知的 prototype 变量,而后用 new 运算符来获取实例:

var Person = function() {};

Person.prototype = {

  show: function() {

    alert(this.name);

  }

};

var _Person = function() {};

_Person.prototype = Person.prototype;

var me = new _Person();

_.extendOwn(me, {name: 'hanzichi'});

console.log(me);

undercore 的实现思路也大抵如此。

_.tap

原本打算把 _.tap 讲掉,忽然以为跟链式调用一块儿讲比较好,挖个坑。

小结

关于 Objects Function 部分的源码剖析就到这了,具体这部分代码能够参考 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L901-L1269。虽说看完了这部分代码,可是要真正理解消化仍是须要时间的,我只能说本身理解了 90% 左右,欢迎探讨交流。

相关文章
相关标签/搜索