JavaScript
Node.JS
CoffeeScript
Comments 67
+13
Зачем вы пишите
 cubes = (math.cube num for num in list) 
когда вы можете написать
 cubes = list.map math.cube 
И получите тот же самый код что и в js. CoffeуScript просто дает вам варианты.

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

PS: Вам надо было код вложить на jsperf, что-б любой желающий мог запустить, так же увидели бы результаты в разных браузерах.
+8
Вот добавил ваш последний тест на jsperf, в Chrome CS-цикл быстрее чем JS reduce более чем в 2 раза, а в Firefox CS-цикл быстрее в 20 раз.
-2
Так. Вот тесты для суммирования массивов. У меня в файрфоксе и хроме зеркальная картина. Хром 45-ой версии.
Далее. Нод, на котором я гонял тесты как выяснилось старый. Версии v0.10.40. В новом ноде пока не проверял сам, но друзья кофеманы говорят, что лямбды не рулят. Сейчас немного подумаю и добавлю апдейт с опровержением, или нову статью сделаю с разбором.
+1
Все массивные методы написаны на JS и сильно избыточны, они априори не могут быть быстрей, почитайте/посмотрите Далтона, он в докладах про lodash хорошо разжевывает суть, ну и тестов у него ого-го.
0
По измерениям в немного устаревшей версии node и в текущей версии Chrome map быстрее. Ну и я верю, что в джаваскрипте рано или поздно начнут инлайнить функции.
0
Проверьте ваш тест, map/forEach/reduce и остальные избыточны, они всегда работают через `call`, но самое главное в них используется мега тормознутый `in`, выкинув его, мы уже получим 5х ускорение (но, да, таким образом мы получим иное поведение чем в native реализации, но вот нужно ли такое поведение? Скорее да, чем нет, ну и тесты говорят за себя).

— Браузерные тесты: jsperf.com/arraymap/14 (как и ожидалось самый медленный нативный)
— Вот ещё я накидал простенький тест, чтобы показать, где и как можно выиграть.

Код теста
'use strict';

const MAX = 1000;
const source = new Array(MAX);

for (let i = 0; i < MAX; i++) {
	source[i] = i * 2;
}

// Максимально приближеный к нативному
function trueMyMap(array, callback, thisArg) {
	const result = [];
	const length = array.length;

	for (let i = 0; i < length; i++) {
		if (i in array) {
			result.push(callback.call(thisArg, array[i], i, array));
		}
	}

	return result;
}

// Простой и быстрый (без учета «дырок»)
function simpleMap(array, callback, thisArg) {
	const result = [];
	const length = array.length;

	for (let i = 0; i < length; i++) {
		result.push(callback.call(thisArg, array[i], i, array));
	}

	return result;
}

// Оптимизированный (без учета «дырок»)
function optimizedMap(array, callback, thisArg) {
	const length = array.length;
	const result = new Array(length);
	const argsLength = callback.length;

	for (let i = 0; i < length; i++) {
		if (thisArg !== void 0) {
			result.push(callback.call(thisArg, array[i], i, array));
		} else if (argsLength <= 1) {
			result.push(callback(array[i]));
		} else if (argsLength === 2) {
			result.push(callback(array[i], i));
		} else {
			result.push(callback(array[i], i, array));
		}
	}

	return result;
}

// Хелпер для запуска тестов
function perf(name, tests) {
	console.log(name);

	Object.keys(tests).forEach(function (name) {
		const callback = tests[name];
		console.time('  ' + name);

		for (let i = 0; i < 1e4; i++) {
			callback();
		}

		console.timeEnd('  ' + name);
	});
}

//
// Тесты
//
perf('Array#map(val)', {
	'native': function () {
		source.map(function (val) {
			return val;
		});
	},

	'trueMyMap': function () {
		trueMyMap(source, function (val) {
			return val;
		});
	},

	'simple': function () {
		simpleMap(source, function (val) {
			return val;
		});
	},

	'optimized': function () {
		optimizedMap(source, function (val) {
			return val;
		});
	}
});

perf('Array#map(val, idx)', {
	'native': function () {
		source.map(function (val, idx) {
			return val * idx;
		});
	},
	
	'simple': function () {
		simpleMap(source, function (val, idx) {
			return val * idx;
		});
	},

	'optimized': function () {
		optimizedMap(source, function (val, idx) {
			return val * idx;
		});
	}
});

perf('Array#map(val, idx, array)', {
	'native': function () {
		source.map(function (val, idx, array) {
			return val * idx + array.length;
		});
	},

	'simple': function () {
		simpleMap(source, function (val, idx, array) {
			return val * idx + array.length;
		});
	},

	'optimized': function () {
		optimizedMap(source, function (val, idx, array) {
			return val * idx + array.length;
		});
	}
});

perf('Array#map(val, idx, array) + thisArg', {
	'native': function () {
		source.map(function (val, idx, array) {
			return val * idx + array.length * this.foo;
		}, {foo: 123});
	},

	'simple': function () {
		simpleMap(source, function (val, idx, array) {
			return val * idx + array.length * this.foo;
		}, {foo: 123});
	},

	'optimized': function () {
		optimizedMap(source, function (val, idx, array) {
			return val * idx + array.length * this.foo;
		}, {foo: 123});
	}
});
Результаты: node v4.0.0
Array#map(val)
  native: 791ms
  trueMyMap: 836ms
  simple: 210ms
  optimized: 164ms
Array#map(val, idx)
  native: 823ms
  simple: 222ms
  optimized: 186ms
Array#map(val, idx, array)
  native: 770ms
  simple: 190ms
  optimized: 203ms
Array#map(val, idx, array) + thisArg
  native: 768ms
  simple: 180ms
  optimized: 199ms



P.S. Инлайнить ой как не скоро будут, потому что нет спроса и есть уже решения, который покрывают эту задачу.
0
Могу и даже рекомендовал писать именно его в заключении. Но приведённый мной код — цитата с главной страницы сайта CoffeeScript. Поэтому он и был подвергнут разбору.
За совет с jsperf спасибо.
+3
Открою секрет, что выхлоп кофе один в один такой, как написал бы человек. Если человек написал бы циклом for, то и выхлоп соответствующий, как если бы он написал циклом for, если бы воспользовался методом forEach или map, то соответственно.
0
С другой стороны не всё так идеально, как хотелось бы. Вот пример на кофе:
some = (args...) -> console.log args

А вот результат:
var some,
  slice = [].slice;

some = function() {
  var args;
  args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  return console.log(args);
};


Как недавно выяснил — в этом варианте есть небольшие проблемы: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
-2
Если бы array comprehensions были частью javascript, замечание было бы справедливым. Но там их нет и поэтому for из coffeescript имеет с for циклом столько же общего, сколько с ним имеет for in.
+1
for i in (arr[Symbol.iterator] || arr) работает точно так же (Ну почти, там ещё next надо брать) как for of в JS. Ну и наоборот: http://coffeescript.org/#try:for%20i%20of%20%7Ba%3A%201%2C%20b%3A%202%7D%0A%20%20console.log(i)

Что с этими циклами не так?
0
for(i=0;i<array.length;++i);

и

for (item in array);

Это две разные конструкции, вот что я хочу сказать.
+1
console.log i for i in array

и

console.log i for i of object


Тоже две совершенно разные конструкции в кофе. И точно так же как в вашем примере, одна по массиву, другая по объекту. Я так и не понял какую мысль вы хотите донести.
-2
Я хочу сказать, что проход по массиву в джаваскрипте делается с помощью forEach map и им подобными. Делается не только из соображений удобства, но и из соображений производительности. А coffeescript при трансляции выбирает медленный вариант.
+3
А что мешает написать на кофе:
array.forEach (i) -> console.log i

?

А кофе вообще ничего не выбирает. Это написавший написал цикл, а не вызвал метод.

Да и цикл побыстрее будет метода. Я не понимаю почему вы ознакомившись с тестами http://habrahabr.ru/post/268753/#comment_8611909 до сих пор пытаетесь всех убедить, что это медленный вариант.

Предлагаю осилить наконец кофе и понять, что это не отдельный язык, а тот же самый JS. Как вы написали на кофе так и будет в JS, один в один, буква к букве. А на главной странице показаны лишь отличия в основных конструкциях языка, а не как что-либо делать.
+2
Причина проста и прозаична до безобразия. То, что в javascript называется массивами, в других языках программирования называется хэшами, а время доступа по ключу к элементу хэша это не тоже самое, что время доступа к элементу массива по индексу.

Вы ничего не перепутали? В JS массивы как массивы, там только цифровые индексы, и:

var nums = [1, 2, 3];
delete nums[1];
console.log(nums.length);

Ожидаемо выдаст 3, просто массивы здесь динамические по размеру. Но это не PHP, где массивы — это всё что угодно, только не обычные массивы.

О cubes = list.map math.cube вам уже написали (хотя я предпочитаю ставить в таких случаях скобки, то есть единственное отличие будет в отсутствии запятой).
А теперь попробуйте сделать такое на JS:

cubes = (math.cube num for num in list by 2)

И тогда мы поговорим о сравнении с чистым JS.

Не стоит винить синтаксический сахар в том, что его используют неправильно.
-3
Вы ничего не перепутали? В JS массивы как массивы, там только цифровые индексы, и:

Увы, это не так. Массивы в джаваскрипте примерно такие же как в php. Собственно если в языке можно писать и array[1] и array['ke'] то они другими и быть не могут.

Не стоит винить синтаксический сахар в том, что его используют неправильно

Не стоит использовать его там, где в нём нет нужды. И тем более не стоит выкладывать примеры бесполезного использования на главную страницу.
-1
Когда вы говорите, что массивы в js это обычные массивы, вы имеете в виду, что они располагаются в одном непрерывном куске памяти?
0
Причем тут это? Как что располагать в памяти решает производитель рантайма исходя из огромного количества факторов. Мы тут говорим о дизайне языка, стандарт которого вообще не описывает реализацию рантайма.
0
При том, что доступ к элементам массива, находящегося в непрерывном куске памяти происходит практически мгновенно. В отличие от хэша.
И да, в джаваскрипте действительно не оговорено как располагать в памяти массив. Но оговорено, то индексы массивов могут быть строками и могут содержать дырки. Что естественным образом приводит к необходимости держать его в хэше. Вследствии чего доступ по индексу мендленный.
+2
При правильном использовании массивов, оптимизатор использует массивы, а не хеш таблицы.
0
Индексы не могут быть строками, не путайте. Массивы в JS — это объекты, так что array['ke'] это доступ к свойству этого объекта, не к элементу массива, так же как и более привычное array.ke.
Дырки могут содержать, это да, размер-то массива может быть больше чем количество элементов в нём, ничего странного в этом нет. А в том же PHP размер массива при удалении сразу же меняется (мы не говорим сейчас об SPL семействе).
+2
> Увы, это не так. Массивы в джаваскрипте примерно такие же как в php. Собственно если в языке можно писать и array[1] и array['ke'] то они другими и быть не могут.

Всегда считал, что в JS массивы — это объект Array (методы forEach. map, etc) с цифровыми ключами и Symbol.iterator методом, а в пыхе — это примитив в чистейшем виде, они совершенно разные как по поведению, так и по реализации (одно объект, другое примитив). Более того, если в JS квадратные скобки являются элементом доступа к полю объекта, то в пыхе за это отвечает ArrayAccess интерфейс и соответствующая реализация оного. Так же и за итерацию, в JS это Symbol.iterator, а в пыхе IteratorAggregate интерфейс (и прочие). Т.е. вы как минимум сравниваете тёплое с мягким. Просьба поправить, если я заблуждаюсь.
-2
В php есть встроенные в язык массивы. Они — хэши. Ещё есть способ разрешить любому объекту весте себя как массив — ArrayAccess. Это как перегрузка оператора []. И есть настоящие массивы фиксированной длины с однотипными элементами.
0
Они не чистые хэши. Там весьма сложная внутренняя структура, которая включаэт и хэши, и списки, вот хорошая презентация: slides.catchy.io/PHP-Data-Structures-and-the-impact-of-PHP-7-on-them.html
И (что немного парадоксально), они хотя и потребляют больше памяти, намного быстрее в типичных задачах за те же SPL реализации. В презентации много интересного.
0
Не чистые хэши, но одного непрерывного куска памяти за массивом в php нет. В документации так и написано — ordered map. Это даёт возможность бысть перебирать элементы по порядку, но не даёт быстрого доступа по индексу.

Насчёт презентации — не подскажете ссылку на видео?
+2
Именно это я и написал с просьбой поправить следующее: Я высказался, что в js массивы вообще ничем не отличаются от обычного Object, включая способ доступа к элементам этого «массива», а в пыхе реализовать их невозможно по определению — передача по значению объекта невозможна на уровне реализации объекта.

0
Array.prototype['0'] = 'x'
console.assert( Array(2).length === 2 )
console.assert( Array(2)[0] === 'x' )
+5
Мне кажется вы немного теряете контекст, этот пример висит там уже без малого 5 лет. В то время, реально поддерживаемые браузерами возможности javascript немного отличаются от теперешних. Насколько я понимаю CoffeeScript до сих пор работает хорошо с IE8, и все это оставлено в угоду совместимости.
-4
К счастью, IE8 в нашем мире осталось не так уж и много. Совместимость это хорошо, но когда совместимость достигается в угоду производительности это уже не так хорошо. Было бы неплохо ключом компиляции это настраивать хотя бы.
+2
Ну так map, reduce и прочие, как оказалось выше (http://habrahabr.ru/post/268753/#comment_8611909) в разы медленнее обычного цикла. Ключ для понижения производительности? А учитывая то, что выражение на кофе один в один будет как выражение на JS, то и в угоду читаемости выхлопа (а учитывая sourcemap, его вообще читать не нужно)?
0
Сделать отдельные ключи это не так просто, особенно если учесть что их нужно подвязывать на какую-то конкретную реализацию рантайма, например, node (у которого и своих ключей совместимости полно). И вообще не ясно, как все это будет влиять на реальную производительность.
+1
Вот зря вы так. Мы тут на новом проекте еле-еле отбились от IE8. Он, к сожалению, много ещё где живее всех живых, особенно в мирах, где правит кровавый ынтырпрайз.
0
Ровно в полночь раз в год появляется очередная статья о том, насколько же кофе ужасен и насколько плохо его читать тем, кто его не осилил. Не кажется ли, что это уже не модно?
-4
Статья о том, что CoffeScript генерирует медленный код, а не о том, что его плохо читать.
+3
Уважаемый, трюк в том, что при написании кода на CoffeeScript думать нужно на порядок больше, чем на JS.
Читать код проще, понимать тоже в целом легко, если знаете синтаксис, но при написании кода нужно четко понимать не только то, что вы хотите получить, но и то, как оно будет выглядеть в JS.
Вы же приводите два совершенно разных куска кода и говорите что один быстрее за другой. Он не может быть быстрее или медленнее просто по определению, вы используете совершенно разные подходы для достижения одной и той же цели!
+1
Если при написании кода на CoffeeScript действительно надо думать на порядок больше, то на нём лучше не писать. Весь смысл использования языка, отличного от джаваскрипта в том, чтобы облегчить написание кода, а не усложнить его.

Ну и если есть 2 совершенно разных куска кода, которые используются для достижения одной и той же цели и один кусок быстрее другого, то лучше использовать инструмент, который генерит быстрый код.
-1
Одно не противоречит другому. Паскаль, к примеру, очень простой язык, но не предпочитать же его, например шарпу. Так же и тут — кофе удобнее и мощнее ES5.
-1
Причина не предпочитать паскаль шарпу — поддержка современных платформ, сборщик мусора, concurrency, виртуальная машина со стектрейсами, богатая стандартная библиотека и коммьюнити.

CoffeScript не может сделать ничего, что не может джаваскрипт, соответственно причина выбирать его — простота написания кода и ошибкобезопасность. Я уверен, если сообщить создателям CoffeeScript, что на их языке писать сложнее, чем на джаваскрипте, они ответят, что стремились создать язык, на котором писать проще.
-1
На паскале тоже это можно реализовать. Так же как на ES5 написать классы. А то что это долго делать и результат не очень красивый будет — это не так уж и важно, правда? Почему вы защищаете шарп, это ведь почти такой же сахар по сравнению с паскалем? И, я должен это сказать, боже, он медленнее!

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

В вашей статье приводятся примеры совершенно разных языковых конструкций и немое удивление почему они работают по разному с разной скоростью. Это как написать «float a» на одном языке и «double a» на другом, удивляясь почему второй жрёт оперативы в два раза больше, даже не осознавая то, что можно и там и там написать и «float a». И читая подобную некомпетентность (точнее откровенное незнание кофе, как языка), у меня, признаюсь, очень сильно подгорает. =)
0
Чтобы реализовать всё, что есть в C# на паскале — придётся написать на нём vm и компилятор. И код в основном будет работать с той же скоростью, что С#. Игра не стоит свеч.

Гораздо лучше пример с С и С++. 2 разных языка, один является подмножетсвом другого, но все оптимизации, которые можно сделать для того, чтобы производительность С++ была такой же, как С — обязательно делаются.

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

Сравнение double c float тут некорректно, потому что такую ситуацию нельзя выловить компилятором. А ситуацию из статьи — можно. И приходится помнить, что конструкцию не надо применять, если шаг равен еденице. Это очень неприятно.

То, что вы классифицируете как незнание языка — на самом деле претензия к компилятору, который не оптимизирует ситуацию, которую оптимизировать можно.
+4
А вы понимаете что надо сравнивать код на кофе:
list.reduce ((prev, curr) curr + prev), 0


С этим на JS:
list.reduce(function(prev, curr) {
    return curr + prev;
}, 0);


И соответственно код на Coffee:
cubes = (math.cube num for num in list)


С этим кодом на JS:
var cubes = (function() {
  var i, len, results;
  results = [];
  for (i = 0, len = list.length; i < len; i++) {
    num = list[i];
    results.push(math.cube(num));
  }
  return results;
})();


Что бы уже были какие-то реальные претензии к транслятору и его оптимизациях.

Вы пишете цикл, а не вызываете метод и удивляетесь, что на выходе цикл, а не метод. Сколько раз это надо повторить всему сообществу хабра, что бы до вас наконец, простите, дошло, что вы сравниваете совершенно разные вещи?
+1
Если бы CoffeeScript выполнялся браузером, а не транслировался в javascript, то смысл делать сравнения, о которых вы говорите, был бы.

А тут такое дело. Мы точно знаем во что компилятор транлирует cubes = (math.cube num for num in list). И знаем во что он мог бы его транслировать для улучшения производительности. И знаем, что причин не делать этой оптимизации нет. Если мне приведут причины не делать этой оптимизации, до меня дойдёт, что я сравнивал совершенно разные вещи.

Причина, которую приводите вы — нужно транслировать for в синтаксически сходную конструкцию. Я считаю, что сохранить синтаксис не важно, так как это не даст ничего — важно сохранить семантику. Она при такой оптимизации сохранится.
+1
В ваших словах есть смысл, если считать кофе отдельным языком. Но проблема в том, что кофе — это транслятор, один-в-один. Все оптимизации делает программист так же, как он делал бы это на JS. У него такая идеалогия. Во втором абзаце на главной странице так и написано: «The golden rule of CoffeeScript is: 'It is just JavaScript'.». Он не извращает язык, не оптимизирует, он просто сокращает синтаксис приводит его в такой вид, какой бы любой человек написал бы на JS. Именно в этом и причины отсутствия каких-либо сторонних манипуляций с кодом, всё транслируется слово-в-слово и можно дословно перевести js в кофе и обратно, не потеряв практически не единой строчки кода.

Если же нужны оптимизации — никто не мешает поверх навесить какой-нибудь гулповский uglifer или ещё что.
0
Если при написании кода на CoffeeScript действительно надо думать на порядок больше, то на нём лучше не писать. Весь смысл использования языка, отличного от джаваскрипта в том, чтобы облегчить написание кода, а не усложнить его.

Знаете, а я предпочитаю больше думать и меньше писать. А вообще, код чаще читают чем пишут (несравнимо чаще), и вот читать CoffeeScript при разумном использовании существенно легче, полагаю, для этого он и создан в первую очередь (радикально меньше визуального мусора).
Ну и если есть 2 совершенно разных куска кода, которые используются для достижения одной и той же цели и один кусок быстрее другого, то лучше использовать инструмент, который генерит быстрый код.

При чем тут генерация вообще? Если бы у вас был шаг не 1, а 2 (как в моем примере выше) — у вас бы код был аналогичной конструкции.
На CoffeeScript можно и нужно писать cubes = list.map(math.cube) — это короче, понятнее и (судя по тому, что вы доказывали в статье) даже быстрее.
А то, что вы на JS взяли простой вариант, а на CoffeeScript взяли неудачный пример из документации (которая просто показывает что так можно делать, но не обязательно нужно) — это ваша личная проблема и ни чья больше. Повторюсь ещё раз, cubes = list.map(math.cube) — вилидный CoffeeScript, который не быстрее и не медленнее такой же строчки на JS. А то, что вы написали называется высасывание проблемы с пальца.
0
Знаете, а я предпочитаю больше думать и меньше писать

История развития языков программирования это история того, как сделать так, чтобы при написании кода можно было меньше думать. Если думать надо больше, то и неверных решений будет принято больше и ошибок значит будет больше.
Если бы у вас был шаг не 1, а 2 (как в моем примере выше) — у вас бы код был аналогичной конструкции.

Если есть способ оптимизировать случай с шагом в 1 — надо это сделать.
+1
Если есть способ оптимизировать случай с шагом в 1 — надо это сделать.

Не совсем, код делает ровно то, что вы написали. Хотите пройтись циклом и вернуть массив значений — код делает ровно это и ничего больше. На JS код был бы аналогичный выхлопу компилятора CoffeeScript. Если хотите применить .map — код опять абсолютно аналогичный.
Ваши примеры НЕ эквивалентны, они совершенно разные, как вы не поймете это! Что написали — то и получили.
0
Что-то я не совсем понял, почему map внезапно превратился в reduce?
-1
Потому, что код, сгенерированный CoffeeScript внутри устроен как reduce. Там есть накопитель, и цикл с его наполнением. map делает массив по размеру равный исходному и потом просто присваивает элементам массива нужные значения. Поэтому логично сравнивать код, сгенерированный CoffeeScript с reduce, а не с map.
0
Ну, во-первых, это сугубо его проблемы, как он устроен. Главное это смысловая суть кода.
А во-вторых, там нет вызова функции на каждой итерации. Так что я совершенно не уверен, что это справедливая замена.
0
Map мог оказаться быстрее цикла только за счёт оптимизации взятия элемента массива и оптимизации складывания в новый, но forEach всегда медленнее обычного цикла из-за накладных расходов на вызов функции. Самым оптимальным решением в данном случае будет раскрыть функцию и написать x*x*x прям в теле цикла.
0
Насчет forEach — да, по всей логике и здравому смыслу он должен быть медленнее. Но на практике — нифига! Я как-то тестировал и довольно много сравнивал — вполне сопоставимо он работает, а иногда даже существенно быстрее. У меня нет внятного объяснения этого феномена, но факт есть факт.
0
Эта тема подробно раскрыта в книге Гэрри Тауба Good Calories Bad Calories, а также в книге Дэвида Перлмуттера Grain Brain.

Лучше бы она была раскрыта в научно достоверных статьях из реферируемых источников.
0
Вас не затруднит привести несколько? Как-то не хочется только ради этого книжку покупать.
+1
Вы не понимаете несколько простых вещей. Да, выхлоп кофе неоптимален, но исключительно из-за использования .push вместо создания массива с заранее заданной длинной. В современных js движках массивы как раз таки массивы, а не хэши, если содержимое однотипно. В V8 методы массивов, вы не поверите, написаны на том же самом js и мало отличаются от представленного выше «оптимизированного» выхлопа кофе — тот же самый цикл и получение / установка элементов по индексу. Вот только с ненужной нам проверкой «дырки» в массиве — необходимо по стандарту. Да, и V8 инлайнит функции, но не коллбэки .map и ему подобных методов (хотя может что в последнее время и изменилось).
0
Да, действительно не понимал. Спасибо за комментарий и за ссылки.
+5
К счастью, я наделён редким даром. Я всегда узнаю зло, и неважно в какие одежды оно вырядилось на этот раз.

После этой фразы — мне стало совершенно понятно, что автор будет выступать в роли истины в последней инстанции…
UFO landed and left these words here
0
Да оно кешируется.
for (i = 0, len = list.length; i < len; i++)

И в последних версиях нода и в файрфоксе for быстрее, чем map.
Вся ценность статьи в коментаторах, которые объясняют, что в ней не так.
Only those users with full accounts are able to leave comments.  , please.