Pull to refresh

Comments 53

Ожидал очередную головоломку, а тут очередная статья, о том что js не идеальный. JSLint спасает от всех неоднозначных фич языка, а так вообще есть TypeScript, Flow где вообще все в рамках приличия, поэтому проблемы js — РЕШАЕМЫ и надуманы )))
К сожалению линтером не обойтись… точки с запятой, скобки да strict equals. Многие вещи вполне допустимы для JavaScript-разработчика по самой природе языка, но со стороны очевидны не всем. И я специально не брал во внимание какое-то типизированное надмножество: у каждого свое ) но ведь можно в рамках приличия и без него.
Ну не знаю, у меня линтер справляется со всем кроме типизации. А насчет очевидности — ну тут уже зависит и от опыта. Ну проблема в js, в том что тут нельзя просто сделать deprecated конструкциям языка (потому что старые сайты должны работать в новых браузерах), поэтому и тянется легаси «неочевидностей».
Мое скромное мнение в том, что не надо постоянно ссылаться на «легаси неочевидностей», показывая кому-то свой код ) Если вся команда — это JavaScript-мастера, то вопросов нет. Иначе стоит задуматься о возможностях языка быть понятнее, которыми он в конце концов располагает и не нуждается в deprecated (почти негде).
Практика показывает, что каждый настраивает линтер под себя, в итоге он не панацея. Проблем бы может быть с TypeScript не было, если бы ему опять таки не пришлось контактировать с библиотеками написанными на чистом js, в итоге внедрять его очень не просто, приходиться писать много типов, ну или добавлять через дженерик (а тогда теряется весь смысл в этой строгой типизации) особенно если это уже рабочий проект.
Полностью согласен с тем, что в основном холиварят и поливают говном те, кто так и не осилил его в прошлом.
Сейчас это очень даже выразительный и понятный язык.
UFO just landed and posted this here
А в чем вопрос? это не идентичные функции, просто примеры. Конечно надо дополнить нужной проверкой на число, на ноль если нужно. Поинт в том, что явная проверка аргумента понятнее себя ведет нежели специфичный «логический» оператор.
Поймал себя на мысли, что не могу придумать ни одного кейса когда можно (и тем более нужно) использовать нестрогое сравнение.

Довольно старая статья об этом: When is it OK to use == in JavaScript?.


По большому счету есть кейсы когда можно, но нет кейсов — когда нужно )
В библиотеках часто используют "когда можно", чтобы код сократить (личное наблюдение).

UFO just landed and posted this here
Там пример про значение аргумента по умолчанию в случае его отсутствия, не про валидацию.
Чтобы избегать такого:
function ping(host, count) {
    count = count || 5;
    /* ... */
}

как раз и были созданы параметры по умолчанию, упоминаемые выше. Вот как бы оно выглядело после рефакторинга:
function ping(host, count = 5) {
    // count = count || 5;
    /* ... */
}

Поэтому приведенный автором способ улучшения
function ping(host, count) {
    // OR arguments.length?
    if (typeof count == 'undefined') {
        count = 5;
    }
    /* ... */
}

ничем не лучше антипаттерна
count = count || 5;
Согласен ) Я поленился придумывать более сложный пример инициализации без привязки к аргументам функции: да и посыл, казалось, понятен был и так.
UFO just landed and posted this here
UFO just landed and posted this here
Ответ про разный результат был дан выше. Пример не об этом и мне стоило более четко выражать свою мысль и делать акцент. Постараюсь исправить ситуацию:

1. Верный вариант задать аргумент по умолчанию — это default parameters.
2. Если все таки нужно определить переменную по месту, то лучше не использовать «логический» оператор с проверкой наличия чего-либо.
3. Если в проверку еще добавляется валидация в несколько условий, то нормальный if statement еще более необходим. Хотелось бы верить, что это понятно (как было прокомментировано выше), но код с инициализацией переменной через кучу И и ИЛИ с проверками встречается.
UFO just landed and posted this here
Я пару раз видел изумление, когда показывал код тому же Java-разработчику. И да, считаю это хорошей практикой читать и понимать код не только JavaScript как для себя, так и для коллег. Мне эти операторы угодили, но в js они только называются «логическими» и часто понятны только js-разработчикам.
let timestamp = +new Date;
Но ведь есть у Date известный метод getTime, давайте использовать его:
let timestamp = (new Date()).getTime();

Вообще то, правильно будет
let timestamp = Date.now();
let str = ''+(expr);
let str = String(expr);
Результат — такой же, только понятен всем.

строго говоря, не такой же. Если expr — это объект, у которого есть метод valueOf, то первый способ переведет в строку то, что вернул метод valueOf, а второй — то, что вернул метод toString.
let d = new Date;
''+d; // Wed Dec 12 2018 14:47:27 GMT+0300
+d; // 1544615247741

Здесь будет строковое преобразование, так как один из операндов — явно строка. Но замечание очень дельное! с этим надо быть осторожным )

Date — это исключение из правил. прежде чем писать статьи о языке, неплохо бы в учебник заглянуть.
Да, вы правы. Спасибо, что поставили на место.
Но во многих случаях, язык позволяет избежать лишних удивлений, явно используя Function.prototype.bind или вовсе так:
setTimeout(() => this.n += 1, 1000);
Оставляя в стороне «явные» отличия в поведении обычных и стрелочных функций (от которых у Гвидо волосы на спине бы встали дыбом), this настолько не стыкуется с синтаксисом классов, что надо или выпилить bind (=сделать код обратно несовместимым), или добавить self.

Утрированный пример: допустим, есть графики D3 или highcharts, которые используют контекст в своих колбеках на всю катушку, и «старый» класс с классическим that = this, который строит конфигурацию для графика:

function Config () {
  let self = this;
  self.field = 'foo';
  
  this.getConfig = function() {
    return {
      tooltip: {
        formatter: function () {
          return 'Value of ' + self.field + ' for X=' + this.x + ' is ' + this.y;
        }
      }
    }
  }
}

Если всё это дело попытаться переписать с использованием «новых» классов, сразу возникают нестыковки:

class Config {
  constructor() {
    this.field = 'foo';
  }

  getConfig() {
    return {
      tooltip: {
        // Так потеряется значение this.field
        formatter: function () {
          return 'Value of ' + this.field + ' for X=' + this.x + ' is ' + this.y;
        },
        // Так станут недоступными this.x и this.y
        formatter: () => {return 'Value of ' + this.field + ' for X=' + this.x + ' is ' + this.y},
      }
    }
  }
}

Но как же хорошо, что в JS существует столько явных возможностей, среди которых есть даже IIFE. Так язык позволяет избежать лишних удивлений:

formatter: (self => function() {return 'Value of ' + self.field + ' for X=' + this.x + ' is ' + this.y})(this)

(Да, это потому, что либы писались ещё за царя Гороха. Но самое страшное, что так продолжают писать! Потому что пока в языке есть этот чёртов this, каждый будет вертеть им как хочет)

Оох… этот контекст в highcharts! Библиотеки — это действительно отдельная тема )


Я разделяю недовольство по поводу нечистой стрелочной функции с this в примере, но готов иногда пойти на это ради читаемости.


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


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

Так надо же добавить тот самый self = this первой строчкой в getConfig…

Это как раз та область, где явно видно силу Typescript. У TS хватает своих проблем (основная из которых — в том, что окружающий мир в основном не TS), но вот в области превращения неочевидных конструкций JS в очевидные — он прекрасен.

Хотя автор конечно безусловно прав в утверждении, что и на JS можно понятно писать.
А чем он поможет в контексте проблем описанных в статье. Все упомянутые конструкции (n || 5, +Date, ...) с тем же успехом можно использовать в TS.

Сделать поля приватными на уровне модуля на данный момент можно так:


const nameField = Symbol('name');

class Foo {
    constructor(name) {
        this[nameField] = name;
    }

    toString() {
        return `Hello, ${this[nameField]}`;
    }
}
Да, вариантов несколько. Попробуй только потом в этом разобраться…
const foo = new Foo('world');
Object.getOwnPropertySymbols(foo).map(sym => foo[sym]); //  [ 'world' ]
В Java тоже можно доступ к приватным полям через рефлексию получить, но это же не делает их публичными :)
Почему бы просто не использовать префикс _ в js или модификатор private в ts.
нужно наверное завести какой-то бестпрактикс по javascript, я вот сам с ним работаю очень мало и действительно приходится пробираться через дебри законов, нюансов этого языка. Но как правило многие вещи это непонятные оптимизации и правила написания кода, и увы с читабельностью они часто разнятся, что нарушает философию уменьшения сложности.
на мой субъективный взгляд конечно.
Есть Airbnb JavaScript Style Guide, не совсем best practices, но частично и их захватывает. Пожалуй, наиболее популярное соглашение в мире JS.
Использование оператора || для задания значений по умолчанию настолько распространено, что уже мало кем считается антипаттерном (при условии что нельзя заменить на параметры по умолчанию). Особенно велик соблазн использовать логические операторы в длинных цепочках.

Можно добавить в список как сделать не очевидно — повсеместную замену обычных функций на стрелочные.
UFO just landed and posted this here

Это очень полезная штука ) на него не было наезда: там про сохранение контекста this или про расширение свойств экземпляра класса в конструкторе.

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

соблюдают ли такую практику разрабочтики других языков

Такого сочетания платформы, поддерживающей один язык и ее популярности нет, пожалуй, нигде в других областях программирования.
Идея в том, что JavaScript позвляет это сделать без усилий в большинстве случаев. А на той, другой стороне как минимум две проблемы: 1) часто это строгие и не динамические языки и 2) на серверной стороне слишком много языков.

как правило всякие неявные фишки языка используют в исключительных ситуациях.


в javascript я часто встречаю


if (foo === void 0)

неужели это всегда необходимо? все же


if (typeof(foo) === 'undefined')

намного читабельнее и понятнее

Это тот прекрасный код, где объективно нужно по-разному обрабатывать undefined и null, или же просто обычный выпендрёж?
Потому что когда не надо — обычное "== null" таки куда понятнее.
не могу ответить, так как не я этот код писал. Только приходится иногда читать

Постараюсь ответить более развернуто.


Одно из правил в программировании гласит что нужно уменьшать сложность. Код должен читаться как книга, конечно есть вещи которые нужно изучить для понимания ЯП, с этим я не спорю.


как одни из примеров:


()();

да я понимаю зачем это нужно, но жестить всегда, потому как "так позволяет язык, так пишут во фреймворках...", без каких либо оснований нет(я про void 0 если что… и подобное...).


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


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


как-то так...

Самый читаемый и правильный вариант, который явно прописан в спецификации вот:
if (foo == null)

void 0 это со времен когда глобального свойства undefuned не было в стандарте и последнее могло резолвиться во что угодно.


window.undefined = 42 // happy debugging, suckers


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


Вариант с typeof может работать медленнее, т.к сравнивает строки посимвольно.

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

А нифига. undefined все такой же небезопасный. Ну разве что слегка менее небезопасный.

console.log(undefined === void 0); // true;

(function () {
  var undefined = 42;

  // a lot of code;

  console.log(undefined === void 0); // false;
})();

Это аффектает только свой код. Надо сильно захотеть и постараться, чтобы так написать.

Sign up to leave a comment.

Articles