Skip to content
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源码解析 #38

Open
huangchucai opened this issue Sep 27, 2017 · 1 comment
Open

underscore源码解析 #38

huangchucai opened this issue Sep 27, 2017 · 1 comment

Comments

@huangchucai
Copy link
Owner

huangchucai commented Sep 27, 2017

underscore源码解析

源码地址

&&和||返回值解析

经常我们在项目中会使用条件判定,&&必须2边都是true的时候,才会返回true, ||有一边是true就返回true, 很少关心是否它的返回值,因为if语句会自动转换成布尔值。下面我们来讨论下,它不转换成布尔值的时候返回的值

// && 
true && 3  => 3
false && 2 => false 
0 && false => 0

// || 
false || 3 => 3
9 || 0 => 9
undefined || 4 => 4

总结: 从上面可以看出,返回值是有一个规律的,当&&||的值决定的时候,返回那个起决定作用的值

公共函数

  1. createCallback

    // createCallback函数
    var createCallback = function(func, context, argCount) {
      // 如果没有传递上下文context 则直接返回函数 (void 0 === undefined)
      if (context === void 0) return func;
      // 这里可以看出underscore的内部通过参数的长度进行了分类 默认值第三类
      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);
      };
    };

公共变量

var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

var push             = ArrayProto.push,
    slice            = ArrayProto.slice,
    concat           = ArrayProto.concat,
    toString         = ObjProto.toString,
    hasOwnProperty   = ObjProto.hasOwnProperty;

var nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys,
    nativeBind         = FuncProto.bind;

集合部分

_.each()

调用方式:

_.each([1, 2, 3], alert); // alert(1)  alert(2)  alert(3)
_.each({one: 1, two: 2, three: 3}, alert);  // alert每一项的value

源码:

// obj => 值  iteratee => 构造器  context=> 函数的执行上下文
_.each = _.forEach = function(obj, iteratee, context) {
  if(obj === null ) return obj;
  // 这里有createCallback函数,这么没有传递上下文,直接返回函数
  iteratee = createCallback(iteratee, context);
  let i, length = obj.length
  // 由于对象是没有length属性的,可以通过length和+length来判定是数组还是对象 (+length转换成数字类型)
  if(length === +length) {
    for(i = 0; i< length; i++) {
      iteratee(obj[i], i, obj)
    }
  } else {
    // 这里确定是对象,获取keys
    let keys = _.keys(obj);
    for(i = 0, length = keys.length; i < length; i++) {
      iteratee(obj[keys[i]], keys[i] , obj)
    }
  }
  return obj
}
// 上面使用了内部的_.keys()  下面分析_.keys()

_.keys(obj) 获取对象的keys,返回一个包含keys的数组

_.keys = function(obj) {
  if(!_.isObject) return [];
  if(nativeKeys) return nativeKeys(obj);
  var keys = [];
  for(let key in obj) {
    if(_.has(obj, key)){
      keys.push(key)
    }
  }
  return keys
}

_.isObject(obj) 判定是否是对象

_.isObject = function(obj) {
  let type = typeof obj;
  return type === 'function' || type === 'object' && !!obj;
}
// 反思: 为什么还要添加 !!obj  
// typeof null === 'object'  !!null === false

_.has(obj, key) 判定属性在自己作用域内,而不是原型链中

_.has = function(obj, key) {
  return obj != null && hasOwnProperty.call(obj,key)
}

_.map(list, iteratee, [context]) 计算数组每一项,返回一个新数组

调用:

_.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]
_.map([[1, 2], [3, 4]], _.first);
=> [1, 3]

源码:

_.map = _.collect = function(obj, iteratee, context) {
	if(obj === null) return []
    // 得到回调函数
    iteratee = _.iteratee(iteratee, context);
    // 如果不是数组就返回包含对象的keys的数组,如果是数组返回false
  	var keys = obj.length !== +obj.length || _.keys(obj),
        //length = keys ? obj.length : keys.length,
        length = (keys || obj).length,
    	// 生成一个长度为length的数组, 值为undefined
    	results = Array(length),
        currentKey;
    for(let index = 0; index < length ; index++){
        // 获取当前的key
        currentKey = keys ? keys[index] : index;
    	results[index] = iteratee(obj[currentKey], currentKey , obj) 
    }
  return results
}

_.iteratee(value, context)

_.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);
};

_.reduce(list, iteratee, memo, context)

调用:

_.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);

源码:

// createCallback第4部分  接受4个参数,第一个参数是memo
 return function(accumulator, value, index, collection) {
      return func.call(context, accumulator, value, index, collection);
 };


_.reduce = function(obj, iteratee, memo, context) {
  if(obj == null) return [];
  iteratee = createCallback(iteratee, context ,4)
  // 数组keys=false  对象key等于一个arr
  var keys = obj.length !== +obj.length && _.keys(obj),
      length = (keys || obj).length,
      index = 0;
  	  currentIndex;
  // 没有传递memo
  if(arguments.length < 3) {
    if(!length) throw new TypeError(reduceError);
    // 获取数组的第一项,并index加一,获得对象的第一个的value
    memo = obj[keys ? keys[index++] : index++];
  }		
  for(; index < length ; index++) {
    // 如果是对象就获取对应的key,是数组就获取index
    currentIndex = keys ? keys[index] : index;
    memo = iteratee(memo, obj[currentIndex], index, obj) 
  }
  return meno;
}
@huangchucai
Copy link
Owner Author

add _.reduce

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant