-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
underscore 系列之内部函数 cb 和 optimizeCb #58
Comments
留此位置用作更新日志 |
怎么越来越少人看啊哈啊哈或或或或或 |
那些浏览器兼容代码不去原ticket的话满脸懵逼,看的话又好费时间... |
难啊~ |
看完了, |
map 方法除了传入要处理的数组之外,还有两个参数 iteratee 和 context,类似于 Array.prototype.map 中的其他两个参数。不太明白这里说的 类似于Array.prototype.map 中的其他两个参数。Array.prototype.map的语法是这样的, |
|
// extend 函数可以参考 《JavaScript 专题之手写一个 jQuery 的 extend》 |
underscore.js@1.13.2 function matcher(attrs) {
// extendOwn 相当于 Object.assign()
// 这行代码就是对 attrs 做了一个浅拷贝
attrs = extendOwn({}, attrs);
return function(obj) {
return isMatch(obj, attrs);
};
}
function isMatch(object, attrs) {
var _keys = keys(attrs), length = _keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = _keys[i];
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
} 同楼上一样,没看明白为什么需要对 |
浅拷贝是为了避免这个对象上的原型造成的属性遮蔽问题,因为后面有一个 key(arrs) 遍历,遍历通常使用的 for....in 而这个会把对象的原型上的东西都能找到,所以才会使用一次浅拷贝的方式,将其拷贝到没有原型的空对象上,一句话:干净的底子,没有太多误会 |
前言
仅看 cb 和 optimizeCb 两个函数的名字,你可能想不到这是用来做什么的,尽管你可能想到 cb 是 callback 的缩写。
如果直接讲解源码,你可能想不明白为什么要这么写,所以我们从 _.map 函数开始讲起。
_.map
_.map 类似于
Array.prototype.map
,但更加健壮和完善。我们看下 _.map 的源码:map 方法除了传入要处理的数组之外,还有两个参数 iteratee 和 context,类似于
Array.prototype.map
中的其他两个参数,其中 iteratee 表示处理函数,context 表示指定的执行上下文,即 this 的值。然后在源码中,我们看到,我们将 iteratee 和 context 传入一个 cb 函数,然后覆盖掉 iteratee 函数,然后将这个函数用作最终的处理函数。
实际上,需要这么麻烦吗?不就是使用 iteratee 函数处理每次迭代的值吗?不就是通过 context 指定 this 的值吗?我们可以直接这样写呐:
你看看也没有什么问题呐,可是,万一 iteratee 我们不传入一个函数呢?比如我们什么也不传,或者传入一个对象,又或者传入一个字符串、数字呢?
如果用我们的方法自然是会报错的,那 underscore 呢?
我们会发现,underscore 竟然还能根据传入的值的类型不同,实现的效果不同。我们总结下:
由此,我们可以推测在 underscore 的 cb 函数中,有对 iteratee 值类型的判断,然后根据不同的类型,返回不同的 iteratee 函数。
cb
所以我们来看看 cb 函数的源码:
这一看就牵扯到了 8 个函数!不要害怕,我们一个一个看。
_.iteratee
我们看看 _.iteratee 的源码:
因为
_.iteratee = builtinIteratee
的缘故,_.iteratee !== builtinIteratee
值为 false,所以正常情况下_.iteratee(value, context)
并不会执行。但是如果我们在外部修改了 _.iteratee 函数,结果便会为 true,cb 函数直接返回
_.iteratee(value, context)
。这个意思其实是说用我们自定义的 _.iteratee 函数来处理 value 和 context。
试想我们并不需要现在 _.map 这么强大的功能,我只希望当 value 是一个函数,就用该函数处理数组元素,如果不是函数,就直接返回当前元素,我们可以这样修改:
当然更多的情况是自定义对不同的 value 使用不同的处理函数,值得注意的是,underscore 中的多个函数都是用了 cb 函数,而因为 cb 函数使用了 _.iteratee 函数,如果你修改这个函数,其实会影响多个函数,这些函数基本都属于集合函数,具体包括 map、find、filter、reject、every、some、max、min、sortBy、groupBy、indexBy、countBy、sortedIndex、partition、和 unique。
_.identity
让我们看看 _.identity 的源码:
这也就是为什么当 map 的第二个参数什么都不传的时候,结果会是一个相同数组的原因。
如果直接看这个函数,可能觉得没有什么用,但用在这里,却又十分的合适。
optimizeCb
当 value 是一个函数的时候,就传入 optimizeCb 函数,我们来看看 optimizeCb 函数:
也许你会好奇,为什么我要对 argCount 进行判断呢?就不能直接返回吗?比如这样:
当然没有问题,但为什么 underscore 要这样做呢?其实就是为了避免使用 arguments,提高一点性能而已,如果不是写一个库,其实还真是没有必要做到这点。
而为什么当参数是 3 个时候,参数名称分别是 value, index, collection ,又为什么没有参数为 2 的情况呢?其实这都是根据 underscore 函数用到的情况,没有函数用到两个参数,于是就省略了,像 map 函数就会用到 3 个参数,就根据这三个参数的名字起了这里的变量名啦。
_.matcher
这段就是用来处理当 map 的第二个参数是对象的情况:
如果 value 是一个对象,并且不是数组,就使用 _.matcher 函数。看看各个函数的源码:
_.property
这个就是处理当 value 是基本类型的值的时候,返回元素对应的属性值的情况:
我们看下源码:
我们好像发现了新大陆,原来 value 还可以传一个数组,用来取深层次的值,举个例子:
最后
如果你想学习 underscore 的源码,在分析集合相关的函数时一定会接触 cb 和 optimizeCb 函数,先掌握这两个函数,会帮助你更好更快的解读源码。
underscore 系列
underscore 系列目录地址:https://github.com/mqyqingfeng/Blog。
underscore 系列预计写八篇左右,重点介绍 underscore 中的代码架构、链式调用、内部函数、模板引擎等内容,旨在帮助大家阅读源码,以及写出自己的 undercore。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: