Pull to refresh

Comments 62

Как по мне, так это не шпаргалка, а краткий, обрезанный перевод документации.
Шпаргалка вот так примерно выглядит: Javascript cheat sheet
Предпочитаю SugarJS. Он проигрывает Lo-Dash в производительности, но значительно удобнее и естественнее в использовании.

sugarjs.com/

Для тех же, кому производительность критична, рекомендую LazyJS. В случаях, когда после объода свойств объекта/массива не требуется вернуть новый объект со всеми свойствами, LazyJS выигрывает у Lo-Dash в производительности. На составных операциях разрыв становится значительным. Например, на операции map -> filter LazyJS в пять раз быстрее, чем Lo-Dash, и в пятнадцать, чем SugarJS. Если же вам нужно не просто пройтись по значениям свойств, а собрать новый объект/массив, то LazyJS теряет преимущество.

danieltao.com/lazy.js/

Бенчмарки десяти аналогичных библиотек: danieltao.com/lazy.js/comparisons.html

Извините, не могу нормально оформить ссылки. Наказан фанатами Half-Life за непопулярное мнение.
Добавлю. что производительность, на мой взгляд, имеет значение только на обработке сколько-нибудь больших объемов данных. Если вы работаете с фронтендом, то никакой разницы в быстродействии этих библиотек вы не обнаружите.
а что, расширять прототипы встроенных объектов уже разрешили?
Я не имел в виду что невозможно, я имел в виду что нельзя. Это глобальная область видимости. Будут конфликты рано или поздно.
Да, в Ruby, например, для решения этой проблемы придумали Refinements. Перед тем, как использовать свои или чужие модификации глобальных объектов в своем коде, ты можешь указать, какие из модификаций тебе нужны. В JS такого нет и, скорее всего, никогда не будет.

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

Решение простое: так делать не нужно. Расширяйте глобальные объекты только для того, чтобы за стандартную библиотеку JS не было стыдно. Не меняйте работу стандартных методов. Документируйте, какие свойства вы (пере)определяете. Используйте юнит-тестирование.
При любой осторожности проблемы будут, может меньше и реже, но будут. Зачем использовать подход который пораждает проблемы и требует дополнительной осторожности? В javascript и без того нужна большая осторожность, особенно для новичков.

И ради чего? Всего-то чтобы кое-где на пару символов меньше кода написать.
Приводите примеры этих ужасных проблем, или ваше утверждение голословно. Суеверие какое-то.

В том-то и дело, что JS — это динамический язык, он позволяет такие вещи, и они работают прекрасно.
есть две библиотеки типа sugarjs мне нужны обе, я не могу подключить их обе, например
И что? Это проблема другого рода. Вы ее спрогнозировали заранее при планировании проекта и можете решить ее любым способом, который вам нравится. Эта проблема не приведет к непредсказуемым результатам, о которых тут все говорят, а примера привести не могут.

Я пока не столкнулся с сиутацией, чтобы нужная мне функция отсутствовала в Sugar, но была доступна в Lo-Dash. Но если такое произойдет, то я прежде всего попытаюсь добавить ее в SugarJS. Разработчик у SugarJS очень отзывчивый.
> Я пока не столкнулся с сиутацией, чтобы нужная мне функция отсутствовала в Sugar

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

> Эта проблема не приведет к непредсказуемым результатам, о которых тут все говорят, а примера привести не могут.

Сами придумайте. Тут ничего особенного, просто кто-то переопределит чей-то метод. Это всеравно что все методы underscore вынести из _ в window. Ну будут же конфликты, согласитесь.

Или вы предлагаете принцип что только одна библиотека может расширять прототипы? Но кто решает кому можно, а кому нельзя?

Повторюсь, может проблемы не такие уж и серьезные, и может их можно избегать путем договоренностей (внутри команды, между майнтейнерами библиотек и т.д.) Но зачем всё это нужно если можно просто не трогать прототипы?
Не надо использовать опциональные зависимости вроде SugarJS или Lo-Dash в библиотеках.

При написании библиотек нужно стремиться к тому, чтобы исключить использование вспомогательных библиотек. Все, что делают Lo-Dash и SugarJS, можно сделать и вручную — с той же или большей производительностью.

Если бы эта рекомендация повсеместно нарушалась, то при установке зависимостей вашего проекта вы бы получали кашу из Underscore, Lo-Dash, Prototype, MooTools, Signals, Zepto…

И лично SugarJS тут совершенно ни при чем.
> При написании библиотек нужно стремиться к тому, чтобы исключить использование вспомогательных библиотек.

Я хочу иметь возможность использовать любые библиотеки, в том числе и плохо написанные и с зависимостями. Обычно я не буду конечно устраивать кашу из Underscore, Lo-Dash, Prototype, MooTools, Signals, Zepto…, но хочется иметь возможность это сделать при необходимости.

И вы не на весь комментарий ответили.
Вот пример, как расширяют протоип нативного объекта в крупной библиотеке: github.com/douglascrockford/JSON-js/blob/master/json2.js#L174

Обратите внимание, что это делает не кто-нибудь, а Дуглас Крокфорд — апологет JS и, в числе прочего, автор JSLint.
Это полифил. Полифилы — единственное допустимое место где можно так делать.
UFO just landed and posted this here
Кстати, а при чем тут defineProperty и ие8? Чтобы расширить прототип встроенного объекта defineProperty не нужен.
Если просто сделать `Object.prototype foo = function () {}`, то новое свойство будет enumerable. Это сломает работу всего на свете.

defineProperty позволяет создавать не-enumerable свойства, безопасные во всех отношениях.
> безопасные во всех отношениях.
а что по повоу «Это глобальная область видимости. Будут конфликты рано или поздно.»?
ИМХО — у него еще пара минусов.
Одно дело — добавление в прототип своих методов, реализация полифилов, соответствующих спецификации, но подмена нативных методов собственными…

['one','two','three'].map('length'); // => [3,3,5]

Объектное api отсутствует как таковое. Как и undescore / lodash, больно избыточен. Многие методы практически дублируют другие.
Подмена нативных методов собственными в случае SugarJS сделана аккуратно и не нарушает работу существующего кода. Там не меняется поведение переопределенных методов, а только добавляются новые возможности. Эти возможности не будут задействованы, если вы будете пользоваться переопределенными методами как обычно.
О, так это вы. Увидел на гитхабе раньше, чем здесь.
Вот что мне не нравиться в undescore — это то что они не стали выносить методы в прототипы нативных объектов js. То есть выносить .map() в прототип Array я считаю намного правильней ибо это приучает тебя писать в стили нового стандарта ECMAScript 5 (уже конечно не такого нового) и дает вам возможность скажем на мобильных устройствах не использовать библиоткеу да и вообще приучает к использованию нового апи. под _.method я бы выносил только те функции которые не попали в спецификацию
Поддерживаю. По этой причине предпочитаю SugarJS (см. коммендарий выше).

> _.method я бы выносил только те функции которые не попали в спецификацию

Зачем? По-моему, это просто глупо, когда апологеты JS разводят понты про протоипную модель, а когда доходит до дела, то боятся модифицировать прототип.
Вы наверное путаете что-то? В прототип таких вещей как Object.prototype Array.prototype нельзя выносить кастомные методы, я лишь предложил туда выносить методы из спецификации если таковые не поддерживаются данными праузером, причем эти методы должны работать 100% по спецификации, чтобы вызвав .map() я не получил потом бредогого созания от автора.

Если вы сделали собственнвй метод для работы с массивом типо quickSort() не надо его пихать в прототим массива, это будет вносить путаницу Создайте свой класс или объект с утилитными методами.

По-моему, это просто глупо, когда апологеты JS разводят понты про протоипную модель,


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

поведение отдельных функций в библиотеке может в каких-то деталях отличаться от одноименных методов стандартных объектов (не со зла, конечно, а для общего блага — ленивость, расширенный интерфейс, и т.п.).и покуда библиотека не лезет в прототипы, это вполне допустимо.
А по-моему, это нормально.
> В прототип таких вещей как Object.prototype Array.prototype нельзя выносить кастомные методы
> идите матчасть учить

А сами подучить не хотите?

obj = {};
Object.prototype.foo = 1;
console.log(obj.foo); // => 1
dbanet как и другие имел в виду, что это плохо, непредсказуемо в дальнейшем и прочее-прочее, а не то, что это невозможно сделать. Речь идет о расширении прототипов Hosted и Native objects. После того как prototype.js намудрил с расширением прототипов эти споры всегда будут актуальны.
> «это плохо, непредсказуемо в дальнейшем и прочее-прочее»

Бремя доказательства лежит на стороне обвинения.

Без аргументации ваше утверждение совершенно голословно. Суеверие какое-то.
Аргументация простая — сейчас прототип расширяет одна библиотека, завтра приходит хитрый джуниор, который тоже решает добавить в Array.prototype.map нужную ему функциональность, которая ломает поведение js. Или подключается другая библиотека, которая опять же хочет вмешаться в поведение js-объектов и пр. Научитесь воспринимать доводы людей, а не кричать о том, какие все тупые.
Если у вас в проекте участвует несколько человек, то вы должны вести по нему документацию. Документируйте, чем вы расширяете стандартную библиотеку, вот и все. Прежде, чем расширять прототипы нативных объектов (непосредственно или при помощи подключения библиотек), сверяйтесь с вашей документацией. Это же элементарный здравый смысл.

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

А с тем, что джуниор может сломать поведение JS в вашем проекте, я не спорю. Только SugarJS тут ни при чем. Он не позиционируется как защита от идиотов.
Речь уже далеко не про SugarJS, а про то что не надо расширять нативные объекты нестанждартной реализацией или методами, которые работают не по стандарту.
И отмазка иди читать доку — не работает!
Если я буду читать доку потмоу что вы изменили стандартный метод .map в прототипе массива то я буду тупо читать доки а не писать код. И даже опытный сеньер жс кодер будет вам писать 100500 ошибок, спотыкаясь на таких вещах.

Наверное стандарты для того и существуют чтобы их соблюдать и упрощать жизнь?
Расширение прототипов, конечно, удобно, но допустимо далеко не всегда. Если вы пишите, например, модуль для Node.js, то расширять прототипы глобальных конструкторов явно не стоит.
> Если вы пишите, например, модуль для Node.js, то расширять прототипы глобальных конструкторов явно не стоит.

Тот же SugarJS, который очень активно расширяет протоипы глобальных объектов, доступен в качестве модуля для Node. Пример использования: stackoverflow.com/a/9321954/901944 Работает это замечательно и совершенно предсказуемо.
Я это прекрасно знаю и использовал, в своё время. Предположим, вы пишите модуль для Node.js используя Shugar.js, заливаете его в npm. Пользователь решает его использовать в своём проекте, ставит, получает в нагрузку Sugar.js. Вот только в качестве стандартной библиотеки он использует MooTools. И проект ложится. Одно дело — писать, используя Shugar.js, конечный продукт, другое — публичный модуль.
Объясните, пожалуйста, почему SugarJS ломает MooTools. Я попробовал погуглить, не нашел ни подтведждений, ни опровержений.

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

Это просто здравый смысл — не использовать опциональные зависимости в публичных библиотеках.
Нашел в исходниках SugarJS юнит-тесты взаимодействия с MooTools:
github.com/andrewplummer/Sugar/tree/master/test/environments/mootools

Так что пока вы не продемонстрируете обратное, считаю ваше утверждение голословным. Прошу демонстрировать сразу в виде баг репорта в одну из этих библиотек.
Вы это серьезно? Может потому, как расширяют глобальные конструкторы и их прототипы методами с одними и теми же именами, но разной сигнатурой? :) Мне приходилось разгребать после такого проекты, это страшно.

// Sugar.js
Object.merge(target , source, deep = false, resolve = true)
// MooTools
Object.merge(obj1, obj2[, obj3[, ...]])

Именно по этой причине в моей стандартной библиотеке есть возможность сборки как с расширением глобальных конструкторов и их прототипов, в стиле Sugar.js, так и без, в стиле undescore.
Вас не смущает то что в юнит-тесты одной библиотеки пришлось добавить тест на совместимость с другой совершенно перпендикулярной библиотекой? Хорошо что их только две таких смелых, современных, а то пришлось бы побольше тестов написать :)
Нет, не смущает. Вам же не приходит в голову использовать в одном проекте, например, jQuery и MooTools. Так зачем использовать вместе MooTools и SugarJS? Если у вас уже есть MooTools, используйте MooTools. И ведите документацию.
Еще раз. Объясняю на пальцах. Есть пара npm модулей, написанных не вами. Один использует MooTools, другой Shugar. Оба они добавляют метод Object.merge. В одном этот метод пытаются использовать для слияния 3 объектов, в другом — для глубокого копирования свойств. Вам в голову не приходило использовать MooTools и Shugar в одном проекте, вы даже не знаете об их существовании. Вы подключили оба эти модуля. Будет ли работать ваше приложение?
Shugar.js штука хорошая, но не панацея. Его стоит использовать только для написания конечных приложений. Undescore тоже штука хорошая, но не на столько удобна. И не дай бог мне наткнуться на npm модуль, написанный вами.
Я уже писал выше, что библиотеки должны стремиться к исключению опциональных зависимостей. В противном случае проекты будут представлять кашу из зависимостей.

SugarJS не делает ничего, что нельзя было бы выполнить нативно.

Посмотрел на npmjs.org. От MooTools зависят 16 библиотек, из них половина — расширения для MooTools.

У SugarJS ситуация хуже, от него зависят 110 библиотек. Не знаю, о чем они думают. Вероятно, описанная вами проблема до сих пор не всплывала.
Кстати, совсем забыл. Если не попросить его явно, Sugar добавляет в Object только одно новое свойство — Object.prototype.extended, которое возвращает объект, расширенный остальными фичами Sugar.

Соответственно, в вашем примере {}.merge будет выполнен MooTools, а {}.extended.merge — SugarJS.
Sugar.js в прототип Object не добавляет вообще ничего. Object.merge — статический метод конструктора в обоих библиотеках. И MooTools — только пример.
Пардон, я имел в виду это: Object.extended({foo: 'foo'}).merge({bar: 'bar'}) // => {foo: 'foo', bar: 'bar'}

Это работает, если подключить одновременно SugarJS и MooTools в любом порядке.

Ломается ли при этом MooTools, я не проверял. Как я сказал выше, SugarJS, MooTools и подобные библиотеке не должны использоваться в качестве зависимостей других библиотек. Что же касается прямых зависимостей конечного проекта, тут вы барин и можете сами решить, какую из библиотек использовать.
Последний абзац я и пытался до вас донести с самого начала.
Да, вы правы. Прошу прощения, запутался немножко в комментариях: спорю параллельно в нескольких тредах.
они не стали выносить методы в прототипы нативных объектов js

для этого у них есть chain:

var characters = [
  { 'name': 'barney',  'age': 36 },
  { 'name': 'fred',    'age': 40 },
  { 'name': 'pebbles', 'age': 1 }
];

var youngest = _(characters)
    .sortBy('age')
    .map(function(chr) { return chr.name + ' is ' + chr.age; })
    .first()
    .value();
Там только 3 функции переведены. А смысл в том, чтобы быстренько глазами пробежаться. Напомнив себе API.
Может, тогда стоит оформить эту статью как pull request к указаному выше сайту, дабы позволить людям проще находить данную информацию?
Статья, ради статьи, на хабре щас более 50% так
P.S. хотел как ответ на вышестоящий коментарий, сорри
в документации почему-то про это не пишут — функции _.max() и _.min(), что в underscore, что в lodash понимают только числа, а не вообще. т.е. их нельзя использовать, например, чтобы найти в коллекции максимальную строку — получается ерунда. все потому, что. авторы underscore пытаются изобразить конкретно Math.min, и Math.max, а авторы lodash изображают underscore.
А, собственно, что Вам мешает использовать кастомный фильтр, который преобразует «максимальную строку» в число?
имеем:
>>> 'ab' > 'aa';
true

>>> _.max('aa', 'ab');
-Infinity
из документации не очевидно, почему именно такой результат. тем не менее, разработчики underscore считают это поведение правильным.

в трекере мне предложили вот такой вариант для нахождения максимальной строки:
>>>  _.reduce(['aa', 'ab'], function(a, b){ return a > b ? a : b; });
'ab'

А, собственно, что Вы предлагаете, пока не понятно.
Sign up to leave a comment.

Articles