重建减少和每个

时间:2014-02-19 01:39:43

标签: javascript

最近有一个关于编程相关演出的采访。面试官让我先重写每个,这完全没有问题和理解。继承我的每一种方法:

var each = function(arrayItems,callback){
  //if arrayItems is not an array
  if(!Array.isArray(arrayItems)){
    //write a for in loop to iterate through the object
    for(var key in arrayItems){
      //store the value, the key and the whole object as the callback parameters 
      callback(arrayItems[key],key,arrayItems);
    }
  }
  //if arrayItems wasn't an object iterate through the array
  for(var i = 0; i < arrayItems.length; i++){
    //store the value,key and whole array as the callback's parameters
    callback(arrayItems[i],i,arrayItems);  
  }
};

其次他重写了我的重写 reduce 这无论我研究devdocs多少次并挑选每一段代码我都难以理解这里是我的方法:

var reduce = function(array,callback,initialValue){
      // Implementing each into reduce
      each(array,function(number){
      //This is the line of code that confuses me
      initialValue = callback(initialValue,number);
      });
    };

我正在寻找有人详细说明reduce中的第二行代码。这是如何工作的initialValue是一个函数,其中initialValue和number作为参数。 initialValue和number是否需要按特定顺序排列?如何使用初始值等于具有intialValue和number作为回调的函数?如果像以下那样执行代码也是如何知道的:[number(s)].reduce(function(a,b){return a + b;},intialValue)我知道这些问题可能看起来有些模糊,面试早已消失,但作为个人挑战,我想了解更好的情况。

3 个答案:

答案 0 :(得分:1)

  

“这如何运作initialValue是一个以initialValuenumber为参数的函数。

我认为你的意思是 callback 是一个功能。如果是这样,是的,它将被传递参数,但不仅仅是两个。

第一个是你拥有的initialValue,虽然这会增加混乱,因为在你第一次更新它之后,它不再是初始值。最好使用不同的变量名称。

第二个是迭代中的当前项。你有一个名字number,但它可能不是一个数字。再一个令人困惑的名字。

第三个应该是当前迭代的索引。

第四个应该是原始集合。


  

initialValuenumber是否需要按特定顺序执行?”

当然。这是您的最终值的“累加器”和当前迭代的值。用户需要知道哪个是。


  

“如果初始值等于以intialValuenumber作为回调的函数,它是如何工作的?”

每次迭代时,回调都应该返回累加器的更新值。该值将作为下一次迭代的第一个参数传递。


  

“如果代码如下所示:”[number(s)].reduce(function(a,b){return a + b;},intialValue)

这是一个奇怪的例子,但是如果你有一个正确的数组,并且你传递了0之类的内容,那么a就是累加器,它将等于initialValue你传递了,或者上一次迭代的返回值。


使用.reduce()的更合理的例子是:

var result = [2,4,3,8,6].reduce(function(acc, curr, i, arr) {
    console.log(acc, curr);
    return acc + curr;
}, 0);

所以在第一次迭代时,acc (累加器的简称)将是0 (给定的初始值)和{{ 1}} (当前的简称)将是数字curr,这是数组中的第一项。

然后返回值为2

在第二次迭代中,0 + 2是最后一次返回的值,即acc0 + 2,而2是数组中的第二项,是curr

然后返回值为4

在第三次迭代中,2 + 4是最后一次返回的值,即acc2 + 4,而6是数组中的第三项,是curr

然后返回值为3

......等等。

正如你所看到的,它只是取初始值或前一次迭代的返回值,将它作为第一个参数传递,让你做任何你想要的操作,然后获取你的返回值并传递它作为下一次迭代的第一个参数。

这一直持续到循环完成,此时6 + 3为您提供从上次迭代返回的任何内容。


请注意,您的任何功能都不符合ECMAScript。第一个操作普通对象???两者都缺少某些细节。

答案 1 :(得分:0)

您可以检查下划线核心功能!

if (typeof (/./) !== 'function') {
    _.isFunction = function(obj) {
      return typeof obj === 'function';
    };
  }
_.bind = function(func, context) {
    var args, bound;
    if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    if (!_.isFunction(func)) throw new TypeError;
    args = slice.call(arguments, 2);
    return bound = function() {
      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
      ctor.prototype = func.prototype;
      var self = new ctor;
      ctor.prototype = null;
      var result = func.apply(self, args.concat(slice.call(arguments)));
      if (Object(result) === result) return result;
      return self;
    };
  };

_.has = function(obj, key) {
    return hasOwnProperty.call(obj, key);
  };

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

_.each = function(obj, iterator, context) {
    if (obj == null) return obj;
    if (nativeForEach && obj.forEach === nativeForEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, length = obj.length; i < length; i++) {
        if (iterator.call(context, obj[i], i, obj) === breaker) return;
      }
    } else {
      var keys = _.keys(obj);
      for (var i = 0, length = keys.length; i < length; i++) {
        if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
      }
    }
    return obj;
  };


 _.reduce = function(obj, iterator, memo, context) {
    var initial = arguments.length > 2;
    if (obj == null) obj = [];
    if (nativeReduce && obj.reduce === nativeReduce) {
      if (context) iterator = _.bind(iterator, context);
      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    }
    each(obj, function(value, index, list) {
      if (!initial) {
        memo = value;
        initial = true;
      } else {
        memo = iterator.call(context, memo, value, index, list);
      }
    });
    if (!initial) throw new TypeError(reduceError);
    return memo;
  };

答案 2 :(得分:0)

我想这取决于您是希望forEachreduce相似(简单但不符合规格),还是尽可能接近ECMA5规范。为了更好地理解它们,我建议阅读规范。

  

Array.prototype.forEach ( callbackfn [ , thisArg ] )

     

callbackfn应该是一个接受三个参数的函数。 forEach按升序为数组中的每个元素调用一次callbackfn。 callbackfn仅针对实际存在的数组元素调用;它不会被称为缺少数组的元素。

     

如果提供了thisArg参数,则每次调用callbackfn时都会将其用作此值。如果未提供,则使用undefined。

     使用三个参数调用

callbackfn:元素的值,元素的索引和被遍历的对象。

     

forEach不直接改变调用它的对象,但是对象可能会被callbackfn的调用变异。

     

forEach处理的元素范围在第一次调用callbackfn之前设置。 callbackfn将不会访问在调用forEach之后附加到数组的元素。如果更改了数组的现有元素,则传递给回调的值将是每次访问它们时的值;

。不会访问在调用forEach开始之后和访问之前删除的元素。

     

当使用一个或两个参数调用forEach方法时,将执行以下步骤:

     
      
  1. 设O是调用ToObject传递此值作为参数的结果。
  2.   
  3. 让lenValue成为使用参数&#34; length&#34;来调用O的[[Get]]内部方法的结果。
  4.   
  5. 设len为ToUint32(lenValue)。
  6.   
  7. 如果IsCallable(callbackfn)为false,则抛出TypeError异常。
  8.   
  9. 如果提供thisArg,则让T为thisArg;否则让T未定义。
  10.   
  11. 设k为0。
  12.   
  13. 重复,而k < LEN
  14.   
  15. 让Pk成为ToString(k)。
  16.   
  17. 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
  18.   
  19. 如果kPresent为true,则
  20.   
  21. 让kValue成为用参数Pk调用O的[[Get]]内部方法的结果。
  22.   
  23. 使用T作为此值和参数列表调用callbackfn的[[Call]]内部方法,其中包含kValue,k和O.
  24.   
  25. 将k增加1。
  26.   
  27. 返回undefined。
  28.         

    forEach方法的length属性为1.

         

    注意forEach函数是有意通用的;它不要求它的这个值是一个Array对象。因此,它可以转移到其他类型的对象以用作方法。 forEach函数是否可以成功应用于宿主对象是依赖于实现的。

-

  

Array.prototype.reduce ( callbackfn [ , initialValue ] )

     

callbackfn应该是一个带有四个参数的函数。 reduce作为函数调用回调,对于数组中存在的每个元素,按升序调用一次。

     使用四个参数调用

callbackfn:previousValue(或前一次调用callbackfn的值),currentValue(当前元素的值),currentIndex和被遍历的对象。第一次调用回调时,previousValue和currentValue可以是两个值之一。如果在reduce的调用中提供了initialValue,则previousValue将等于initialValue,currentValue将等于数组中的第一个值。如果没有提供initialValue,那么previousValue将等于数组中的第一个值,currentValue将等于第二个值。如果数组不包含元素且未提供initialValue,则为TypeError。

     

reduce不会直接改变调用它的对象,但是对象可能会被callbackfn的调用所突变。

     

reduce处理的元素范围在第一次调用callbackfn之前设置。 callbackfn将不会访问在调用reduce开始后附加到数组的元素。如果更改了数组的现有元素,则传递给callbackfn的值将是reduce访问它们时的值;

。不会访问在调用reduce开始之后和访问之前删除的元素。

     

使用一个或两个参数调用reduce方法时,将执行以下步骤:

     
      
  1. 设O是调用ToObject传递此值作为参数的结果。
  2.   
  3. 让lenValue成为使用参数&#34; length&#34;来调用O的[[Get]]内部方法的结果。
  4.   
  5. 设len为ToUint32(lenValue)。
  6.   
  7. 如果IsCallable(callbackfn)为false,则抛出TypeError异常。
  8.   
  9. 如果len为0且initialValue不存在,则抛出TypeError异常。
  10.   
  11. 设k为0。
  12.   
  13. 如果存在initialValue,则
  14.   
  15. 将累加器设置为initialValue。
  16.   
  17. 否则,initialValue不存在
  18.   
  19. 让kPresent为假。
  20.   
  21. 重复,而kPresent为假且k < LEN
  22.   
  23. 让Pk成为ToString(k)。
  24.   
  25. 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
  26.   
  27. 如果kPresent为true,则
  28.   
  29. 让累加器成为用参数Pk调用O的[[Get]]内部方法的结果。
  30.   
  31. 将k增加1。
  32.   
  33. 如果kPresent为false,则抛出TypeError异常。
  34.   
  35. 重复,而k < LEN
  36.   
  37. 让Pk成为ToString(k)。
  38.   
  39. 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
  40.   
  41. 如果kPresent为true,则
  42.   
  43. 让kValue成为用参数Pk调用O的[[Get]]内部方法的结果。
  44.   
  45. 让累加器是使用undefined调用callbackfn的[[Call]]内部方法的结果,因为此值和参数列表包含累加器,kValue,k和O.
  46.   
  47. 将k增加1。
  48.   
  49. 返回累加器。
  50.         

    reduce方法的length属性为1。

         

    注意reduce函数是有意通用的;它不要求它的这个值是一个Array对象。因此,它可以转移到其他类型的对象以用作方法。 reduce函数是否可以成功应用于宿主对象是依赖于实现的。

对我来说,我会写(这些不是100%的规格,但接近)。你当然可以按照上面的规范中的说明完全写出它们。

帮助程序功能

function firstToCapital(inputString) {
    return inputString.charAt(0).toUpperCase() + inputString.slice(1).toLowerCase();
}

function isClass(inputArg, className) {
    return Object.prototype.toString.call(inputArg) === '[object ' + firstToCapital(className) + ']';
}

function throwIfNotAFunction(inputArg) {
    if (!isClass(inputArg, 'function')) {
        throw TypeError('Argument is not a function');
    }

    return inputArg;
}

说明书中描述的摘要

function checkObjectCoercible(inputArg) {
    if (typeof inputArg === 'undefined' || inputArg === null) {
        throw new TypeError('Cannot convert argument to object');
    }

    return inputArg;
};

function ToObject(inputArg) {
    checkObjectCoercible(inputArg);
    if (isClass(inputArg, 'boolean')) {
        inputArg = new Boolean(inputArg);
    } else if (isClass(inputArg, 'number')) {
        inputArg = new Number(inputArg);
    } else if (isClass(inputArg, 'string')) {
        inputArg = new String(inputArg);
    }

    return inputArg;
}

function ToUint32(inputArg) {
    return inputArg >>> 0;
}

<强>的forEach

function forEach(array, fn, thisArg) {
    var object = ToObject(array),
        length,
        index;

    throwIfNotAFunction(fn);
    length = ToUint32(object.length);
    for (index = 0; index < length; index += 1) {
        if (index in object) {
            fn.call(thisArg, object[index], index, object);
        }
    }
}

<强>减少

function reduce(array, fn, initialValue) {
    var object = ToObject(array),
        accumulator,
        length,
        kPresent,
        index;

    throwIfNotAFunction(fn);
    length = ToUint32(object.length);
    if (!length && arguments.length === 2) {
        throw new TypeError('reduce of empty array with no initial value');
    }

    index = 0;
    if (arguments.length > 2) {
        accumulator = initialValue;
    } else {
        kPresent = false;
        while (!kPresent && index < length) {
            kPresent = index in object;
            if (kPresent) {
                accumulator = object[index];
                index += 1;
            }
        }

        if (!kPresent) {
            throw new TypeError('reduce of empty array with no initial value');
        }
    }

    while (index < length) {
        if (index in object) {
            accumulator = fn.call(undefined, accumulator, object[index], index, object);
        }

        index += 1;
    }

    return accumulator;
}

jsFiddle