牛小葵

牛气冲天 一举夺魁

  • 主页
  • JavaScript
  • 读书笔记
  • VUE
所有文章 关于我

牛小葵

牛气冲天 一举夺魁

  • 主页
  • JavaScript
  • 读书笔记
  • VUE

学习underscore中cb和optimizeCb函数

2019-02-13

摘要:学习冴羽的博客研究 underscore 中 cb 和 optimizeCb 函数

_.map

_.map 函数中有使用 cb 函数:

1
2
3
4
5
6
7
8
9
10
11
// 简化过,这里仅假设 obj 是数组
_.map = function (obj, iteratee, context) {
iteratee = cb(iteratee, context);

var length = obj.length, results = Array(length);
for (var index = 0; index < length; index++) {
results[index] = iteratee(obj[index], index, obj);
}

return results;
};

_.map 的三个参数: obj 是待处理数据; iteratee 是处理函数; context 是指定的执行上下文,即 this 的值

cb

cb 函数源码:

1
2
3
4
5
6
7
8
9
10
11
12
var cb = function (value, context, argCount) {

if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);

if (value == null) return _.identity;

if (_.isFunction(value)) return optimizeCb(value, context, argCount);

if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);

return _.property(value);
};

cb 函数对传入的 value 值类型进行判断,然后根据不同的类型,返回不同的函数。涉及到 8 个函数,其中 _.iteratee 、 _.identity 、optimizeCb 、 _.matcher 、 _.property 这五个函数还没有接触过

_.iteratee

_.iteratee 的源码:

1
2
3
_.iteratee = builtinIteratee = function (value, context) {
return cb(value, context, Infinity);
};

因为源码中_.iteratee = builtinIteratee,所以通常情况下_.iteratee !== builtinIteratee为 false。但是当我们在外部修改 _.iteratee 函数后,则返回 true,执行_.iteratee(value, context),也就是使用自定义的 _.iteratee 函数来处理 value 和 context
因为 underscore 中的多个函数都使用了 cb 函数,而 cb 函数又使用了 _.iteratee 函数,所以要修改 _.iteratee 函数的话,一定要谨慎操作

_.identity

_.identity 的源码:

1
2
3
_.identity = function(value) {
return value;
};

_.identity 函数就是将传入的第一个参数原样返回,当 value 值为 null 或 undefined 时,返回 _.identity

optimizeCb

optimizeCb 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var optimizeCb = function (func, context, argCount) {
// 如果没有传入指定的上下文 context,就返回 func 函数
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function (value) {
return func.call(context, value);
};
// 值为2的情况被省略了,因为 underscore 函数中没有用到
case 3: return function (value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function (accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function () {
return func.apply(context, arguments);
};
};

传入的三个参数:func 是处理函数; context 是指定的执行上下文 this ; argCount 是返回函数参数的个数
void的返回值都是undefined,用void 0是为了防止undefined被重写而出现判断不准确的情况

ES5之后的标准中,规定了全局变量下的undefined值为只读,不可改写的,但是局部变量中依然可以对之进行改写。

中间使用 switch 对 argCount 进行判断而不是直接返回下面的匿名函数,可以避免使用 arguments 以提高一点性能

_.matcher

_.matcher 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
var nativeIsArray = Array.isArray;

_.isArray = nativeIsArray || function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};

_.isObject = function (obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};

_.extendOwn = function () {
var obj, key, src, value, valueIsArray, typeTarget;
// 默认不是深复制
var deep = false;
var length = arguments.length;
// 默认第一个是目标对象
var target = arguments[0] || {};
var i = 1;
if (typeof target === "boolean") {
deep = target;
target = arguments[i] || {};
i++;
}
// 复制属性要求 target 必须为对象(包含函数),如果不是,就设为{}
if (typeof target !== "object" && !isFunction(target)) {
target = {};
}
for (; i < length; i++) {
obj = arguments[i];
if (obj != null) {
for (key in obj) {
src = target[key];
value = obj[key];
// 如果待复制对象的属性值与目标对象 target 相同就跳出
if (target === value) {
continue;
}
// 如果要求深复制且属性值是引用类型,就进行递归
if (deep && value && (isPlainObject(value) || (valueIsArray = Array.isArray(value)))) {
if (valueIsArray) {
valueIsArray = false;
typeTarget = src && Array.isArray(src) ? src : [];
} else {
typeTarget = src && isPlainObject(src) ? src : {};
}
target[key] = _extend(deep, typeTarget, value);
} else if (value !== undefined) {
target[key] = value;
}
}
}
}
return target;
}

_.matcher = _.matches = function (attrs) {
attrs = _.extendOwn({}, attrs);
return function (obj) {
return _.isMatch(obj, attrs);
};
};

// 判断 attrs 对象中的键值是否在 object 中有并且相等。如果有且相等,返回 true
_.isMatch = function (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;
};

_.matcher 就是用来处理当 map 的第二个参数是对象的情况
调用效果如下:

1
_.map([{name:'Kevin'}, {name: 'Daisy', age: 18}], {name: 'Daisy'}); // [false, true]

_.property

_.property 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
_.property = function (path) {
// 如果不是数组
if (!_.isArray(path)) {
return shallowProperty(path);
}
return function (obj) {
return deepGet(obj, path);
};
};

var shallowProperty = function (key) {
return function (obj) {
return obj == null ? void 0 : obj[key];
};
};

// 根据路径取出深层次的值
var deepGet = function (obj, path) {
var length = path.length;
for (var i = 0; i < length; i++) {
if (obj == null) return void 0;
obj = obj[path[i]];
}
return length ? obj : void 0;
};

_.property 用来处理当 value 是基本类型的值的情况,会返回元素对应的属性值
调用效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
_.map([{ name: 'Kevin' }, { name: 'Daisy' }], 'name'); // ['Kevin', 'daisy']

var person1 = {
child: {
nickName: 'Kevin'
}
}
var person2 = {
child: {
nickName: 'Daisy'
}
}
// 获取深层次属性值
_.map([person1, person2], ['child', 'nickName']); // ['Kevin', 'daisy']
  • JavaScript
  • underscore
学习underscore中restArgs函数
学习underscore链式调用
© 2023 牛小葵
Hexo Theme Yilia by Litten
  • 所有文章
  • 关于我

tag:

  • CSS
  • JavaScript
  • Flex
  • 工具
  • HTML
  • JSON
  • 浏览器
  • node
  • MongoDB
  • Vue
  • 笔记
  • 正则
  • 数据结构与算法
  • underscore
  • 小程序
  • 压缩
  • 总结
  • 目标
  • 设计模式
  • HTTP
  • 【安全】
  • Chrome

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

持续学习