资讯专栏INFORMATION COLUMN

【underscore 源码解读】Object Functions 相关源码拾遗 & 小结

neuSnail / 3090人阅读

摘要:直接来看例子一目了然,第一个参数是对象,第二个参数可以是一系列的值,也可以是数组数组中含,也可以是迭代函数,我们根据值,或者迭代函数来过滤中的键值对,返回新的对象副本。

Why underscore

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

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

之后楼主会写一系列的文章跟大家分享在源码阅读中学习到的知识。

underscore-1.8.3 源码解读项目地址 https://github.com/hanzichi/underscore-analysis

underscore-1.8.3 源码全文注释 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/underscore-1.8.3-analysis.js

underscore-1.8.3 源码解读系列文章 https://github.com/hanzichi/underscore-analysis/issues

欢迎围观~ (如果有兴趣,欢迎 star & watch~)您的关注是楼主继续写作的动力

Main

趁着今天是工作日的最后一天,把源码解读部分 Object Functions 更新完毕。

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

_.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% 左右,欢迎探讨交流。

接下去应该会着手看 Array 扩展方法相关代码,继续为大家整理有意思的东西,各种奇淫怪巧,敬请期待。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79572.html

相关文章

  • underscore 源码解读】Array Functions 相关源码拾遗 & 小结

    摘要:最近开始看源码,并将源码解读放在了我的计划中。将转为数组同时去掉第一个元素之后便可以调用方法总结数组的扩展方法就解读到这里了,相关源码可以参考这部分。放个预告,下一篇会暂缓下,讲下相关的东西,敬请期待。 Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。 阅读一些著名框架类库的源码,就好...

    SimpleTriangle 评论0 收藏0
  • Underscore 整体架构浅析

    摘要:支持形式的调用这其实是非常经典的无构造,其实就是一个构造函数,的结果就是一个对象实例,该实例有个属性,属性值是。 前言 终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度。十一月,多事之秋,最近好多事情搞的楼主心力憔悴,身心俱疲,也想尽快把这个系列完结掉,也好了却一件心事。 本文...

    ningwang 评论0 收藏0
  • underscore 源码解读】如何优雅地写一个『在数组中寻找指定元素』的方法

    摘要:今天要讲的是,如何在数组中寻找元素,对应中的,,,以及方法。如果往一个有序数组中插入元素,使得数组继续保持有序,那么这个插入位置是这就是这个方法的作用,有序,很显然用二分查找即可。 Why underscore (觉得这部分眼熟的可以直接跳到下一段了...) 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。 阅读一...

    0x584a 评论0 收藏0
  • underscore.js 源码解读】for ... in 存在的浏览器兼容问题你造吗

    摘要:最近开始看源码,并将源码解读放在了我的计划中。像和使用内置构造函数所创建的对象都会继承自和的不可枚举属性,例如的方法或者的方法。循环将迭代对象的所有可枚举属性和从它的构造函数的继承而来的包括被覆盖的内建属性。 Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。 阅读一些著名框架类库的源码...

    Pikachu 评论0 收藏0
  • underscore.js 源码解读】常用类型判断以及一些有用的工具方法

    摘要:最近开始看源码,并将源码解读放在了我的计划中。今天就跟大家聊一聊中一些常用类型检查方法,以及一些工具类的判断方法。用是否含有属性来判断工具类判断方法接下来看下一些常用的工具类判断方法。 Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。 阅读一些著名框架类库的源码,就好像和一个个大师对话...

    tanglijun 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<