最近有一个关于编程相关演出的采访。面试官让我先重写每个,这完全没有问题和理解。继承我的每一种方法:
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)
我知道这些问题可能看起来有些模糊,面试早已消失,但作为个人挑战,我想了解更好的情况。
答案 0 :(得分:1)
“这如何运作
initialValue
是一个以initialValue
和number
为参数的函数。”
我认为你的意思是 callback
是一个功能。如果是这样,是的,它将被传递参数,但不仅仅是两个。
第一个是你拥有的initialValue
,虽然这会增加混乱,因为在你第一次更新它之后,它不再是初始值。最好使用不同的变量名称。
第二个是迭代中的当前项。你有一个名字number
,但它可能不是一个数字。再一个令人困惑的名字。
第三个应该是当前迭代的索引。
第四个应该是原始集合。
“
initialValue
和number
是否需要按特定顺序执行?”
当然。这是您的最终值的“累加器”和当前迭代的值。用户需要知道哪个是。
“如果初始值等于以
intialValue
和number
作为回调的函数,它是如何工作的?”
每次迭代时,回调都应该返回累加器的更新值。该值将作为下一次迭代的第一个参数传递。
“如果代码如下所示:”
[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
是最后一次返回的值,即acc
或0 + 2
,而2
是数组中的第二项,是curr
。
然后返回值为4
。
在第三次迭代中,2 + 4
是最后一次返回的值,即acc
或2 + 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)
我想这取决于您是希望forEach
和reduce
相似(简单但不符合规格),还是尽可能接近ECMA5规范。为了更好地理解它们,我建议阅读规范。
Array.prototype.forEach ( callbackfn [ , thisArg ] )
callbackfn应该是一个接受三个参数的函数。 forEach按升序为数组中的每个元素调用一次callbackfn。 callbackfn仅针对实际存在的数组元素调用;它不会被称为缺少数组的元素。
如果提供了thisArg参数,则每次调用callbackfn时都会将其用作此值。如果未提供,则使用undefined。
使用三个参数调用callbackfn:元素的值,元素的索引和被遍历的对象。
forEach不直接改变调用它的对象,但是对象可能会被callbackfn的调用变异。
forEach处理的元素范围在第一次调用callbackfn之前设置。 callbackfn将不会访问在调用forEach之后附加到数组的元素。如果更改了数组的现有元素,则传递给回调的值将是每次访问它们时的值;
。不会访问在调用forEach开始之后和访问之前删除的元素。当使用一个或两个参数调用forEach方法时,将执行以下步骤:
- 设O是调用ToObject传递此值作为参数的结果。
- 让lenValue成为使用参数&#34; length&#34;来调用O的[[Get]]内部方法的结果。
- 设len为ToUint32(lenValue)。
- 如果IsCallable(callbackfn)为false,则抛出TypeError异常。
- 如果提供thisArg,则让T为thisArg;否则让T未定义。
- 设k为0。
- 重复,而k < LEN
- 让Pk成为ToString(k)。
- 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
- 如果kPresent为true,则
- 让kValue成为用参数Pk调用O的[[Get]]内部方法的结果。
- 使用T作为此值和参数列表调用callbackfn的[[Call]]内部方法,其中包含kValue,k和O.
- 将k增加1。
- 返回undefined。
醇>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方法时,将执行以下步骤:
- 设O是调用ToObject传递此值作为参数的结果。
- 让lenValue成为使用参数&#34; length&#34;来调用O的[[Get]]内部方法的结果。
- 设len为ToUint32(lenValue)。
- 如果IsCallable(callbackfn)为false,则抛出TypeError异常。
- 如果len为0且initialValue不存在,则抛出TypeError异常。
- 设k为0。
- 如果存在initialValue,则
- 将累加器设置为initialValue。
- 否则,initialValue不存在
- 让kPresent为假。
- 重复,而kPresent为假且k < LEN
- 让Pk成为ToString(k)。
- 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
- 如果kPresent为true,则
- 让累加器成为用参数Pk调用O的[[Get]]内部方法的结果。
- 将k增加1。
- 如果kPresent为false,则抛出TypeError异常。
- 重复,而k < LEN
- 让Pk成为ToString(k)。
- 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
- 如果kPresent为true,则
- 让kValue成为用参数Pk调用O的[[Get]]内部方法的结果。
- 让累加器是使用undefined调用callbackfn的[[Call]]内部方法的结果,因为此值和参数列表包含累加器,kValue,k和O.
- 将k增加1。
- 返回累加器。
醇>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;
}
上