Learning Underscorejs Source Code

_.iteratee = function(value, context, argCount) {}

1
2
3
4
5
6
7
8
9
// A mostly-internal function to generate callbacks that can be applied
// to each element in a collection, returning the desired result — either
// identity, an arbitrary callback, a property matcher, or a property accessor.
_.iteratee = function(value, context, argCount) {
  if (value == null) return _.identity;
  if (_.isFunction(value)) return createCallback(value, context, argCount);
  if (_.isObject(value)) return _.matches(value);
  return _.property(value);
};

_.iteratee的作用是生成callbacks,应用于collection的每个元素,返回分4种情况。

1) value == null

return _.identity

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

Example

1
2
3
4
> .identity('input value')
<- "input value"
> .iteratee(null)('input value')
<- "input value"

2) _.isFunction(value)

如果value为function, bind value(func) with context。 调用createCallback(value, context, argCount),return function(value, index, collection) {...}

3) _.isObject(value)

return _.matches(value) returns a predicate function.

4) else

return _.property(value)


createCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
var createCallback = function(func, context, argCount) {
  if (context === void 0) return func;
  switch (argCount == null ? 3 : argCount) {
    case 1: return function(value) {
      return func.call(context, value);
    };
    case 2: return function(value, other) {
      return func.call(context, value, other);
    };
    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);
  };
};

1) 没传参数context。直接返回参数func

1
if (context === void 0) return func;

Example:

1
2
3
4
> func = _.iteratee(console.log)
<- function log() { [native code] }
> func2(1)
VM419:2 Uncaught TypeError: Illegal invocation

这里有报错,参考stackoverflow。http://stackoverflow.com/questions/8159233/typeerror-illegal-invocation-on-console-log-apply。It may not work in cases when execution context changed from console to any other object

修改后:

1
2
3
4
> func2 = _.iteratee(function(value) { console.log.call(console, value) } )
<- function (value) { console.log.call(console, value) }
> func2(1)
  1

2) 传参数context,没传参数argCount

1
2
3
4
5
switch (argCount == null ? 3 : argCount) {
    case 3: return function(value, index, collection) {
      return func.call(context, value, index, collection);
    };
}

Example

1
2
3
4
5
6
> func = _.iteratee(function(v1,v2,v3){ console.info.call(console, v1+":"+v2+":"+v3) }, null)
<-  function (value, index, collection) {
        return func.call(context, value, index, collection);
      }
> func(1,0,[1])
   1:0:1


_.matches = function(attrs) {}

1
2
3
4
5
6
7
8
9
10
11
12
_.matches = function(attrs) {
  var pairs = _.pairs(attrs), length = pairs.length;
  return function(obj) {
    if (obj == null) return !length;
    obj = new Object(obj);
    for (var i = 0; i < length; i++) {
      var pair = pairs[i], key = pair[0];
      if (pair[1] !== obj[key] || !(key in obj)) return false;
    }
    return true;
  };
};

根据参数attrs,返回一个断言函数。

Example

1
2
3
4
5
6
7
> attrs = {"a":1, "b":2}
> obj1 = {"a":1, "c":3, "b":2}
> obj2 = {"a":2, "c":3}
> .matches(attrs)(obj1)
<- true
> .matches(attrs)(obj2)
<- false


_.property = function(key) {}

1
2
3
4
5
_.property = function(key) {
  return function(obj) {
    return obj[key];
  };
};

传入targetkey, 返回一个function。对返回的function传入obj即返回obj[targetkey]

Example:

1
2
3
4
5
> obj = {"a":1, "b":2}
> .property("a")(obj)
<- 1
> .property("b")(obj)
<- 2

_.keys = function(obj) {}

1
2
3
4
5
6
7
_.keys = function(obj) {
  if (!_.isObject(obj)) return [];
  if (nativeKeys) return nativeKeys(obj);
  var keys = [];
  for (var key in obj) if (_.has(obj, key)) keys.push(key);
  return keys;
};

nativeKeys = Object.keys,如果有原生方法,直接调用。

先看for (var key in obj)

1
2
3
4
5
6
7
> [3,4]
<- 3
<- 4

> {"a":1, "b":2}
<- "a"
<- "b"

.has 检查key是否属于obj本身

1
2
3
.has = function(obj, key) {
  return obj != null && hasOwnProperty.call(obj, key);
};


_.map = _.collect = function(obj, iteratee, context) {}

1
2
3
4
5
6
7
8
9
10
11
12
13
_.map = _.collect = function(obj, iteratee, context) {
  if (obj == null) return [];
  iteratee = _.iteratee(iteratee, context);
  var keys = obj.length !== +obj.length && _.keys(obj),
      length = (keys || obj).length,
      results = Array(length),
      currentKey;
  for (var index = 0; index < length; index++) {
    currentKey = keys ? keys[index] : index;
    results[index] = iteratee(obj[currentKey], currentKey, obj);
  }
  return results;
};
1
var keys = obj.length !== +obj.length && _.keys(obj)

如果obj为Array, obj.length !== +obj.length为false,false && [...] 为false

如果obj为Object, obj.length !== +obj.length为true, true && [...]为[…]