Pull to refresh

Comments 112

typeof age !== "undefined" плохой вариант. Лучше использовать сравнение непосредственно с undefined
age !== undefined

Но надо учитывать, что, теоретически, кто-то где-то в коде может определить глобальную переменную undefined и всё сломается.
На такой случай (ну и вообще) удобно использовать «хак» с инкапсуляцией:
(function(window, $, undefined){
// ваш код
// window и jQuery – просто для примера
// главное, чтоб на вход всегда ничего не передавалось для переменной undefined
}(window, jQuery))
Мне кажется стоит тогда уж с void 0 сравнивать.

age !== void 0
UFO just landed and posted this here
Удивительно что заминусовали толковый комментарий. Действительно, проверка на неравенство null гарантирует, что программа не выпадет с исключением при попытке получить какое-нибудь свойство переменной. Это очень удобное сравнение.
UFO just landed and posted this here
В статье кстати сказано про undefined и создание одноименной переменной — как вариант, предлагается использовать void 0.
typeof age !== «undefined» плохой вариант. Лучше использовать сравнение непосредственно с undefined
age !== undefined


А не наоборот?

(function() {
    var undefined = 1;
    console.log(undefined); // 1
    console.log(typeof undefined); // number
    console.log(typeof a); // undefined
})();
И словить ошибку, если переменная age не определена ни в одной из областей видимости. Отлично решение! Вам надо писать больше статей для новичков!
Способов выстрелить себе в ногу в JS огромное количество. Для защиты от необъявленных переменных я бы посоветовал, в статье для новичков, использовать линтер.
Почему минусуют человека? Все правильно сказано же — дереференс необъявленной переменной, в коде типа age !== undefined, выбьет ReferenceError: age is not defined же. typeof age !== "undefined" лишен этого недостатка.

При проверке же свойств имеет смысл проверять напрямую — foo.bar != null, потому что свойства ReferenceError не выбрасывают.
Я бы сказал, что это не недостаток, а преимущество. Т.к. если переменная не определена, то ваш кусок кода ничего не делает и его нужно убрать.
А вы можете себе представить человека в здравом уме, который переопределил undefined, и требует чтобы код продолжал работать?

Кстати, в глобале ещё полно глобальных переменных, почему от их переопределения никто не огораживается? Развлечения ради можете вбить в REPL node.js «Math = 0» и посмотреть, как его раскосячит.

Совсем другое дело — оптимизация на скорость. Вроде как, используя void 0, можно избежать долгого разрешения переменной undefined из глобала через всю цепочку скопов. Но в последнем хроме void 0 работает даже чуть медленнее.
>> можно избежать долгого разрешения переменной undefined из глобала через всю цепочку скопов

а это точно происходит? Разве движки это не оптимизируют?
Дык, ссылочка на jsperf в конце коммента как раз и говорит о том, что использование void 0 вместо undefined в качестве микро-оптимизации зачастую абсолютно бесполезно.
Паттерн (function() {...})() стоило сразу же назвать IIFE. Про "||" написано, а про "&&" почему то нет.
Вы про такую конструкцию?:
(function(){
  var sad = true;
  function makeMeHappy() {
    console.log('I\'m happy now!');
    return false;
  }
  sad = sad && makeMeHappy();
})();
Я думаю имелось в виду проверка на != null. Что то типа a && a.b && a.b.c && a.b.c().
Есть ещё один удобный (ИМХО) хак, который я всегда использую вместо оператора switch:
var options = {
  case1: function () { ... },
  case2: function () { ... },
  ...,
  'default': function () { ... }
};
options[value||'default']();

Вместо функций можно непосредственные значения указывать, зависит от ситуации.
Это не хак, а простая реализация паттерна «стратегия».
Использовал терминологию статьи. :)
Это паттерн-матчинг, разумная замена конструкции switch. Например в питоне этой конструкции вообще нет, вместо нее делают паттерн-матчинг. А в эрланге — так вообще нормальная практика, никаких ветвлений не нужно.
Совсем недавно оно в JS ещё и работало намного быстрее, чем switch. В новых версиях браузеров уже оптимизировали.
Без разрушающего присваивания (или разрушающего связывания) от паттерн-матчинга остается одно название.
Тогда уж лучше (options[value] || options['default'])() использовать.

А не то options[value||'default']() дурно ведёт себя, когда value пусть не == false, но и имени ни одного из свойств options не соответствует.
От value === «toString» все равно не застрахуешься…
Это убивает всю прелесть использования литерала объекта. Проще уж hasOwnProperty использовать, пусть это и далеко не так красиво выглядит.
Согласен. И это минус языка. На IE11+ можно уже использовать {__proto__: null, ...}, но всё равно не то.
Да, согласен, поторопился.
А как же хак «вертикальная черта — ноль»? Который позволяет получить целую часть от числа (или конвертировать строку в число и вытащить целую часть):

"8.97"|0; // 8
8.97|0; // 8
"8.01"|0; // 8
"a801"|0; // 0
"801a"|0; //0
Ещё можно парсить числа так:
x *= 1;
Способ веселый, но на очень больших числах начинает врать.
Обычное переполнение интеджера, уходит в отрицательное.
мой любимый хак:
if (~arr.indexOf('val')) {
    // Элемент в массиве есть
}
Ещё говорят, что это тоже хак:
var arr = [1,2,3,4];
arr.length = 0; // очищает массив
сомнительный хак.
var arr = [1,2,3,4];
arr.length = 0; // 15 символов
arr = []; // 9 символов

// Толи дело:
var arr = [4, 9, "e", 123, 321];
arr.length = 2;
arr; // [4, 9]
Надо заметить, что arr.length изменяет существующий массив, в то время как присваивание пустого массива создает новый массив. Иногда это может быть важно.
var arr = [1, 2, 3, 4];
arr.testProperty = true;
arr.length = 0;
arr.testProperty; // -> true
arr === arr; // true

var old = arr;

arr = [];
arr.testProperty; // -> undefined

old === arr; // false
Использование большинства подобных хаков приводит к небольшому уменьшению количества символов и сильному ухудшению читаемости и понятности кода. Если у тебя, %программист%, есть уверенность, что твой код будут читать и править люди, чья квалификация не уступает твоей, то ты можешь использовать эти хаки. Если такой уверенности нет, напиши гораздо более понятное
if (!age) {
  age = 10;
}
В tutorial’ах по значительной части динамических языков этот трюк описан и иногда даже рекомендован: из тех языков, что я знаю так делают Perl, Python, Ruby, Shell¹, Lua. В последнем так вообще вместо тернарного оператора (которого нет) часто используется a and b or c (в отличие от того же Python b здесь не должно быть всего лишь nil и false, что сильно расширяет область применимости).

Так что понимание такого кода достаточно вероятно.

¹ Речь идёт о только кодах возврата (т.е. о “присвоении” $?), что практически бесполезно. Конструкция $(( a || b )) может вернуть только 1 или 0.
Разумеется, квалифицированному программисту не составит разобраться во всех этих конструкциях. Более того, он в силу своего опыта знает во всех тонкостях как они работают и т.д. Безусловно, не составит труда и новичку сесть разобраться что именно хотел сказать автор кода. Я говорю о другом. О том, что понятность кода для программистов более низкой квалификации приводится в жертву его (кода) компактности, а это, на мой взгляд, неправильно. Грубо говоря, я могу нахреначить в своём коде столько хаков, что любой, прочитавший 5 его строк, проклянёт меня. А могу написать тот же самый по функциональности код таким образом, что любой юниор без труда сможет продолжать его развивать.
Зато для всех кроме новичков ваш код будет менее читабелен. Банально потому что длиннее.
Вы всегда именуете переменные одним или двумя символами? Почему нет, ведь иначе код становится длиннее?

Программирование — это искусство, в том числе искусство находить баланс. В количестве хаков, длине имен переменных, количестве комментариев, уровне абстракции и т.д.

Ещё раз: если вы уверены, что ваши хаки поймут все, кто будет читать и поддерживать код — на здоровье, лепите сколько хотите. Если такой уверенности нет, то лучше упростить код. Это же элементарно — подумать о тех, кто пойдёт за тобой.
UFO just landed and posted this here
Речь как раз о том, чтобы не стать Эллочкой Людоедочкой, которой хватает 10 строк, чтобы выразить любой алгоритм.

Если честно, я в некотором недоумении. Я не призываю раздувать код, я не призываю отказаться от паттернов, от красивых и элегантных решений. Я призываю писать код так, чтобы он был понятен тому, кто его читает, даже если читающий обладает более низкой квалификацией. И говорю о том, что использование хаков, как правило, затрудняет, а не облегчает чтение кода. При этом я уже на протяжении 5 комментариев спорю с чем угодно, но только не с этим изначальным высказыванием.
UFO just landed and posted this here
this.age = age || his_age || stored_age && parsed_age || 10.

У меня полное ощущение, что вы хорошо понимаете, что я пытаюсь донести. Разве нет?

UFO just landed and posted this here
Наверное, я взял слишком простой и очевидный хак, чтобы продемонстрировать свою мысль. Признаю свою ошибку.
Никогда не считал себя квалифицированным кодером на js, но только из этой статьи узнал что что вот это this.age = age || 10 хак. Достаточно очевидная конструкция. Много где видел и сам писал. И читается проще чем if. ИМХО, конечно.
Да как же проще-то? Сравните:
1. Если не возраст, то сделать возраст равным десяти.
2. Сделать возраст равным возрасту или 10.

Какой из двух вариантов заставляет меньше напрягаться встроенный в мозг интерпретатор, неужели второй?
Возможно, я уже не человек, но именно второй вариант мне кажется прозрачным и понятным.
Мозг — не компьютер. Оба варианта воспринимаются как «если не возраст, то возраст равен 10», если вы знакомы с этим хаком.

А если вы достаточно длительное время писали на Perl’е (почему‐то там я использовал эту конструкцию чаще всего), то age = age || 10 будет восприниматься именно так: «если возраст не определён, то он равен 10». Вам нужно обновить свою версию «встроенного в мозг интерпретатора»: он вполне способен по ходу дела повышать уровень абстрагирования. Не думаю, что тут имеет смысл сравнивать энергетические затраты на повышение уровня с затратами на чтение вчетверо большего числа строчек.

Ктати, если переменных со значением по‐умолчанию больше одной, то || всегда лучше:

this.age = age || 10;
this.sex = sex || "Male";

if (!age) {
    age = 10;
}
if (!sex) {
    sex = 10;
}
this.age = age;
this.sex = sex;
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Гм. Откуда в this возьмётся age?

Кроме того, у вас здесь три строчки «мусора»: кода, который не нужен для восприятия алгоритма (если такие конструкции раскиданы по всему коду, то к ним быстро привыкаешь), а также кода, который это восприятие затрудняет (если вы такую конструкцию видите в первый раз, то понять, что она делает, гораздо сложнее, чем в варианте с this.age = age || 10;).

Даже более того, вы используете ||. Супер! Будучи применённой к значениям внутри по‐первости (если о новичках: людях, плохо знакомых с вашим кодом) непонятного объекта defs (definitions? defaults? ещё что‐то?) она, разумеется, становится понятнее, чем в this.age = age || 10;.

Переменная defs понадобится только, если переменных уж слишком много: в этом случае для установки значений будет использоваться функция вида mergeObjects(this, args, defaults), а все переменные будут в объекте args.
Если этот хак общепринятый и программисту он не понятнет, то программист полезет в гугл или форум и станет немножечко продвинутей
Приведение переменной к числу при помощи +"123" лучше, чем используя parseFloat/parseInt тем, что parse* для строки "123foobar" вернут 123, тогда как +"123foobar" вернет NaN.
Еще в копилку хаков — a >>> 0 работает, как приведение числа к uint32_t:
12345 >>> 0 // 12345
-12345 >>> 0 // 4294954951
12345678901 >>> 0 // 3755744309
Работает лучше, чем a & 0xFFFFFFFF, из-за явной беззнаковости.
Чит для получения всех ключей объекта в массив:

var arr = [], i = 0, obj = {a:1,b:2,c:3};
for( arr[i++] in obj );
// arr = ['a','b','c']

Я бы использовал Object.keys если es5 доступно. А вообще если, нечто расширит Object.prototype в ie8 вы словите нечто веселое. Вам бы проверку для hasOwnProperty добаить и тогда это будет почти shim для Object.keys.
Зависит от того поведения, которое нужно. Временами надо получить и ключи прототипов. hasOwnProperty сюда не вкорячить, тут вся магия в arr[i++] in.
Оно и понятно, что в зависимости от того что нужно. Хотя признаться у меня не было случая когда нужны вообще все методы, даже прототипов, а вот ie8 появляется регулярно.
честно говоря от фразы
если вы поместите объект в условный оператор if, то он выполнит либо true-ветку кода (когда объект имеет значение true), либо выполнит false-ветку (соответственно, когда объект имеет значение false).

у меня мозг взрывается.
Напишите «переменная», «выражение», ведь неокрепшие разумом дети будут читать.
Однако стоит заметить, что с таким способом вам не удастся задать переменной значение 0, так как 0 является ложным выражением.

image
Разумеется, если значение по умолчанию 0 — то никаких проблем не возникает. Проблемы начинаются, когда значение по умолчанию — 10, а нам надо 0 передать.
Ах вот о чём речь. Не сразу понял.
В JavaScript || является примером выполнения короткого замыкания.

крайне неудачный перевод англ. термина Short-Circuit Evaluation, у нас обычно это называют «короткой схемой вычисления» или «упрощенный порядок вычисления выражений»

В JavaScript есть понятие замыканий (closure) и фраза «короткое замыкание» взорвет мозг юному паддавану
Однако стоит заметить, что с таким способом вам не удастся задать переменной значение 0, так как 0 является ложным выражением.

Поправьте плз, если я не прав.
function x(x){alert(x||0)};x(); //0
Прибавлю, что «void 0» ещё и набирать быстрее и проще, чем «undefined».
Ну это смотря где набирать. В моем редакторе автодополнение выкинет undefined уже после «un».
UFO just landed and posted this here
Статья из цикла «Как из JS сделать perl»
Конвертация строки в число с помощью оператора +
И не только строки:

+new Date
В общем случае унарный плюс форсит вызов valueOf.

Скорее всего, вы бы применили методы parseFloat и parseInt.
В очередной раз незаслуженно не упоминается функция и конструктор Number. Функции parseInt и parseFloat делают несколько больше, чем конвертация в число. Наиболее близкий аналог это «выковыривание» числа из числоподобной строки. Для конвертации лучше прямолинено использовать конструктор Number.
Вот хороший пример:

['1','2','3'].map(parseInt);
['1','2','3'].map(Number);
С другой стороны, вместо if (x == «test») можно просто написать if (x). Если же x будет пустой переменной, то просто выполнится код из блока else.

Если x число и равно 0, то тоже выполнится else. Из-за этого многие пишут код, который потом работает, не так, как ожидается.
Поэтому, товарищи, пишите тесты!
Понимаю, что перевод, но все эти вещи описываются в любом туториале (javascript.ru, mozilla) и книгах типа «Effective JS» и «JavaScript: the good parts». Причём объясняется лучше почему, как, зачем, когда надо, когда не надо.
UFO just landed and posted this here
Есть закономерность: Каждый, кто знакомится с внутренностями JS — начинает высказывать предсказуемыми фразами.
UFO just landed and posted this here
А никто и не говорит иного — хуже языка, нежели JS лично я пока не видел. Даже всеми осуждаемый PHP по сравнению с сабжем — является идеально структурированным и продуманным до мелочей.
Вы видимо мало языков знаете. Есть и хуже.
В таком случае примеры в студию ;)
Этот пост о javascript. Я не хочу плодить оффтоп. Если вы хотите подискутировать на эту тему, то напишите отдельный пост о том, почему на ваш взгляд javascript ужасен и спросите мнения у сообщества. И уже в рамках этого поста мы с удовольствием все обсудим.
UFO just landed and posted this here
Помню вашу статью про «фрактал отсоса». Вижу, что мнение ваше не изменилось. Повторно разводить тут полемику я не вижу смысла. У JS тьма недостатков, но для тех задач, которые он решает, у нас нет альтернативы. Потому и появились coffeescript и typescript, которые тоже далеки от идеала.
Вывод из морали: Objective-C появился для того, чтобы хорошенько развеселить очень грустных разработчиков.
Достаточно было сказать что в js слабая динамическая типизация и программа старается не падать почти никогда(пытаясь обработать необрабатываемое), все ваши аргументы против js проистекают отсюда.
Удивительно как вы эту одну особенность языка свели к множеству недостатков.
Javascript и правда ужасен, но и ваши аргументы тоже.
UFO just landed and posted this here
Я хорошо знаю js, и мне он нравится — это я подтверждаю закономерность, или нарушаю?
UFO just landed and posted this here
Провоцируете? Хорошо.

По сравнению с PHP
В js всё-таки меньше бардака, и объектно-ориентированность прикручена получше. В JS можно понять несколько принципов, и дальше можно в уме прокручивать код не хуже интерпретатора:

— Значения приводятся к нужному типу, если это возможно. У объектов приведения к примитивам для это дёргаются методы valueOf и toString.
— Если хотя бы один операнд — строка, то весь оператор строка работает как конкатенация.
var объявляет переменную в скопе на уровне функции. Если перемнная явно не объявлена, она она через механизм делегирования берётся из верхних скопов.
— Если у объекта нет свойства, она через механизм делегирования берётся из прототипа.
this определяется при вызове, а не при создании.

В PHP же без php.net, открытого в соседней вкладке делать нечего.

По сравнению с Python
Да, считай, два брата-близнеца. Только питон не так активно приводит типы, как js. Половину wtfjs можно портировать на питон без особых изменений (напомню, что BOM/DOM не является частью языка), даже поведение супер-страшного прототипного наследования:

function Foo(){};
Foo.prototype.bar = 42;

var foo = new Foo();
console.log(foo.bar); // 42

Foo.prototype.bar = 32;
console.log(foo.bar); // 32


class Foo:
	bar = 42

foo = Foo()
print foo.bar # 42

Foo.bar = 32
print foo.bar # 32


Сравнивать с VBScript, надеюсь, не заставите? А с Руби у меня, к сожалению, не сложилось.
Значения приводятся к нужному типу, если это возможно. У объектов приведения к примитивам для это дёргаются методы valueOf и toString.

В php тоже приводятся к нужному типу, причём приведение типов более приятное, нежели в JS, у объектов также дёргается метод __toString.

Если хотя бы один операнд — строка, то весь оператор строка работает как конкатенация.

В php специально сделали оператор ".", для конкатенации, а "+" для сложения. При использовании конкатенации — всё будет стараться приводиться к строке, при сложении — к числам — это значительно удобнее и гибче, нежели в JS.

var объявляет переменную в скопе на уровне функции. Если перемнная явно не объявлена, она она через механизм делегирования берётся из верхних скопов.

$some — объявляет переменную в скоупе на уровне функции. Если требуется передать переменную в безымянную функцию — её требуется явно указать в операторе use — это облегчает понимание кода, так же в абсолютно любом месте кода ссылки на класс и на инстанс класса, включая родителей и наследников ($this, self, parent, static) никогда не поменяются — это намного удобнее и практичнее, нежели страдать с постоянно изменяющимися контекстами this и через раз использовать call\apply.

Если у объекта нет свойства, она через механизм делегирования берётся из прототипа.

Очень сомнительное преимущество. С таким же успехом в php можно создать абстрактный класс и назвать его Prototype и после наследования от оного — будет тоже самое, если у объекта нет свойства — оно возьмётся из абстрактного класса =)

this определяется при вызове, а не при создании.

А вот это как раз наоборот боль — изменяющийся контекст добавляет больше проблем, нежели их решений. Мне проще в нужных местах передать этот контекст в виде аргумента, нежели следить за тем, чтобы он нигде не поменялся от неосторожного телодвижения.
Если взять Ваш код на питоне, добавить в конец класса end и заменить print на puts (или p) — получится ruby. Так что отличий в примере почти нет.
UFO just landed and posted this here
В других языках это либо запрещено, либо есть специальный оператор конкатенации.
Извините, мне стало смешно. Наверное, C#, Java и Python слишком мало распространены…
UFO just landed and posted this here
Может вы перепутали с Ruby? Насколько я знаю — это единственный язык (помимо JS), где символ "+" используется как в качестве сложения, так и в качестве конкатенации и при этом он не выдёт ошибку при использовании этого оператора с разными типами.
Хм… Я и не говорил, что описанные мной принципы — это нечто уникальное для джаваскрипта. Это — минимум, знание которого позволит не удивляться wtfjs.

Когда при приведении к json метод возвращает null — это не удивительно, в json нет NaN. Когда таймстемп невалидной даты — невалидное число, для меня тоже нет в этом ничего удивительного. А примеры с == true надуманны. Вы видели, чтобы кто-то так писал на практике, сравнивая я true явно, причем двойным равно? Но так или иначе — тут оба операнда приводятся к числу, и никакой магии нет. И это, кстати, ничем не лучше пхпшного isEmpty().

Да, на php я не писал ничего более-менее серьёзного уже пару лет. И, возможно, там всё стало хорошо, и даже появилась поддержка юникодовых строк. Но ведь и из джаваскрипта понемногу выкашивают родовые травмы — нельзя взять и поменять некомпилируемый язык. Та же хрень и с питоном.

Пробрасывать целый скоуп в функцию при помощи apply

Вы не знаете, что делает apply, ведь так?

И да, я помню, как в ноде прочитать файл (монгой я не пользуюсь), хотя пишу на ней пару десятков строчек в месяц максимум — стандартный модуль возвращает объект стандартного интерфейса Stream. И никаких плясок с ob_start(), из-за того, что какой-то метод может возвращать строки только записав в stdout.
UFO just landed and posted this here
Ну, нравится мне этот язык. На нём можно писать для продакшна — жутко скучно со всеми включенными ворнингами jshint (кроме eqnull), используя только самые известные и несложные паттерны. Ну вроде как, в php можно использовать register_globals и $_REQUEST, но не стоит. Так и в js можно использовать with и ==, но не стоит.

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

Наверняка у других участников и коллег тоже подобное психоэмоциональное состояние перманентного стресса после JS, под конец рабочего дня ;)
Я уже больше 6 лет на фронте, было всякое, но сейчас: прикрутил нормальный пакетный менеджер, разруливающий зависимости, подучил матчасть по языку. И знаете, мне JS тоже нравится. При всех его «особенностях». Поэтому не говорите за всех.
Ах да. Про функцию sum. Это уже зависит от вас. Если внутри аргументы будут приводиться к Number, то ничего плохого не произойдёт, и не надо приводить ничего вручную при каждом вызове — это уже вопрос апи, а не языка.

Угадайте, что вернет `Math.max(«2», «3») ` из стандартной либы. Как ни странно, число 3.
UFO just landed and posted this here
А ведь помнится ходили разговоры про tcl в браузере вместо/вместе c js…
Sign up to leave a comment.

Articles