2009-10-07 2 views
4

Я написал простую реализацию карты для некоторой задачи. Затем, из любопытства, я написал еще два. Мне нравится map1, но код довольно трудно читать. Если кто-то заинтересован, я бы оценил простой обзор кода.Три реализации карты в javascript. Какая из них лучше?

Какой из них лучше? Вы знаете какой-то другой способ реализовать это в javascript?

var map = function(arr, func) { 
    var newarr = []; 
    for (var i = 0; i < arr.length; i++) { 
    newarr[i] = func(arr[i]); 
    } 
    return newarr; 
}; 

var map1 = function(arr, func) { 
    if (arr.length === 0) return []; 
    return [func(arr[0])].concat(funcmap(arr.slice(1), func)); 
}; 

var map2 = function(arr, func) { 
    var iter = function(result, i) { 
    if (i === arr.length) return result; 
    result.push(func(arr[i])); 
    return iter(result, i+1); 
    }; 
    return iter([], 0); 
}; 

Спасибо!

EDIT

Я думаю о такой функции в целом.

Например, прямо сейчас я собираюсь использовать его для перебора, как это:

map(['class1', 'class2', 'class3'], function(cls) { 
    el.removeClass(cls); 
}); 

или

ids = map(elements, extract_id); 
/* elements is a collection of html elements, 
    extract_id is a func that extracts id from innerHTML */ 
+0

«Лучше» в каком смысле? – Rob

+0

Можете ли вы разместить образец использования? – OscarRyz

+0

Роб, лучше с точки зрения стиля. –

ответ

2

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

Вы всегда можете задать размер выходного как в

var map = function(arr, func) { 
    var n = arr.length & 0x7fffffff; // Make sure n is a non-neg integer 
    var newarr = new Array(n); // Preallocate array size 
    var USELESS = {}; 
    for (var i = 0; i < n; ++i) { 
    newarr[i] = func.call(USELESS, arr[i]); 
    } 
    return newarr; 
}; 

Я использовал func.call() форму, а не только FUNC (...), а так как я не люблю вызова пользователя прилагаемого кода без указания, что ' это «есть», но YMMV.

+0

Как arr.length может быть отрицательным целым числом? –

+0

Антон, если arr на самом деле не массив, как в {length: 'foo'}. –

0

Я бы сказал, что первая побеждает на простоте (и сразу понятна); производительность будет сильно зависеть от того, что оптимизирует движок, поэтому вам нужно будет профилировать в двигателях, которые вы хотите поддерживать.

3

В качестве эталона, карта реализуется следующим образом в JQuery

map: function(elems, callback) { 
    var ret = []; 

    // Go through the array, translating each of the items to their 
    // new value (or values). 
    for (var i = 0, length = elems.length; i < length; i++) { 
     var value = callback(elems[ i ], i); 

     if (value != null) 
      ret[ ret.length ] = value; 
    } 

    return ret.concat.apply([], ret); 
} 

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

+0

Интересно, почему безумное возвращение, почему бы не просто вернуть ret ;? – Dave

+0

Обычно он используется для «сглаживания» массива. '[1,2,3, [4,5]] => [1,2,3,4,5]' Мне интересно, почему это необходимо здесь –

+2

Я думаю, это потому, что array.concat возвращает «один который содержит копии тех же элементов, объединенные из исходного массива. См. MDC - https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/concat –

1

Во втором методе что-то не так. «funcmap» не следует менять на «map1»?

Если так - этот метод теряет, так как метод concat() дорог - создает новый массив из заданных, поэтому он должен выделять дополнительную память и выполнять в O (array1.length + array2.length).

Мне нравится ваша первая реализация лучше всего - это определенно проще всего понять и кажется быстрым в исполнении. Никакой дополнительной декларации (например, в третьем порядке), дополнительных вызовов функций - всего один для цепочки и назначения array.length.

+0

Правда, имя было funcmap, я переименовал его во время публикации здесь. Так funcmap> map1. Спасибо за обзор! –

2

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

Тем не менее, на есть функция Array: она определена в пятой редакции ECMA-262 и, как встроенная функция, будет оптимальным выбором.Используйте это:

alert([1,2,3].map(function(n) { return n+3; })); // 4,5,6 

Единственная проблема заключается в том, что пятое издание не поддерживается всеми современными браузерами: в частности, расширение массива не присутствует в IE. Но вы можете исправить это с небольшим количеством ремонтных работ на массиве прототипа:

if (!Array.prototype.map) { 
    Array.prototype.map= function(fn, that) { 
     var result= new Array(this.length); 
     for (var i= 0; i<this.length; i++) 
      if (i in this) 
       result[i]= fn.call(that, this[i], i, this); 
     return result; 
    }; 
} 

Эта версия, согласно стандарту ECMA, позволяет дополнительный объект, который будет принят в связываться с this в вызове функции, и скачет над любыми отсутствующими значениями (в JavaScript право иметь список длины 3, где нет второго элемента).

5

Что о map реализации используется изначально на Firefox и SpiderMonkey, я думаю, что это очень прямо вперед:

if (!Array.prototype.map) { 
    Array.prototype.map = function(fun /*, thisp*/) { 
    var len = this.length >>> 0; // make sure length is a positive number 
    if (typeof fun != "function") // make sure the first argument is a function 
     throw new TypeError(); 

    var res = new Array(len); // initialize the resulting array 
    var thisp = arguments[1]; // an optional 'context' argument 
    for (var i = 0; i < len; i++) { 
     if (i in this) 
     res[i] = fun.call(thisp, this[i], i, this); // fill the resulting array 
    } 

    return res; 
    }; 
} 

Если вы не хотите расширить Array.prototype, объявить его как нормальное выражение функции.

+0

Действительно, было бы лучше использовать этот на милю, потому что он практически гарантированно совместим с будущими версиями javascript. В браузере, который поддерживает его изначально, вы получаете встроенную реализацию кода. Это нужно считать чем-то, а? – Breton

+0

@Brenton: Да, я думаю, что это самая важная часть, если доступна реализация собственного кода, это будет действительно ** много ** быстрее. – CMS

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