Open lessfish opened 8 years ago
再请教一个问题,.each和.map中,为什么前者的迭代函数是iteratee = optimizeCb(iteratee, context);而后者的却是iteratee = cb(iteratee, context);
@zhoucumt
好问题,这点我也十分诧异,个人觉得两者作用相同,可以互换。类似的还有 _.each
源码中用了 if else
结构,而 _.map
中没有用,我觉得也是一样的。唯一可以想到的原因是,可能为了测试 optimizeCb
和 cb
两个内部方法的正确性?
@zhoucumt cb 会检查 iteratee 是否为函数,只有当 iteratee 是函数时,才会在 cb 内部调用 optimizeCb 对 iteratee 进行优化(optimizeCb 只能优化函数)。 因为不像 each 那样 iteratee 肯定是函数,map 中 iteratee 可以是对象或字符串等: var results = _.map([{name:'cxp'},{name:'comma'}],'name'); // => results: ['cxp', 'comma']; 如果 iteratee 不是函数,cb 就不会调用 optimizeCb, 而是返回其他函数对 map 中传入的集合进行迭代。
可否采用 undefinedOnly === void 0 来判断,而非 arguments.length
如果在严格模式,这里用的arguments还行吗?
@ooooevan 不行。
@sqfbeijing 为什么不行,可以的啊,严格模式只是淘汰了arguments.callee 和 arguments.caller
@anotherleon caller 并不在 arguments对象上的
Why underscore
最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。
阅读一些著名框架类库的源码,就好像和一个个大师对话,你会学到很多。为什么是 underscore?最主要的原因是 underscore 简短精悍(约 1.5k 行),封装了 100 多个有用的方法,耦合度低,非常适合逐个方法阅读,适合楼主这样的 JavaScript 初学者。从中,你不仅可以学到用 void 0 代替 undefined 避免 undefined 被重写等一些小技巧 ,也可以学到变量类型判断、函数节流&函数去抖等常用的方法,还可以学到很多浏览器兼容的 hack,更可以学到作者的整体设计思路以及 API 设计的原理(向后兼容)。
之后楼主会写一系列的文章跟大家分享在源码阅读中学习到的知识。
欢迎围观~ (如果有兴趣,欢迎 star & watch~)您的关注是楼主继续写作的动力
createAssigner
(PS:本文有点水,没多少干货,主要想跟大家分享下这里的闭包)
今天要跟大家聊的是 underscore 源码中一个重要的内部方法,createAssigner。
这个方法是用来干嘛的呢?该方法涉及的 api 包括 .extend & .extendOwn & _.defaults,那么这三个 api 又是用来干嘛的呢?ok,先简单介绍下这三个 api 的用处。
首先思考这样一个场景,有 a,b 两个对象,将 b 所有的键值对都添加到 a 上面去,返回 a,如何写这个方法?恩,应该不难,刷刷刷写下如下代码:
我擦,这么少,这是真的吗?是真的,除了没有兼容 IE < 9 下某些 key 不能被 for ... in 枚举到的 bug(这个可以参考 前文)。你不禁开始怀疑人生,讲这个有毛的意义?
事实上,.extend 大概就是用来干上面 extend 函数的事情的;而 .extendOwn 则只会取 b 对象的 own properties,大同小异; .defaults 呢?跟 .extend 类似,但是如果 key 相同,后面的不会覆盖前面的,取第一次出现某 key 的 value,为 key-value 键值对。除此之外,三个方法都能接受 >= 1 个参数,以 .extend 为例,.extend(a, b, c) 将会将 b,c 两个对象的键值对分别覆盖到 a 上。
那么问题来了,如何设计这三个用途相似的 api?我们来看看 underscore 是怎么做的。
我们完整地看下带注释的 createAssigner 函数:
函数返回函数,并且返回的函数引用了外面的一个变量,这不正是经典的闭包?因为变量的个数可以 >=1,于是我们用 arguments 去获取变量。当变量个数为 1,或者第一个参数是 null 时,这时我们不需要做任何 "extend",直接返回第一个参数。之后,我们便可以用 arguments 枚举除去第一个参数外的其他参数,将它们的键值对覆盖到第一个参数对象上,具体可以看我的源码注释。