2014-02-19 1 views
1

Недавно у меня было интервью для программирования, связанного с концертом. Интервьюер заставил меня переписать каждый во-первых, это я не имел никаких проблем и понял полностью. Heres мой каждый метод:Реконструкция уменьшена и каждая

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

Во-вторых, он был мой переписывают уменьшить это независимо от того, сколько раз я исследую 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); 
     }); 
    }; 

Я ищу кого-то, чтобы подробно описать вторую строку кода в сокращении. Как это работает initialValue является функцией с initialValue и числом в качестве параметров. Нужно ли initialValue и число в определенном порядке? Как это работает, что начальное значение равно функции с intialValue и номером в качестве обратного вызова? Также как он знает, чтобы выполнить код, если его что-то вроде: [number(s)].reduce(function(a,b){return a + b;},intialValue) Я знаю, что эти вопросы могут показаться немного расплывчатыми, интервью давно ушло, но, как личный вызов, я хочу понять, что происходит лучше.

+1

Эрм ... так что вы сбиты с толку то, как тривиальный, как 'х = Foo (х, у)'? –

+0

@nietthedarkabsol Да, пожалуйста .. уточните –

ответ

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; 
    }; 
+0

'underscore' будет использовать' native'-функции, если они существуют, если нет, то он захочет использовать собственные реализации, которые не соответствуют спецификациям ECMA5 для «резервных копий». Они предпочитают не следить за ними из-за известных ошибок в старых браузерах, особенно IE <9, и это нарушает реализацию «Array». Поэтому они обрабатывают массивы как «плотные». Я действительно не думаю, что этот код помогает с пониманием. – Xotic750

+0

Лучшим примером будет показать код, который используется в ['es5_shim'] (https://github.com/es-shims/es5-shim) или [polyfill] (http: //en.wikipedia. org/wiki/Polyfill), предоставленные MDN для ['forEach'] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) и [' уменьшить '] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) – Xotic750

1

"Как это работает initialValue функция с initialValue и number в качестве параметров."

Я предполагаю, что вы имеете в виду callback является функцией. Если да, то да, будут переданы аргументы, но больше двух.

Первый - это то, что у вас есть как initialValue, хотя это добавляет путаницу, потому что после того, как вы впервые его обновили, это уже не первоначальное значение. Лучше использовать другое имя переменной.

Второй - текущий элемент на итерации. У вас есть имя number, но это может быть не номер. Опять путаное имя.

Третий должен быть индексом текущей итерации.

Четвертый должен быть оригинальной коллекцией.


"Do 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(начальное значение дано) и curr(сокращение от тока) будет номером 2, который является первым элементом в массиве.

Возвращаемое значение тогда 0 + 2.

На второй итерации acc является значением последнего возвращения, который был 0 + 2 или 2 и curr является вторым элементом в массиве, который 4.

Возвращаемое значение 2 + 4.

На третьей итерации acc является значение последнего возвращения, который был 2 + 4 или 6 и curr третий элемент в массиве, который 3.

Возвращаемое значение 6 + 3.

... и прочее.

Таким образом, как вы можете видеть, оно просто принимает либо значение intial, либо возвращаемое значение предыдущей итерации, передавая его в качестве первого аргумента, позволяя вам делать любые манипуляции, которые вы хотите, а затем принимая ваше возвращаемое значение и передавая это как первый аргумент следующей итерации.

Это продолжается до тех пор, пока цикл не будет завершен, и в этот момент .reduce() предоставит вам все, что вы вернули с последней итерации.


Обратите внимание, что ни одна из ваших функций не соответствует требованиям ECMAScript. Первый работает на простых объектах ??? И оба не хватает некоторых деталей.

+0

' Первый работает на простых объектах? 'да, это должно быть как * forEach * '... преднамеренно общий; он не требует, чтобы это значение было объектом Array' ([ECMA-262 §5.4.4.18] (http://ecma-international.org/ecma-262/5.1/#sec-15.4.4.18)). Что касается «... обоих недостающих деталей» - да, немало.;-) – RobG

+0

@ RobG: Конечно, вы не думаете, что он предназначен для работы таким образом, который был закодирован в переписывании. Да, они общие. Это не означает, что это перечислитель свойств общего назначения. Вот что я имел в виду «простые объекты». –

+0

-yes, некоторые из недостающих деталей проверяют, что посещаемые свойства являются действительными идексами и для собственных свойств перед действиями. По крайней мере, он только посещает свойства, которые существуют. :-) – RobG

0

Я думаю, это зависит от того, хотите ли вы, чтобы forEach и reduce были похожими (простыми, но не специфицированными) или как можно ближе/разумно к спецификации ECMA5.Чтобы лучше понять их, я бы предложил прочитать спецификации.

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

callbackfn должна быть функция, которая принимает три аргумента. forEach вызывает callbackfn один раз для каждого элемента, присутствующего в массиве, в порядке возрастания. callbackfn вызывается только для элементов существующего массива; он не вызывается для отсутствующих элементов массива.

Если указан параметр thisArg, он будет использоваться как это значение для каждого вызова callbackfn. Если он не указан, вместо него используется undefined.

callbackfn вызывается с тремя аргументами: значением элемента, индексом элемента и пройденным объектом.

forEach не напрямую мутирует объект, на который он вызывается, но объект может быть мутирован вызовами callbackfn.

Диапазон элементов, обработанных forEach, устанавливается перед первым вызовом callbackfn. Элементы, которые добавляются в массив после вызова метода forEach, не будут посещаться callbackfn. Если существующие элементы массива изменены, их значение, переданное обратному вызову, будет значением в то время, когда каждый посетит их; элементы, которые удаляются после вызова метода forEach и до его посещения, не посещаются.

Когда метод Foreach вызывается с одним или двумя аргументами выполняются следующие шаги:

  1. Пусть O будет результатом вызова ToObject передавая это значение в качестве аргумента.
  2. Пусть lenValue является результатом вызова внутреннего метода O [[Get]] с аргументом «length».
  3. Позвольте len быть ToUint32 (lenValue).
  4. Если IsCallable (callbackfn) является ложным, введите исключение TypeError.
  5. Если этотArg был поставлен, пусть T - этоArg; иначе пусть T не определено.
  6. Пусть к 0.
  7. Repeat, в то время как к < Len
  8. Пусть Рк ToString (к).
  9. Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
  10. Если kPresent является истинным, то
  11. Пусть kValue будет результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
  12. Вызвать метод [[Call]] внутренний метод callbackfn с Т в качестве списка этого значения аргумента и содержащего-значение, К и О.
  13. Увеличения к на 1.
  14. Возврат не определен.

Длина свойство метода Foreach равно 1.

Примечание Функция Foreach намеренно родовое; он не требует, чтобы это значение было объектом Array. Поэтому он может быть передан другим типам объектов для использования в качестве метода. Может ли функция forEach успешно применяться к объекту хоста, зависит от реализации.

-

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

callbackfn должна быть функция, которая принимает четыре аргумента. уменьшает вызовы callback, как функцию, один раз для каждого элемента, присутствующего в массиве, в порядке возрастания.

callbackfn вызывается с четырьмя аргументами: previousValue (или значением предыдущего вызова callbackfn), currentValue (значение текущего элемента), currentIndex и проходящий объект. При первом вызове обратного вызова значение previousValue и currentValue может быть одним из двух значений. Если в вызове было предложено initialValue, то значение предыдущегоValue будет равно initialValue, а currentValue будет равно первому значению в массиве. Если initialValue не было предоставлено, то предыдущее значение Value будет равно первому значению в массиве, а currentValue будет равно второму. Это TypeError, если массив не содержит элементов, а initialValue не предоставляется.

сокращение не напрямую мутирует объект, на который он вызывается, но объект может быть мутирован вызовами callbackfn.

Диапазон элементов, обработанных сокращением, устанавливается перед первым вызовом callbackfn. Элементы, которые добавляются к массиву после начала вызова для отказа, не будут посещаться callbackfn. Если существующие элементы массива изменены, их значение, переданное callbackfn, будет значением, которое в то время уменьшает количество посещений; элементы, которые удаляются после начала вызова до начала и перед посещением, не посещаются.

Когда метод снижения вызывается с одним или двумя аргументами выполняются следующие шаги:

  1. Пусть O будет результатом вызова ToObject передавая это значение в качестве аргумента.
  2. Пусть lenValue является результатом вызова внутреннего метода O [[Get]] с аргументом «length».
  3. Позвольте len быть ToUint32 (lenValue).
  4. Если IsCallable (callbackfn) является ложным, введите исключение TypeError.
  5. Если len равно 0, а initialValue нет, введите исключение TypeError.
  6. Пусть к 0.
  7. Если InitialValue присутствует, то
  8. Установите аккумулятор в InitialValue.
  9. Else, initialValue нет
  10. Пусть kPresent будет ложным.
  11. Повторите, в то время как kPresent является ложным и k < len
  12. Пусть Pk be ToString (k).
  13. Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
  14. Если kPresent является истинным, то
  15. Позвольте аккумулятору быть результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
  16. Увеличение k на 1.
  17. Если kPresent является ложным, введите исключение TypeError.
  18. Повторите, в то время как k < len
  19. Позвольте Pk быть ToString (k).
  20. Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
  21. Если kPresent является истинным, то
  22. Пусть kValue будет результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
  23. Пусть аккумулятор будет результатом вызова метода [[Call]] внутренний метод callbackfn с неопределенным, как это значение и список аргументов, содержащий аккумулятор, к,-значение, и О.
  24. Увеличение к на 1.
  25. Возвратный аккумулятор.

Длина свойство методы уменьшения равно 1.

ПРИМЕЧАНИЕ уменьшает функцию намеренно родовой; он не требует, чтобы это значение было объектом Array. Поэтому он может быть передан другим типам объектов для использования в качестве метода. Может ли функция сокращения успешно применяться к объекту хоста, зависит от реализации.

Который для меня я бы написал (и это не 100% спецификации, но близко). Разумеется, вы могли бы написать их точно так, как описано выше.

функции Helper

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

Тезисы, описанные в speciications

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

Смежные вопросы