company_banner

Я перехожу на JavaScript

Автор оригинала: Rob
  • Перевод
После того, как я 5 лет писал на Go, я решил, что мне пора двигаться дальше. Go хорошо послужил мне. Вероятно, это был лучший язык, которым я мог бы пользоваться столько времени, но теперь настал момент оставить Go.

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



Мне не хотелось бы писать материал о том, почему я перешёл с Go на JavaScript, перечисляя минусы Go. Я полагаю, что подобные материалы оторваны от жизни и приносят читателям очень мало пользы. Поэтому я написал материал о том, что мне нравится в JavaScript, о том, что подвигло меня на переход.

Система типов и работа с числами


В JavaScript очень долго не было целочисленного типа данных. В языке были только какие-то странные числа двойной точности, которые иногда сносно играли роль целых чисел. Теперь всё уже не так. Теперь в JavaScript есть и целые числа, и массивы целых чисел. Разные значения такого рода можно преобразовывать друг в друга. Всё это делает JS языком с самыми лучшими числовыми типами.

var sb = new ArrayBuffer(4)
var intArr = new Int32Array(sb)
intArr[0] = 1 << 16
// 65536
intArr[0]
// 65536

Это — именно то, чего я долго ждал: точные вычисления. Очень приятно знать, что выбранный тобой язык поддерживает такие возможности.

Но учитывайте то, что JavaScript на этом не останавливается, так как вышеозначенные возможности вполне обычны во многих других языках. Мы можем использовать Int32Array как Int16Array, и всё будет работать так, как ожидается, без выдачи исключений:

var sb = new ArrayBuffer(4)
var intArr = new Int32Array(sb)
intArr[0] = 1 << 16
// 65536
intArr[0]
// 65536
// Тут я использую тот же буфер
var shortArray = new Int16Array(sb)
shortArray[0]
// 0
shortArray[1]
// 1

Вы можете тут спросить: «Зависит ли результат от порядка следования байтов в системе, в которой выполняется код? А если так — почему я пользуюсь архитектурой, где сначала идёт старший байт?». А я на это отвечу: «Спасибо, что спросили. Хороший вопрос».

Теперь поговорим о ещё одной замечательной особенности этих целых чисел. А именно, переполнения, в отличие от любого другого языка, обрабатываются именно так, как этого можно ожидать:

var shortArr = new Int16Array(1)
var c = shortArr[0] = 1 << 15 // бонус: приятное множественное присваивание
c == shortArr[0]
// false
shortArr[0]
// -32768
c
// 32768

Полагаю, что к этому моменту я уже продемонстрировал мощь чисел в JavaScript, но, чтобы окончательно прояснить мою мысль, приведу ещё примеры.

Оператор получения остатка от деления (%) можно применять при работе с числами с плавающей точкой. И ведёт себя этот оператор именно так, как он и должен работать:

3.14 % 5
// 3.14
13.14 % 5
// 3.1400000000000006

Кроме того, массивы целых чисел легко сортировать, что называется, «на месте». При этом программисту даже не надо видеть стандартного кода, выполняющего сортировку. Это выгодно отличает JavaScript от многих языков со строгой статической типизацией:

[-2, -7, 0.0000001, 0.0000000006, 6, 10].sort()
// [-2, -7, 10, 1e-7, 6, 6e-10]
А синтаксис языка всегда чёток и интуитивно понятен:
(1,2,3,4,5,6) === (2,4,6)
true

Теперь давайте представим, что у нас есть строка, представляющая число, введённое пользователем. Это число нужно увеличить на единицу. Во многих языках для того чтобы это сделать, приходится тратить время, писать много муторного кода, выполняющего преобразование типов. В JavaScript же это делается так, что код получается изящным и читабельным:

var a = "41"
a += 1
// 411, неправильно, несбалансированно, странно.
var b = "41"
b -=- 1
// 42, а вот использование этой симметричной конструкции приводит к просто замечательному результату

Тут я обнаружил лишь одну вещь, которую трудно удержать в голове (но мне нужно будет к этому привыкнуть). Дело в том, что при работе с датами нужно учитывать то, что нумерация месяцев начинается с 0, а нумерация всего остального — с 1. Ничего другого, такого, что стоит запомнить, я не нашёл.

Подробности о типах


Как оказалось, самая красота JavaScript — это, бесспорно, его система типов. Поэтому я потрачу тут ещё немного времени на рассказ о том, что мне удалось выяснить в ходе экспериментов с языком или благодаря знатокам JavaScript из Twitter.

Благодаря новой возможности языка, представленной оператором ??, теперь можно писать такой код:

~~!![[]]||__``&&$$++<<((""??''))**00==ಠಠ--//\\
// 1

Нужна строка banana? Внимательно относитесь к расстановке пробелов:

('b'+'a'++'a'+'a').toLowerCase()
// Uncaught SyntaxError: Invalid left-hand side expression in postfix operation
('b' + 'a' + + 'a' + 'a').toLowerCase()
// "banana"

Нравится писать «однострочники»? JavaScript вам в этом поможет:

// Лучший код - это краткий код
input ?? obj?.key ? 'yes' : 'no'

А вот — моё любимое. Я вообще без ума от регулярных выражений. А в JavaScript есть много такого, что никому не даст скучать:

var re = /a/g
re.test('ab')
// true
re.test('ab')
// false
re.test('ab')
// true

Операторы в JS иногда не отличаются коммутативностью. Помните об этом:

{property: "value"} && {property: "value"}
// {property: "value"}
Date() && Date()
// "Wed Apr 1 2020 00:01:00 GMT+0100 (Central European Standard Time)"
Date() && {property: "value"}
// {property: "value"}
{property: "value"} && Date()
// Uncaught SyntaxError: Unexpected token '&&'

Типы могут меняться в самых неожиданных местах, что помогает программисту не заснуть во время долгих рабочих ночей:

typeof([1][0])
// number
for(let i in [1]){console.log(typeof(i))}
// string

Даже значения могут меняться только от того, что мы к ним обращаемся. Это, опять же, помогает продуктивно трудиться:

const x={
  i: 1,
  toString: function(){
    return this.i++;
  }
}

if(x==1 && x==2 && x==3){
  document.write("This will be printed!")
}

Я могу тут приводить и другие примеры, демонстрирующие лёгкость чтения и понимания JavaScript-кода, но я закончу этот раздел следующим примером, который предусматривает работу с DOM:

document.all
// HTMLAllCollection(359) [html.inited, head, …]
document.all == true
// false
document.all == false
// false

Углубляемся в кроличью нору


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

Я недавно начал работать с одним человеком. Он прислал мне этот прекрасный фрагмент JS-кода:

(function({substr}){return substr.call})("")
// function call()
var x = (function({substr}){return substr.call})("")
// undefined
x.name
// "call"
x + ""
// "function call() {
//     [native code]
// }"
typeof x
// "function"
x("this is a string")
// TypeError: Function.prototype.call called on incompatible undefined

На этом он не остановился и прислал мне ещё вот это:

(function(){return this}.call(1)) == (function(){return this}.call(1))
// false

Последняя капля: конкурентность


Когда я дошёл до этого, я, в общем-то, уже был уверен в том, что мне необходимо перейти на JavaScript.

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

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

Так как я иду в JavaScript из Go — мне было очень легко понять причину, по которой этот код три раза выводит 3:

for (i = 1; i <= 2; ++i) {
  setTimeout(function(){
    console.log(i);
  }, 0);
}

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

console.log(0)
setTimeout(_ => console.log(1), 0)
requestAnimationFrame(_ => console.log(2))
Promise.resolve().then(_ => console.log(3))
console.log(4)
// 0
// 4
// 3
// 2
// 1

Всё становится гораздо яснее в том случае, если учесть, что тут мы вызываем две синхронных функции, планируем «макро-задачу», запускаем «микро-задачу» и сообщаем браузеру о том, что хотим произвести анимацию.

Полагаю, зная это, ничего не стоит понять механизмы конкурентного исполнения кода в JavaScript. А как насчёт параллелизма?

Вот код, которым я собираюсь тут пользоваться. Благодарю за него codediodeio.

// Этот код предназначен для логирования сведений о прошедшем времени:
const start = Date.now();
function log(v){console.log(`${v} \n Elapsed: ${Date.now() - start}ms`);}
// Тут запускаем тяжёлую задачу, блокирующую текущий поток
function doWork(){
  for(let i = 0; i < 1000000000; i++); // обратите внимание на точку с запятой
  return 'work done';
}
log('before work');
doWork();
log('after work');
// before work
//  Elapsed: 0ms
// after work
//  Elapsed: 624ms

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

Давайте теперь попробуем воспользоваться промисом:

function doWork(){
  return new Promise((resolve, reject) => {
    for(let i = 0; i < 1000000000; i++);
    resolve('work done');
  })
}
log('before work');
doWork().then(log);
log('after work');
// before work
//  Elapsed: 0ms
// after work
//  Elapsed: 637ms
// work done
//  Elapsed: 637ms

Здесь мы сталкиваемся с той же проблемой. Кто-то может тут возразить, сказав, что проблема заключается в том, что код всё ещё выполняется как «макро-задача», или в том, что данная задача блокирует всё тот же поток. Поэтому давайте переработаем код так, чтобы весь тяжёлый цикл выполнялся бы в виде «микро-задачи»:

function doWork(){
  // Добавление `resolve` приводит к выполнению этого в задаче другого вида
  return Promise.resolve().then(v =>  {
    for(let i = 0; i < 1000000000; i++);
    return 'work done';
  })
}
log('before work');
doWork().then(log);
requestAnimationFrame(()=>{log('time to next frame')});
log('after work');
// before work
//  Elapsed: 0ms
// after work
//  Elapsed: 1ms
// work done
//  Elapsed: 631ms
// time to next frame
//  Elapsed: 630ms

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

Это — кое-что такое, что меня прямо-таки поразило: если у нас имеются тяжёлые вычисления, которые нужно выполнить в JavaScript, то они заблокируют главный поток и замедлят интерфейс приложения. Можно найти множество видеоматериалов и учебных руководств о том, как пользоваться конструкцией async/await или промисами для решения этой задачи, но все они будут не в тему. Эти примитивы дают возможность конкурентного, а не параллельного выполнения кода. Единственный вид промисов, которые я смог запустить в настоящем параллельном режиме, это встроенные промисы браузера, вроде fetch. А то, что описано в коде, никогда не будет выполняться параллельно.

Как же выполнять код в параллельном режиме? Надо создать веб-воркер — нечто вроде потока из других языков. Это — отличная новая возможность, которая вводит в JavaScript так необходимое в этом языке состояние гонок параллельное выполнение кода.

Пользоваться веб-воркерами очень просто: надо поместить код, который планируется запускать параллельно, в отдельный файл, а потом — создать экземпляр нового воркера, с которым можно обмениваться данными, используя postMessage. Это даёт нам настоящий параллелизм. Всё, кроме того, хорошо документировано на MDN. Всё это выглядит несколько громоздко? Да, это так, но это — возможность, которая есть в распоряжении программиста. Кроме того, создаются библиотеки, которые облегчают использование этого примитива. В конце концов — что плохого в ещё одной дополнительной зависимости?

Так как в JavaScript теперь есть эта замечательная возможность, это значит, что в языке должны быть предусмотрены механизмы, представленные примитивами для оркестрации или синхронизации асинхронных задач. Верно? Верно. Язык даёт нам postMessage и MessageChannel (это похоже на chan), что очень хорошо. Если пользоваться только этими механизмами, в вашем коде никогда не случится «гонка потоков», в этом коде будет очень легко ориентироваться, о нём будет легко рассуждать. Чудно.

Однако если вам нужно что-то более производительное, что-то такое, чему не нужно вызывать события и ожидать планирования коллбэков, если вам нужно, чтобы что-то работало по-настоящему быстро, тогда к вашим услугам SharedArrayBuffer. Речь идёт о фрагментах памяти, которые можно совместно использовать в разных потоках, выполняя над данными из этих фрагментов атомарные операции. Нет ни Mutex, ни select, ни WaitGroup. Если вам нужны эти примитивы — вам понадобится самим их написать (как я). Как вам это понравится? Этот API даже возвращает значения, которые невозможно использовать правильно!

Да, в конце концов, кому понравится лёгкая работа‽ (Это — вопроцательный знак, который пока ещё не относится к числу операторов JavaScript).

Итоги


JavaScript — это зрелый язык, который даёт мне всё, что мне нужно: конкурентность, параллелизм, строго типизированные переменные. Более того, JS оживляет программирование и делает работу веселей, выдавая всякие непредсказуемые и таинственные вещи без каких-либо дополнительных библиотек, что называется, «из коробки». В результате тому, кто пишет на JavaScript, не будет скучно при отладке. Что тут может не понравиться?

Уважаемые читатели! Планируете ли вы переходить на JavaScript с того языка, на котором пишете сейчас?

RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Комментарии 409

    +19

    Да, хорошая саркастическая статья. Оценил юмор

      +6

      Оригинал опубликован 1 апреля. Статья соответствует )

        +3

        Немного толстовато, но в целом норм. Про пару моментом не знал, например.

          +13
          Не ловкая ситуация, когда читаешь и принимаешь за чистую монету.
            +15
            Юмор уровня школьников. У нас помнится класс поделился на сишников и паскалистов, и чуть ли не до драк доходило. Там подобные писюльки были в ходу.
              +2
              … и так будет вечно…
            +8
            Странно, вроде не пятница, а «смИшная статья про JS» есть.
              +5

              Ну раз завтра выходной, значит сегодня — пятница.

              +2

              Надо было до завтра дотянуть. Первоапрельская статья, первомайская… Какая разница?! )

                +3
                День победы! Javascript над Golang
                  0

                  С 9м мая не путаете?

                +27

                Хорошо что это всего лишь юмор и человек останется гофером. Нам тут клоунов не нужно...

                  –33

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


                  Вот не в моих правилах вообще опускаться на личности и так бомбить, но откуда столько людей с синдромом графомании?


                  Как им времени своего не жалко только эти статейки калякать.


                  // я понимаю, что это перевод

                    +27

                    Вопрос не "почему" оно так работает, а "какого хрена".

                      –10

                      Ну если человек искренне считает, что вот эта конструкция в чем-то неправильна:


                      (1,2,3,4,5,6) === (2,4,6)

                      То надо не статьи писать бежать, а читать доки про операторы скобок и запятой. И то, что они возвращают последнее выражение.


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


                      Я вообще не понимаю, какого рожна в программировании должно быть хотя бы что-то интуитивно понятно? Есть строгие правила — либо ты их знаешь, либо ты не программист. Додумывать своей мудрой головой ничего не надо.

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

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

                        В приличных спецификациях на каждое решение есть убедительная аргументация: это решает проблему неоднозначности, это необходимо для оптимизации, это улучшает продуктивность. В джиесе такого нет. Решения, принятые спонтанно по зову сердца, просто оказались с годами высечены в камне. Динамическая типизация? Ну, двадцать пять лет назад это было модно, особенно для крошечного скриптового языка, каким планировался джиес. Убогое стандартное API даты таково, потому что его взяли под копирку из Java. Даже название (!) украдено у Java, чтобы словить хайп того времени.
                          –11
                          Ох уж этот юношеский максимализм… Объем памяти в голове сильно ограничен. Некоторые языки позволяют запомнить несколько универсальных правил взаимодействия сущностей языка, и на основе них дальше можно писать любые программы. А другие языки кишат исключениями из этих правил, за которыми постоянно приходится лезть в спеку и тратить на это время.

                          Возможно вам близок такой подход, мне нет. У технаря должно быть строгое понимание того, что и как он делает. Математика, инжниринг и программирование это не худ. литеретура, где достаточно вдохновится одной книгой и написать похоже другую. Если вам лень или если конкретно у вас ограничен объем того, что можете запомнить — ну это тогда не мои проблемы и не проблемы остальных людей, коих не мало и кто программирует на JS.


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


                          Я могу согласится с некоторыми изьянами языка — например тот же дейт, typeof null, оператор ==. Но это часто совсем не то, за что язык хейтят. Более того это довольно редкие случаи или случаи выбора — никто тебя не обязывает обходит неочевидности ==, просто юзай === и все. Никаких проблем. Но вот такие вот статьи единственное, что оставляют — так ощущение того, что автор просто хотел выплеснуть свою желчь.

                            +30
                            В JS иначе — много флексибилити, много специфик. Трудно, не удобно, не нравится — берешь и программируешь на другом, на том — что нравится. Но какого поливать этот язык грязью я не знаю.

                            Ну так а что хорошего во флексибилити? Для реализации чего-либо в языке необходимо и достаточно иметь всего один простой способ. Если в языке есть много способов, причем ряд из них приводят к трудно обнаруживаемым ошибкам, это плохой язык. Кидайте в меня любыми какашками, но JS, это объективно очень плохой язык, которому круто повезло в своё время оказаться единственным скриптовым языком для очень перспективной платформы.
                              +1
                              JS, это объективно очень плохой язык

                              Любая объективность должна иметь объективные метрики. В студию, пожалуйста.

                                0
                                градус ненависти сообщества подойдет?) или как вариант — количество первоапрельских статей и выступлений на конфах с высмеиванием некоторых решений, принятых в языке) в конце концов ЯП существует для промышленных программистов, в этом смысле количество жалоб на его кривизну — вполне объективная метрика
                                  0

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

                                    0
                                    Увы в промышленном программировании (и не только программировании) нельзя просто так взять и отказаться от чего-то, даже когда ситуация круто поменялась и появились лучшие альтернативы. Обратная совместимость очень важна. Есть конечно смельчаки «выкинуть всё старое и переписать с нуля», но они как правило плохо кончают)
                              +1
                              В JS иначе — много флексибилити, много специфик.

                              много специфик не из за того что динамическая типизация и много флексибилити, а просто так случайно получилось, язык «разработали» в спешке, так и оставили ради обратной совместимости. В том же пайтоне при динамической но более строгой типизации, WAT моментов куда меньше.
                                +6
                                У технаря должно быть строгое понимание того, что и как он делает.
                                Полностью согласен. Проблема в том, что джиес заставляет не понимать, а зазубривать. Инженерный подход же предполагает поиск оптимального, элегантного решения, а не закидывание задачи костылями и копипастой.
                                Если вам лень или если конкретно у вас ограничен объем того, что можете запомнить — ну это тогда не мои проблемы и не проблемы остальных людей, коих не мало и кто программирует на JS.
                                Если вы гордитесь тем, как мастерски умеете есть кактус и почти не колетесь — ваше право. До относительно недавнего времени это было неизбежно, т.к. джиес занимал уникальное положение и не имел альтернатив. Но сейчас им достаточно наелись, что появились Typescript, WebAssembly, ReasonML и наконец-то ситуация стала улучшаться.
                                автор просто хотел выплеснуть свою желчь
                                С этим тоже согласен. Но «желчь» подана тонко и вполне оправдана.
                                0
                                Я понимаю, о чем вы говорите. Но я искренне не понимаю, зачем писать такой код в JS. Может, человек ожидал наличие кортежей (сарказм), может еще что-то, конечно, но все же. Ни в одном языке не нужно использовать конструкции, с которыми ты не знаком. И о типах, да, я согласен, что здешняя система типов местами отвратительна, но ее действительно нужно знать. В C++ типизация тоже не на высоте, но никто в здравом уме не будет отрицать, что этот язык очень важен во многих сферах, и нужно знать о некоторых нюансах его реализации, чтобы его использовать.
                                Но как рофл — статья действительно хороша. xd
                                P.S. Как поживает ваш компилятор LENS? Будут ли новые статьи на тему компиляции?
                                  +1
                                  C++ никто и не считает образцом изящного дизайна ЯП, скорее наоборот.
                                    0

                                    Думаю, автор намекает на то, что во многих случаях JS заимствует элементы из других языков, но выворачивает их семантику наизнанку так, что они становятся непредсказуемыми и опасными. Это особенно актуально, если пользуешься не только JS, а несколькими языками параллельно.


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

                                      0
                                      Я вас понял. Немного жаль, конечно. Во всяком случае, всегда буду рад снова увидеть ваши оригинальные статьи на любую другую тему. Удачи с вашим проектом!
                                  +16
                                  Я вообще не понимаю, какого рожна в программировании должно быть хотя бы что-то интуитивно понятно? Есть строгие правила — либо ты их знаешь, либо ты не программист. Додумывать своей мудрой головой ничего не надо.

                                  Сами дойдёте до понимания, когда вам понадобится взять на поддержку/доработку проект от других разработчиков на не очень хорошо знакомой вам платформе. Если код интуитивно понятный, вы садитесь, и реализуете функционал. Если код состоит из хрен-пойми-каких конструкций, вы садитесь, и начинаете учить строгие правила (или не строгие, это уж смотря какая платформа). И знаете, второе вы будете искренне воспринимать как тупейшее занятие, когда у вас юность позади, а эта платформа — тридесятая в череде уже изученных. И будете справедливо считать разработчиков, применяющие не интуитивные конструкции там, где можно было сделать то же самое читабельным и понятным, феерическими дятлами.
                                    –15

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


                                    Но не язык. Конечно проще обвинить JS, а не что-то другое. Но от этого он хуже не станет. Хейтерс гонна хейт )

                                      +17
                                      Понимаю ваше разочарование в такой ситуации — но, тем не менее, язык не виноват.

                                      Нет, вообще не понимаете. Не виноват шеф, просто потому, что инструмент — вещь вторичная, главное как раз решить задачу, и если в команде нет свободного профильного специалиста, то взять опытного непрофильного — это самое правильное решение. Не виноват клиент, потому что быстрое решение задач — это их бизнес, их доходы, и это их естественное и здравое желание. Не виноват я, потому что я уже много-много лет в этом бизнесе, и прекрасно разбираюсь, что в нём стоящее, а что дутая фигня.
                                      Виноватых всего двое:
                                      — предыдущие разработчики, которые не были профессионалами, и вместо решения задач игрались хитрожопозакрученными языковыми конструкциями.
                                      — сам язык, который не просто допускает возможность писать хреновый код (да на любом можно говнокодить при желании), а в принципе требует усилий, чтобы этот самый код был адекватным.
                                        +1

                                        Те языки, которые вообще не требуют усилий для написания адекватного кода очень ограничены. Вы сами сказали, что говнокод можно писать на чем угодно, но почему вы уверены, что для написания нормального кода на js (es6, TS) требуется усилий больше, чем на любом другом? К тому же везде используются линтеры, которые предотвращают написание плохого кода. Я не знаю что у вас происходит на работе, но такое чувство (по вашим комментариям), что компании вообще пофигу кого нанимать, "лишь бы работоло", а как работает — наплевать.

                                          0
                                          а в принципе требует усилий, чтобы этот самый код был адекватным.

                                          Что разумеется неверно. Или всегда писать === вместо == для Вас усилие? Ну да, печатать дольше. И да, это один из минусов JS, т. к. == можно было бы выкинуть, а === сделать на 1 символ короче. Но не такой, что всё, катастрофа. Я написал ниже хороший комментарий с пояснением всех пунктов из этой статьи.

                                      +4

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

                                        +2

                                        Давайте засунем это в сишарп и посмотрим что он скажет:


                                        var x = (1,2,3,4,5,6) == (2,4,6);
                                        
                                        CS8384 Tuple types used as operands of an == or != operator must have matching cardinalities. But this operator has tuple types of cardinality 6 on the left and 3 on the right.

                                        Как по мне, это куда более логичный результат чем false, и особенно true.

                                          –3
                                          или в питон

                                          >>> (1,2,3,4,5,6) == (2,4,6)
                                          False
                                          >>> (2,4,6) == (2,4,6)
                                          True
                                          
                                            –1
                                            Или в Delphi:
                                              var x := [1,2,3,4,5,6] = [2,4,6];
                                              // FALSE
                                              var y := [2,4,6] = [2,4,6];
                                              // TRUE
                                            

                                            Но если
                                              var x1 := [1,2,3,4,5,6];
                                              var x2 := [2,4,6];
                                              var x3 := x1 = x2; // E2008 Incompatible types
                                            
                                              +8
                                              В JavaScript для квадратных скобок тоже всё «нормально»:
                                              [1,2,3,4,5,6] == [2,4,6]
                                              // false
                                              [2,4,6] == [2,4,6]
                                              // false
                                              Упс
                                                +4

                                                Что значит упс? У Вас два разных объекта, с чего вдруг они должны быть равны?


                                                Если бы это было не так, у меня были бы претензии к языку. Это было бы достаточно странно, что равны разные объекты.


                                                А оператор "," в JS — очень крутая вещь, не раз меня выручал. То, что Вы не знаете юз-кейсы, не значит, что их нет.

                                                  0
                                                  У вас случайно нет претензий к JS, что "foo"=="foo"? Объекты-то разные.
                                                    0
                                                    Объекты-то разные.

                                                    Эм, кто Вам сказал? В данном случае это скорее всего будет один объект внутри движка (по крайней мере в V8).


                                                    В любом случае, со строками у Вас нет возможности проверить, одинаковый ли это объект или разный. А значит, можно сделать, чтобы === сверял содержимое строки, а не ссылку.


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


                                                    В итоге "aaa" и "aaa" — это абсолютно одинаковые объекты с точки зрения программиста, поэтому === говорит, что они одинаковые. А вот {a: 1} и {a: 1} — это два разных объекта с точки зрения программиста.

                                                      +1
                                                      Эм, кто Вам сказал? В данном случае это скорее всего будет один объект внутри движка (по крайней мере в V8).

                                                      Не вопрос, давайте сделаем так


                                                      "foo1" === ("foo" + 1)

                                                      В итоге "aaa" и "aaa" — это абсолютно одинаковые объекты с точки зрения программиста, поэтому === говорит, что они одинаковые. А вот {a: 1} и {a: 1} — это два разных объекта с точки зрения программиста.

                                                      А этого вы не можете знать. Например, с моей точки зрения вполне вероятно что {id: 10} и {id: 10} — это один объект.

                                                        +1
                                                        А этого вы не можете знать. Например, с моей точки зрения вполне вероятно что {id: 10} и {id: 10} — это один объект.

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


                                                        Это как с близнецами — если они выглядят одинаково (например, на фото), это не значит, что это один и тот же человек. Чтобы это проверить, нужно использовать оператор ===.


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


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


                                                        Причём если бы это было не так, то и со строками в JS было бы тоже самое, что с обычными объектами: "aaa" !== "aaa".

                                                          0

                                                          У меня по бизнесу часто бывает нужно трактовать такие объекты как одинаковые (базовый пример — когда я иду делать апдейт в базу а у меня есть два объекта с одинаковой айдишкой и разным value). Так что


                                                          В итоге "aaa" и "aaa" — это абсолютно одинаковые объекты с точки зрения программиста, поэтому === говорит, что они одинаковые. А вот {a: 1} и {a: 1} — это два разных объекта с точки зрения программиста.

                                                          Вот я — программист, и у меня есть необходимость как различать "aaa" и "aaa", так и считать одинаковыми {a: 1} и {a: 1}

                                                            0
                                                            У меня по бизнесу часто бывает нужно трактовать такие объекты как одинаковые

                                                            Для этого есть deepEqual.


                                                            Строка (также как и обычные числа) считаются простым неизменяемым значением, поэтому сравниваются по содержимому, а объект сложным изменяемым, поэтому сравнивается по ссылке (причины объяснены выше — всё дело в изменяемости).


                                                            Если нужно сравнить по содержимому, юзают deepEqual или equal, а не ===.


                                                            PS. Внутри движка на самом деле строка может изменяться для оптимизации скорости и памяти, но для программиста это остаётся прозрачным. Это внутренняя реализация, на результат вычислений она не влияет.

                                                              0
                                                              часто бывает нужно трактовать такие объекты как одинаковые

                                                              Дайте универсальное определение "одинаковости"

                                                                +4

                                                                x = y, если для любого предиката P: P(x) <=> P(y).


                                                                Дальше можно вводить понятие отношения эквивалентности и рассматривать фактор-множество/группу/кольцо/вотэва по этому отношению (с, вероятно, усиленной структурой).

                                                                  –2

                                                                  Спасибо, наглядно показывает, что в JS всё сделано максимально правильно. В принципе я говорил то же самое: JS считает одинаковым то, что всегда ведёт себя как одинаковая сущность.


                                                                  2 разных объекта совсем не ведут себя как одинаковые сущности. Мы начинаем изменять объект, и если мы считали их одинаковыми, будет фейл. А, к примеру, строки и числа ведут себя как одинаковые сущности — что бы программист не делал, невозможно написать такой код, чтобы можно было отличить их, либо чтобы их различие/одинаковость привела к ошибкам в коде. Причина этого проста: числа и строки нельзя изменять. И да, это касается не только строк, но и чисел: в памяти две "5" (пятёрки) не значат, что это два одинаковых числа, в реальности скорее всего они будут продублированы, т. е. это будут две разные "5". Но программист никак не может это узнать и написать код, который бы привёл к ошибке из-за считания их одинаковыми. Всё потому, что "5" никогда не изменяется, а изменяется только значение переменной. Сама "5" так и остаётся "5" навечно.


                                                                  Как итог: числа и строки считаются одинаковыми сущностями, объекты нет.


                                                                  Более того: если в каких-то языках сделано не так, у меня будут очень серьёзные претензии к этим языкам.

                                                                    0

                                                                    Но ведь...


                                                                    > let P = (x) => x === 5;
                                                                    undefined
                                                                    > P(5)
                                                                    true
                                                                    > P("5")
                                                                    false
                                                                      0

                                                                      Не очень понял, что тут не так. P выдало разные результаты, и при этом в JS 5 !== "5". Всё работает как надо.

                                                                        0

                                                                        Но вы же сами писали:


                                                                        числа и строки считаются одинаковыми сущностями, объекты нет.

                                                                        А они не одинаковые.

                                                                          +1

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

                                                                            0

                                                                            Разумеется, имелось ввиду, что, к примеру, число 5 и 5 — это в JS одинаковые сущности, как и строки "5" и "5". Выполняются равенства (5 === 5) и ("5" === "5") (т. е. они одинаковые между собой).


                                                                            Конечно есть фишки с даблами типа 0.1 + 0.2 !== 0.3, но это потому что сам процессор при сложении 0.1 и 0.2 получает другое число (не 0.3) из-за потерь точности. Это во всех языках так.


                                                                            Это достаточно логично, что, к примеру, 5 и 5 равны между собой, даже несмотря на то, что в памяти это совсем разные пятёрки. Почему так и должно быть, я объяснил выше. JS максимально логичен здесь. А вот другие языки не всегда, как я догадываюсь. Если строки могут изменяться, то ок, тогда правильно (но тогда бы неплохо ввести неизменяемые строки). Но если не изменяются, и их нельзя сравнить с помощью ===, с языком явно что-то не так. Это тоже самое, если бы 5 было бы не равно 5, а для сравнения нужно было бы писать 5.isEqual(5). Смысла в этом ноль, т. к. не существует случая, когда сравнение 5 === 5 подвело бы программиста (почему — написал выше). Аналогично и со строками.

                                                                        0

                                                                        С точки зрения JS (да и некоторых других языков) == проверяет именно на одинаковость, на одинаоковое значение, а === на идентичность, на тожесамость.


                                                                        Для скалярных типов данных одинаковость и идентичность синонимы по сути, а вот для ссылочных — нет. Два объекта могут быть одинаковыми, но === проверяет для них не одинаковость, а идентичность один и тот же объект справа и слева или разные, пускай и одинаковые.

                                                        +1
                                                        так себе пример, поскольку в js массивы это объекты, а объекты так сравнивать нельзя.
                                                          0
                                                          Потому что так решили разработчики получилось. Обьектно-ориентированный язык — а обьекты сравнивать надо каким-то хитрым способом…
                                                            0
                                                            1. Он не объектно-ориентирован, а прототипно-ориентирован. Всё ООП в js — то натяжка совы на глобус (ради удобства программистов ООЯП).

                                                            2. Он не единственный язык, в котором нельзя вот так прямо сравнивать 2 объекта. И это логично, поскольку надо различать сравнение объекта с самим собой и с таким же, но другим объектом.
                                                            При простом сравнении идёт сравнение ссылок на объект в памяти.
                                                              0
                                                              Он не объектно-ориентирован, а прототипно-ориентирован.

                                                              Как минимум, прототипно-ориентирован — это и подразумевает ООП.


                                                              А во-вторых, как его там ориентировали не знаю, но язык имеет очень красивое ООП.

                                                        0
                                                        В современных Дельфях стало можно объявлять переменные, не указывая тип?
                                                        +14

                                                        Сущности слегка разные.
                                                        В js это перечисление, берущее последние значения, в шарпе-тюльпы, в питоне и делфи — хз, смахивает на сравнение массивов.


                                                        А вообще в js количество ВТФ после определённого предела сильно падает. Из последнего, что вызывало втф — работа с датами, точность при делении и, цук, неочевидное наличие this в static методах.


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

                                                          +14
                                                          Когда после js берешь python, там тоже хватает wtf моментов и ощущение странного, кривоватого дизайна тебе долго не покидает.
                                                          Думаешь, ну емае, в js было все попроще и логичнее. :)
                                                            0
                                                            Было бы интересно увидеть примеры.
                                                              0
                                                              Ну например, отсутствие switch огорчает тех, кто к нему прикипел душой.
                                                                0
                                                                Неявный return например. Не очень понятные импорты. Странная работа с файлами, похожая на with из js, отсуствие switch, да. Неуклюжие пляски с virtualenv.

                                                                Нельзя сказать что эти мелочи прям ужасные, но таки кажутся раздражающей архаикой. В процессе работы все это сходит на нет, привыкаешь ко всему, тем не менее не могу сказать, что python прям стройнее и удобнее js.
                                                              0
                                                              В примере Delphi в 1-м случае — множества целых чисел, а во 2-м — векторы целых чисел (внезапно).
                                                                +2
                                                                в python, как и в шарпе — кортежи
                                                                +12

                                                                Не хочу влазить в холивар, но в js tuples вообще нет и конструкция все таки достаточно однозначная — скобки работают как группировка параметров и их можно опустить, а значит это эквивалентно
                                                                var x = 1,2,3,4,5,6 == 2,4,6;


                                                                Зная что означает операнд «,», никаких вопросов не должно быть. Разве что операнд странный, но он не только в js есть, а в том же С. И если там парсер съест скобки (почему бы и нет, это опять же как группировка), результат будет тем же. Да даже в C# до появления tuple результат такой же. Странно ожидать поведения как в каком-нибудь го от конструкции, которой вообще нет в ja.

                                                                  +1

                                                                  PS, не совсем эквивалентная, потому что он сравнивает результаты раскрытия скобок, и будет 6 в каждой из них. В моём «эквиваленте» он сперва сравнит 6 и 2, а потом отбросит этот (как и остальные результаты), и вернёт 6 как результат всей конструкции, но это допущение ради наглядности

                                                                    +2
                                                                    На самом деле не эквивалентно, потому что в декларации var запятая разделяет объявляемые переменные, и в данном случае будет ошибка парсинга:
                                                                    var x = 1,2,3,4,5,6 == 2,4,6;
                                                                    // Uncaught SyntaxError: Unexpected number
                                                                    +4

                                                                    Потому что в C# это Tuple, в Питон это массив, а в JS это comma-оператор. Очевидно ведь.


                                                                    Вот как вы звучите:


                                                                    5 % 3 = 2. А!!! ЧТО ЗА БРЕД 3 — это не 2 % от 5.

                                                                    Это всё семантика языка. Вы стараетесь его интерпретировать как-то по своему, ваши ожидания не оправдываются. Это проблема ваших ожиданий, а не языка.


                                                                    Ну вот к примеру рубист придёт и напишет:


                                                                    function f(x,y) {
                                                                        x*x + y*y;
                                                                    }

                                                                    А потом будет кричать: "ааа, джаваскрипт плохой, в руби оно вернуло бы значение". Ну глупо ведь.


                                                                    В Хаскеле, насколько я знаю, если вызвать функцию, которая ожидает два аргумента с одним аргументом, то вернётся карированная функция, которая ожидает один аргумент. Всё, теперь все языки, в которых не так — сломаны? Или, может сломан Хаскель, что он не падает с исключением как шарпы?


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

                                                                      0
                                                                      Потому что в C# это Tuple, в Питон это массив, а в JS это comma-оператор. Очевидно ведь.

                                                                      В Python это не массив, а тоже tuple.
                                                                      В Python вообще не используют термин «массив»: вместо массивов там списки.

                                                                      Это проблема ваших ожиданий, а не языка.

                                                                      Просто какие-то языки разрабатываются по «принципу наименьшего удивления», а какие-то — по принципу «потому что я так решил».
                                                                        +2
                                                                        В Python это не массив, а тоже tuple.
                                                                        В Python вообще не используют термин «массив»: вместо массивов там списки.

                                                                        "Отвратительный и непонятный язык! Как таким вообще можно пользоваться? Нужно срочно писать ироничную статью!

                                                                          –2

                                                                          Я не знаю питон, но судя по комментариям здесь, питон — жуткая хрень, ибо имеет овердофига серьёзных недостатков. При этом JS практически не имеет серьёзных недостатков (подробнее см. здесь), но имеет мелкие недостатки, которые практически не мешают работе. Также JS имеет огромную кучу настоящих достоинств, есть ли столько достоинств в Питоне, не знаю.

                                                                          –1
                                                                          а какие-то по принципу «потому что я так решил»

                                                                          Явно не в данном случае, т. к. использовать запятую как оператор — это очень логично, т. е. языки C, C++, Javascript, Perl достаточно логичны здесь.


                                                                          какие-то разрабатываются по «принципу наименьшего удивления»

                                                                          Ну вообще синтаксис JS во многом взят из C и C++, и как раз наименьшее удивление, думаю, будет, когда запятая будет работать как оператор. Т. е. это не только логично, но и более привычно.


                                                                          Конечно можно привести аргументы и за другое поведение запятой, но это уже на вкус и цвет… Хороший аргумент — возможность быстрого сравнения кортежей сразу из нескольких значений. Но теоретически в будущем может появиться что-то типа такого: {{5, 6, 7}} === {{6, 7, 8}}

                                                                            +1

                                                                            С, C++ и особенно Perl уж никак нельзя считать образцами грамотного синтаксиса.

                                                                          +3
                                                                          Это всё семантика языка. Вы стараетесь его интерпретировать как-то по своему, ваши ожидания не оправдываются. Это проблема ваших ожиданий, а не языка.

                                                                          Нет, если язык не оправдывает ожиданий, это может быть и проблема языка. А то так-то и с С++ нет проблем, просто у вас неоправданные ожидания что x + 1 при переполнении может быть меньше чем x.


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


                                                                          а в JS это comma-оператор. Очевидно ведь

                                                                          Не очевидно

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

                                                                            Какие языки в 1995-м году записывали так таплы?

                                                                              0
                                                                              Python?
                                                                                +1

                                                                                То JS обязательно должен был сделать этот кусок так же как в одном другом языке, который на тот момент был малопопулярен. Правильно?

                                                                                  +2
                                                                                  Не должен был.
                                                                                  Но неудивительно, что в 2020 году языки, тянущие «тяжёлое наследие проклятых 90-х», выглядят менее привлекательно, чем созданные позднее и научившиеся на чужих ошибках.
                                                                                0

                                                                                ML и его потомки, Haskell.
                                                                                Другой вопрос – какие языки, кроме C и C++, записывают так «вернуть правый операнд»?

                                                                        –3
                                                                        классическое «не баг, а фича». Наличие документации к кривому решению не придает ему элегантности
                                                                      –12

                                                                      Посмотрел, что это вроде бы как первоапрельская шутка. Ах шутка. Шутка же, ну — че ты?


                                                                      Так вот, есть одно видео — вроде тоже "шутка" — https://www.destroyallsoftware.com/talks/wat
                                                                      Тоже про JS в том числе. И я сам знаю массу людей, которые посмотрев такое видео совсем не будут думать так:


                                                                      Ах это же незатейливая шутка, пойду-ка почитаю спеку и начну понимать!

                                                                      А будут думать вот так:


                                                                      Ох е*ать, да я такое гавно даже трогать не буду.

                                                                      Поэтому это никакая не шутка, а просто безосновательный хейт.
                                                                      Почему-то автор не решил так пошутить про свой любимый Go.

                                                                        +3

                                                                        Может потому что гоу логичней Js. Да в гоу не хватает некоторых вещей, но там хотя бы с логичностью все нормально.

                                                                          +2

                                                                          В го тоже есть не однозначные вещи. Изменится ли поле ban после выполнения цикла?


                                                                          type Person struct {
                                                                              name string
                                                                              ban bool
                                                                          }
                                                                          
                                                                          func main() {
                                                                              persons := []Person{Person{name: "Alex", ban: false}}
                                                                              for _, person := range persons {
                                                                                  person.ban = true
                                                                              }
                                                                          }
                                                                            +2

                                                                            Тут неоднозначности нет. Тут достаточно знать указатель или нет. Действует, везде и всегда одинаково. В других языках так же работает.

                                                                              +3
                                                                              В скриптовых языках, включая и JS, нет не-указателей, поэтому всё действует ещё одинаковее.
                                                                            +5
                                                                            Может потому что гоу логичней Js. Да в гоу не хватает некоторых вещей, но там хотя бы с логичностью все нормально.

                                                                            Не может. Гоу — отвратительный и крайне нелогичный язык. Хуже всего, что он притворяется статическим, а на самом деле таким не является. А ещё у него дизайн намного сырее, чем в ЖС.

                                                                              0
                                                                              Хотя мне нравится больше js (es6, ts), но я не могу согласиться с тем, что го отвратительный язык. Сами разработчика языка говорили для какой группы людей он предназначен. Если мы возьмем языки с низким порогом вхождения (Js, PHP, Python, Golang), то я бы больше рекомендовал Go новичкам, т.к. именно он накладывает больше ограничений на стиль написания, те же типы. Т.е. для меня сейчас Go это как паскаль раньше.
                                                                              Насчет дизайна я не понимаю что именно вы имеете ввиду.
                                                                                +1
                                                                                именно он накладывает больше ограничений на стиль написания, те же типы

                                                                                Он накладывает ограничения слишком слабо. Из статических языков с низким порогом вхождения хороши C# и TypeScript. А Гоу — отвратительный язык, который прививает отвратительные практики. У него WTF не на уровне "вот метод странно называется", а на уровне "какой идиот так сделал архитектуру языка?"


                                                                                Вот пример, первый комментарий: https://habr.com/ru/company/mailru/blog/341822/#comment_10510426

                                                                                  0

                                                                                  Я бы не рекомендовал новичку TS просто потому что в TS есть any. Даже если взять юзкейс коллбэка. Вряд ли новичок будет писать тип функции коллбэка…
                                                                                  Насчёт шарпов — я бы не сказал, что шарпы на равне с Го по лёгкости изучения (хотя шарпы был вторым языком после плюсов, которые я изучал в 2014).
                                                                                  Я согласен с вами, что Го это не самый лучший язык для решения определённых задач, но я писал именно про то, что для новичка он бы подошёл и сравнил его с паскалем (лично нас обучали Паскалю в школе).
                                                                                  А вот дальше, когда человек понимает прелесть ООПа, понимает смысл документировать свой код, абстрагировать модули, тогда да, Го с этим справляется не очень хорошо (если брать пункт про ооп, то не справляется вообще)
                                                                                  А про ограничения я имел ввиду то, что го накладывает ограничения на стиль написания кода (у него есть офф форматер и компилятор ругается на неправильный стиль)
                                                                                  Насчет прививает отвратительные практики — новички не будут писать настолько сложные программы, чтобы вышли за пределы общих практик

                                                                                    +1
                                                                                    Я бы не рекомендовал новичку TS просто потому что в TS есть any

                                                                                    Который стараются не использовать в реальном коде в отличие от того же Go.
                                                                                    А в Go any — это просто идеология и часто — единственный путь. Я про interface{} если что.

                                                                                      0

                                                                                      Я помню interface{} в го, я помню боль xD. Это из личного опыта, а не стёб, если что =)
                                                                                      Вы правы что any стараются не использовать, но я всё же говорил про новичков в программировании. Лично я помню свои первые ощущения от разработки ПО (О, это работает, ура! Нужно добавить больше функционала), а при таком настрое наврядли люди задумываются про документированность (да, я считаю, что указание типов относится к документированию).
                                                                                      Наврятли новички зайдут настолько глубоко, чтобы у них было многое завязано на interface{} (вспоминая свою первую учебную программу, где я просто реализовал туду, там с interface{} я сталкивался только при сериализации данных)

                                                                                        0

                                                                                        Просто странно, что в TS для вас присутствие any — аргумент, а в GO — нет.


                                                                                        Я бы не рекомендовал новичку TS просто потому что в TS есть any

                                                                                        Хотя в TS в отличии от Go можно вообще без any, а в Go рано или поздно приходишь к тому, что выразительности не хватает

                                                                                          0

                                                                                          Дап сам не знаю на 100% почему в Го это не такой сильный аргумент как в TS для меня. Возможно, в Го ты не так быстро столкнешься с interface{}, как в TS. Возможно при просмотре кода других людей я чаще встречал any, чем interface{}
                                                                                          Насчет выразительности я согласен, но боюсь, что нас другие могут не понять, почему мы го обвиняем в этом =)

                                                                                      0

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

                                                                                        0

                                                                                        Я не говорю про всех, возможно это только в моей "шараге" студенты толком не разбираются со структурами. Но по правде, у нас до 4 курса доживают 5 из 30 студентов.
                                                                                        Под новичками я всё же имел ввиду именно начинающих погружаться в мир программирования. Даже сравнил Го с паскалем.
                                                                                        Я не спорю с тем, что на Го неудобно решать какие-либо задачи. Но вы согласитесь с тем, что на Го удобно запустить сервер из коробки?

                                                                                          0

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

                                                                                            0

                                                                                            Хорошо, что мы с вами поняли, что говорим о разных "новичках" =)
                                                                                            Не знаю поверите вы или нет, но для новичка (любого) интереснее что-то создавать, а не изучать как всё устроено внутри.
                                                                                            Я не спорю про то, что знать как всё устроено внутри (в стандартных функциях либо структурах) нужно, но это зачастую не так интересно новичкам, как просто настроить окружение и писать чисто бизнес логику приложения, без продумывания архитектуры без документации.
                                                                                            Возможно вы гораздо старше меня и не помните почему сами окунулись в изучение программирования.
                                                                                            Я не обсуждаю что правильно, а что нет, я лишь накладываю свои эмоции от знакомства с разработкой на возможные ситуации, если бы начинал знакомство с программированием на других языках

                                                                                              +1

                                                                                              Прекрасно знаю, о чем речь, потому что сам создавал на юкосе в конструкторе сайты на вбуллетине для клана в травиане. Копался в php, пытался там аватарки настроить, и мнил себя кулхацкером.


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


                                                                                              Я отлично помню как я окунулся в программирование. В конце нулевых я пошел на курсы в институт, чтобы подготовиться к ЕГЭ. Помимо всего прочего решил походить на информатику, там доплата была типа 1000 рублей в месяц. Прихожу, нас сажают за стол, на доске объясняют всякое, часа через пол говорят садиться за компьютеры и написать программу: определить, оканчивается ли число на цифру. Достаточно быстро сделал, мы показали наши результаты, урок закончился, я пришел домой. И стало мне интересно: как проверить, заканчивается ли число на другое число? В итоге просидел несколько часов, так крутил, эдак, в итоге родил нечто с двумя циклами и пятком goto, которое как-то в франкенштейновском стиле работало. Тут и вопросы пришлось решать, а что если число вообще меньше чем делимое (для цифр такого кейса нет), и вообще как это сделать думать..


                                                                                              В общем, мне кажется именно вопрос "а как вот так сделать" и определило мою профессию.

                                                                                                +1

                                                                                                Ну блин, такой интересный рассказ, чем-то похож на мою историю (тоже сайтики, дкп система для клана и т.п.), а в итоге вы в последнем абзаце написали "а как вот так сделать", что больше относится к написанию нового (по-моему мнению), а не "а как это работает", что относилось бы к изучению =)
                                                                                                Всё же я считаю пример с бензопилой не совсем уместным (там травмоопасно =) ). Да и вы сказали, что обучать нужно с азов, а что если некому обучать?) Что если у вас есть желание творить, вы хотите создать тот же сайт, как быстро в вас погаснет искра, если начинаете изучение со структур?)
                                                                                                Лично я начал изучать структуры в школе, на оллимпиадном программировании, но к тому моменту я уже разрабатывал другие программы несколько лет =)
                                                                                                Ладно) мне кажется невозможно дать объективный ответ.
                                                                                                Кстати там пониже мы обсуждаем как раз поверхностные знания и к чему это приводит xD

                                                                              –5
                                                                              А будут думать вот так:

                                                                              Ох е*ать, да я такое гавно даже трогать не буду.


                                                                              Мне кажется у языка тут больше заслуга чем у wat видео.
                                                                                +2
                                                                                Ну да, прочитает кто-то такую статью и решит не становиться js-программистом, в мире появится на полтора псевдо-десктоп приложения меньше и где-то будет остывать незанятый транскомпиляцией процессор. Ужасная картина, прям антиутопия.
                                                                                –3
                                                                                Человек, который не может переплюнуть, старается оплевать.
                                                                                  +8
                                                                                  От создателей «Брат за брата — за основу взято.» и «Он обидел, слёз не видел.» Как Вы подтвердите истинность вашего утверждения?
                                                                                    +1
                                                                                    Он просто за цитаты и двор стреляет в упор.
                                                                                  –2
                                                                                  Ок, давайте так, есть правила написания хорошего кода, JS не соответствует им. Если я напишу в проекте метод сортировки, который будет сначала приводить элементы массива к строке, а потом сортирует и назову его не «sortAsString», а просто «sort» — меня по головке не погладят.
                                                                                  В частности поэтому JS это кал говна, а не язык. Я не могу помнить всю документацию наизусть и знать что там себе нафантазировали те кто писал реализации.
                                                                                    +3

                                                                                    Я надеюсь вы понимаете то вы спорите об default-ом значении аргумента компаратора? Т.е. лексикографически сортировка производится ТОЛЬКО тогда когда вы сами так захотели. В противном случае вы просто указываете нужный компаратор и всё работает как вам надо. Причина почему .sort сам не угадывает какой компаратор вам нужен кроется в динамической природе типизации. У вас в нём могут быть как строки, так и числа, и объекты и пр…


                                                                                    Т.е. в том что .sort без явного указания метода компаратора сортирует всё лексикографически всё правильно. Единственное что можно было сделать вместо этого, это вообще не позволять метод sort без указания компаратора. Ну тоже такое спорное решение.

                                                                                      0
                                                                                      $ python
                                                                                      Python 2.7.16 (default, Mar 20 2019, 12:15:19)
                                                                                      >>> n = [-2, -7, 0.0000001, 0.0000000006, 6, 10]
                                                                                      >>> n.sort()
                                                                                      >>> print n
                                                                                      [-7, -2, 6e-10, 1e-07, 6, 10]

                                                                                      ??
                                                                                        +3

                                                                                        и что вы хотели этим показать?


                                                                                        m = [-2, "-1", "AAA", -10, 10, 100, "BBB", "ZZZ", "1000"]
                                                                                        m.sort()
                                                                                        print m
                                                                                        [-10, -2, 10, 100, '-1', '1000', 'AAA', 'BBB', 'ZZZ']

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


                                                                                        Но не зная этого факта наверняка я бы не решился использовать этот .sort() как есть. Причём ни в python, ни в javascript. Нельзя писать такой код вслепую на удачу. А это приводит нас к тому, с чего мы начали — это всего лишь спор о дефолтном компараторе в методе сортировки массива.

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

                                                                                            Я честно говоря с трудом могу себе представить зачем мне может потребоваться отсортировать массив выше так, как это сделал python. Какую задачу это решает? Правда я с трудом понимаю зачем мне в целом такой массив может потребоваться. Из реальных примеров это когда у вас массив чисел состоит по факту из чисел и строко-чисел (такие грешки в JS мире встречаются часто). Но тогда обе сортировки дадут убогий результат. Так что пожалуй соглашусь, что default comparator в python выбран несколько лучше. Но я решительно не вижу никаких "WAT?" в лексикографическом варианте. Я нахожу это очень даже логичным и последовательным поведением. Просто в python решили сделать более продвинутую версию. Молодцы.

                                                                                            +1

                                                                                            Второй питон уже даже официально не поддерживается. В третьем же всё нормально: TypeError, так как строки и числа не сравниваемые.

                                                                                      –2
                                                                                      Полностью поддерживаю, хоть и написано несколько в грубой форме. 90% моментов описанных тут из-за не знания языка. Пример с (2,4,5,6) === (2,5,6) вообще смешон. А чего автор ожидал от такого сравнения? Ему не знакомо как работает оператор, или оператор ()? Вот этот пример очень хорошо отображает суть статьи — чем меньше знаний о языке имеешь тем больше моментов тебе (считаешь глупыми) не понятны. Повторюсь с остальными так же. Везде есть объяснение.

                                                                                      Пример с обфускацией тоже глупый. Обфуцировать можно любой язык. И придумать там аналогичную строку, которая будет исполняться. Возьмите хоть тот же BF)))

                                                                                      Мне только не ясен пример с коммутативностью. Плюс параллелизм в JS я не особо знаю, но вроде он не сильный — да. Остальное всё просто школьные выдумки о якобы «плохом языке», а на самом деле о слабых знаниях автора, либо же просто желание автора поджечь очередной холивар.
                                                                                      +8
                                                                                      var re = /a/g
                                                                                      re.test('ab')
                                                                                      // true
                                                                                      re.test('ab')
                                                                                      // false
                                                                                      re.test('ab')
                                                                                      // true
                                                                                      
                                                                                      Проверил — реально так и есть. Кто-то может объяснить в чём цирк? Документация ничего такого не предполагает.
                                                                                        +2

                                                                                        https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex


                                                                                        Если свойство lastIndex больше длины строки, методы test() и exec() завершатся с неудачей, а свойство lastIndex будет установлено в 0.

                                                                                        Кстати, по вашей же ссылке:


                                                                                        Как и при вызове метода exec() (или при совместном с ним вызове), метод test(), вызванный несколько раз на одном и том же экземпляре глобального регулярного выражения, будет начинать проверку с конца предыдущего сопоставления.
                                                                                          0
                                                                                          Ах вот оно что. Спасибо что объяснили. Я, вообще, люблю JS, но это какая-то дичь…
                                                                                            +12

                                                                                            Просто статья завалена бредовыми примерами. Вот это вообще какой-то угар:


                                                                                            Типы могут меняться в самых неожиданных местах, что помогает программисту не заснуть во время долгих рабочих ночей:
                                                                                            
                                                                                            typeof([1][0])
                                                                                            // number
                                                                                            for(let i in [1]){console.log(typeof(i))}
                                                                                            // string

                                                                                            Начнем с того, что typeof это оператор, поэтому не совсем понятно зачем он там юзает скобки. Ну это косметика, не важно.
                                                                                            Важно то, что в первом случае он делает


                                                                                            typeof 1 // что логично - намбер

                                                                                            Во втором он делает


                                                                                            typeof "0" // так как итерирует над ключами объекта, а первый ключ объекта массива это "0".

                                                                                            Что можно вообще думать о познаниях автора?


                                                                                            Следующий пример не лучше же — сам дергает toString (ну ему это, конечно, не известно, так как зачем читать как работает ==) — и удивляется, что переменная то меняет свое значение.

                                                                                              +6
                                                                                              Что можно вообще думать о познаниях автора?

                                                                                              Мне кажется, что с познаниями у автора всё нормально. Просто нужен был материал для статьи :) Всё равно те, на кого эта статья ориентирована, не полезут разбираться во всех этих for-of, for-in, for;; и т.д… А значит пример задаче пригоден.


                                                                                              Очень немногие пункты из всей статьи по факту являются чем-то… спорным или глупым. В большинстве своём просто мелкие нюансы инструментов, используемых не по делу. Хорошо хоть массивы с объектами не стал складывать.


                                                                                              P.S. всякий раз когда мне приходилось залезать в C++ у меня бомбило похлеще Хиросимы. Но я не бежал писать статью про всякие глупости.

                                                                                                +13

                                                                                                Хм… Знали бы вы, как бомбит от C++ людей, которые реально на нём пишут :-D
                                                                                                И, тем не менее, пользуемся.

                                                                                                  +1
                                                                                                  P.S. всякий раз когда мне приходилось залезать в C++ у меня бомбило похлеще Хиросимы. Но я не бежал писать статью про всякие глупости.

                                                                                                  А кто-то пишет

                                                                                                  +4
                                                                                                  Да тут вся статья, как и всегда в подобных случаях кстати, основана на примерах, которых в реальном мире и не встретишь. Ну может в очень специфичных местах или по незнанию. Не сталкиваются реальные люди со всей этой дичью настолько часто, чтобы это мешало.
                                                                                                    +1
                                                                                                    А если еще и с typescript, то все эти приколы встречаются примерно никогда.
                                                                                                    –1
                                                                                                    Здесь в js плохо то, что если работаешь с массивом, в котором элементы пронумерованы от 0 до N, и по-другомы быть никак не может, то ожидаешь что эта прогрессия будет иметь числовое выражение, а не строковое.
                                                                                                    Я однажды потратил часов пять на поиск бага, связанного с этим поведением.
                                                                                                    После чего сделал шпаргалку из косяков JS и перед тем как искать проблему в своем коде, сверяюсь с этой шпаргалкой.
                                                                                                    И знаете что? Стало реально проще работать! Предположение, что это не я дурак, а автор языка, стало экономить просто прорву времени.
                                                                                                      +4

                                                                                                      Всё же использовать for-in не имея ни малейшего представления о том, что это такое и жаловаться, что оно работает не так как ожидал, это странновато, нет? :)


                                                                                                      Какой язык не возьми, если писать на нём в стиле "я что-то загуглил, скопировал в код и оно кажется работает" — проблемы не заставят себя долго ждать.


                                                                                                      А если с динамически типизированными языками до этого в целом неработали (будь то ruby, php, js, python) то слепое следование своим привычкам, скажем, из Java, C#, C++ очень часто будет приводить к печальным следствиям.


                                                                                                      Пассаж про то, что автор языка дурак, потому что в JS ключами у объектов могут быть только строки и символы не ясен.

                                                                                                        0
                                                                                                        for-in в Python и в Ruby работает как раз так, как автор ожидает.
                                                                                                        Т.е. дело не в динамически типизированных языках в целом, а в неочевидном синтаксисе конкретно JS.
                                                                                                          +1
                                                                                                          for-in в Python и в Ruby работает как раз так, как автор ожидает

                                                                                                          Беру пример из документации:


                                                                                                          fruits = ["apple", "banana", "cherry"]
                                                                                                          for x in fruits:
                                                                                                            print(x) 

                                                                                                          результат: apple banana cherry
                                                                                                          что я ожидал из ваших слов: 0 1 2


                                                                                                          Т.е. работает он как for-of в JavaScript-е:


                                                                                                          const fruits = ["apple", "banana", "cherry"]
                                                                                                          for (const x of fruits) 
                                                                                                            console.log(x) 
                                                                                                          // apple banana cherry

                                                                                                          Может быть вы имели ввиду другую конструкцию?
                                                                                                          P.S. ruby проверять лень, давайте остановимся на python-е

                                                                                                            –1
                                                                                                            Так автор и ожидал поведения, как у for-of в JS.
                                                                                                              +4

                                                                                                              Это очень плохо характеризует автора. Писать код по принципу "я так писал в языке X, если оно не упадёт с syntax error, значит должно работать точно также"… В общем логика тут немного безумная. В большинстве языков вообще будет syntax-error.

                                                                                                                +1
                                                                                                                Я же говорю: дело не в динамически типизированных языках в целом, а в неочевидном синтаксисе конкретно JS.

                                                                                                                Конструкция for-in ожидаемым образом работает в большом числе языков — кроме Python и Ruby, ещё как минимум в батниках и шелл-скриптах; и в одном лишь JS она означает нечто иное.
                                                                                                                  0

                                                                                                                  В том же Go, с которого "переходил" автор, не всё так однозначно. Угадайте, что выведет этот код?


                                                                                                                  arr := []int{2, 1, 0}
                                                                                                                  for x := range arr {
                                                                                                                      fmt.Printf("%d\n", x)
                                                                                                                  }

                                                                                                                  В любом языке желательно знать синтаксис, интуиция может подвести.

                                                                                                                    0
                                                                                                                    Выведет ключи, что-то вроде for in в JS. Не сложно и понятно
                                                                                                                      0

                                                                                                                      А в JS это самое — сложно и непонятно

                                                                                                                  –1
                                                                                                                  Вот безумная логика:
                                                                                                                  x = [1,2,3,4,5];
                                                                                                                  for (var i in x) {
                                                                                                                  console.log(x[i+1]);
                                                                                                                  }
                                                                                                                  Думаете, будет 2 3 4 5 undefined?
                                                                                                                  Это совершенно естетсвенно так думать.
                                                                                                                  Но увы.
                                                                                                                    +2

                                                                                                                    Думаю, что i — строка. Правильно думаю?

                                                                                                            0
                                                                                                            А если с динамически типизированными языками до этого в целом неработали


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

                                                                                                              Аргументы против динамической типизации как раз понятны. Но я думаю это топик для отдельного спора. Я тоже больше склонен к статической типизации, нежели к динамической. Но не жалею, что много лет писал именно на динамической, т.к. оказалось, что есть и свои плюсы. Не имея больших оков, и не имея нужды доказывать всё компилятору, оказывается очень удобно работать со всякими сложными абстракциями. Многие мои знакомые которые всю жизнь пишут на статически типизированных языках очень долго въезжают в некоторые абстракции, или не понимают их вовсе. Часто не понимают реальной подоплёки под многими паттернами, т.к. для них многие из них были скорее попыткой обыграть компилятор, нежели представляли решение какой-то конкретной задачи.


                                                                                                              И вот сейчас имея Typescript со структурной типизацией и продвинутыми generic-ами я ловлю себя на том, что мне было бы физически больно писать на каком-нибудь более костном языке, где к типам прилагаются ещё и кандалы.

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


                                                                                                                Ну, аргумент так себе. Сексом заниматься, стоя на сноуборде в гамаке тоже можно — но так ли это нужно? Вопрос в том, чем объективно эти самые «некоторые абстракции» хороши?
                                                                                                                  0
                                                                                                                  Вопрос в том, чем объективно эти самые «некоторые абстракции» хороши?

                                                                                                                  На самом деле я не думаю, что на этот вопрос можно дать объективный ответ. К тому же всё очень сильно зависит от вашей команды. Если писать такие абстракции которые только их автор и может поддерживать, то это одна история. Если эти абстракции, заключая в себе, много сложности, в значительной степени упрощают весь остальной код, а сами хорошо протестированы, то это другая история… И переменных в этом уравнении куда больше двух. Так или иначе они сужают bus-factor. И… вызывают привыкание.


                                                                                                                  Могу лишь сказать, что в отрыве от задач бизнеса, опираясь скорее не собственные хотелки, их писать очень приятно. Этакий challenge за challenge-ом. Люблю нетривиальные задачи. А вот рутинное копи-пейст программирование нагоняет тоску.

                                                                                                                    +2
                                                                                                                    Этакий challenge за challenge-ом


                                                                                                                    В кровати, хате и халате
                                                                                                                    Покой находит обыватель.
                                                                                                                    А романтик все снует,
                                                                                                                    В шестеренки хер сует.

                                                                                                                    (с) Губерман
                                                                                                                  0
                                                                                                                  Не имея больших оков, и не имея нужды доказывать всё компилятору, оказывается очень удобно работать со всякими сложными абстракциями.

                                                                                                                  Например?

                                                                                                                    +1

                                                                                                                    У меня есть задача на Шарпах, которую я не могу решить полностью статически. Допустим, я хочу получить такой контракт:


                                                                                                                    class IListener<TEvent> {
                                                                                                                        void On(TEvent ev);
                                                                                                                    }
                                                                                                                    
                                                                                                                    class MyClass : IListener<EventA>, IListener<EventB> {
                                                                                                                    
                                                                                                                        public void On(EventA ev) {
                                                                                                                            // EventA handler
                                                                                                                        }
                                                                                                                    
                                                                                                                        public void On(EventB ev) {
                                                                                                                            // EventB handler
                                                                                                                        }
                                                                                                                    
                                                                                                                    }
                                                                                                                    
                                                                                                                    class Emitter {}
                                                                                                                    
                                                                                                                    public class App {
                                                                                                                        public static void Main () {
                                                                                                                            var emitter = new Emitter();
                                                                                                                            var myClass = new MyClass();
                                                                                                                    
                                                                                                                            emitter.Subscribe(myClass);
                                                                                                                        }
                                                                                                                    }

                                                                                                                    Я могу написать Emitter без рефлексии, который позволит мне делать следующие вызовы:


                                                                                                                    if (condition) {
                                                                                                                      emitter.Fire(new EventA())
                                                                                                                    } else {
                                                                                                                      emitter.Fire(new EventB())
                                                                                                                    }

                                                                                                                    Но не могу написать следующий код:


                                                                                                                    var ev = condition
                                                                                                                      ? new EventA()
                                                                                                                      : new EventB();
                                                                                                                    
                                                                                                                    emitter.Fire(ev);
                                                                                                                      0

                                                                                                                      И хорошо, потому что у класса может быть еще один метод On который вообще ни к каким интерфейсам не относится (и может даже приватный), и вызваться не должен ни при каких обстоятельствах.


                                                                                                                      В общем, это нормальная ситуация. Если нужна открытая иерархия то вы не можете гарантировать что нужный метод есть. Если закрытая, то делается АДТ и единственный метод On в котором свичом разбирается это адт.


                                                                                                                      А вообще такие вещи визиторами обычно делаются, с OnEventA/OnEventB

                                                                                                                        +1
                                                                                                                        В общем, это нормальная ситуация.

                                                                                                                        А я и не спорю. Я вообще не представляю, как статически это реализовать. По сути оно сводится к такому:


                                                                                                                        class MyClass : IListener<EventA>, IListener<EventB> {
                                                                                                                            public void On(EventA ev) {}
                                                                                                                            public void On(EventB ev) {}
                                                                                                                        }
                                                                                                                        
                                                                                                                        var ev = condition
                                                                                                                          ? new EventA()
                                                                                                                          : new EventB();
                                                                                                                        
                                                                                                                        new MyClass().On(ev);
                                                                                                                        

                                                                                                                        Естественно, тут нельзя выбрать статически. Сами требования к задаче — динамические.


                                                                                                                        А вообще такие вещи визиторами обычно делаются, с OnEventA/OnEventB

                                                                                                                        Понимаю. Но немного ни к чему обязывающей рефлексии — и у меня в два раза меньше кода.

                                                                                                                          0

                                                                                                                          Так а зачем тут рефлексия? Чем это лучше чем


                                                                                                                          class MyClass : Listener {
                                                                                                                              public override void OnEventA (EventA ev) {}
                                                                                                                              public override void OnEventB (EventB ev) {}
                                                                                                                          }
                                                                                                                          
                                                                                                                          IEvent ev = condition
                                                                                                                            ? new EventA()
                                                                                                                            : new EventB();
                                                                                                                          new MyClass().On(ev);

                                                                                                                          И мне не кажется что тут есть какие-то динамические требования. Вопрос только как их выразить. Хотя шарп довольно слаб, тут не поспоришь.

                                                                                                                            +1

                                                                                                                            А если у вас 1000 эвентов, а листенер должен слушать только 3 из них?


                                                                                                                            Плюс ваш код не работает

                                                                                                                              0

                                                                                                                              Оверрайдите столько методов сколько эвентом собираетесь слушать.


                                                                                                                              Плюс ваш код не работает

                                                                                                                              В каком плане? Это пример, а не полноценное приложение.

                                                                                                                                0

                                                                                                                                Ну я понял, что пример. Вы там метод On вызываете, которого нету… Он делает пример, увы, непонятным.


                                                                                                                                То есть Listener — такой огромный класс с тьмой методов на каждый эвент? И добавляя эвент я должен зайти в листенер и добавить ещё метод в него, правильно?

                                                                                                                                  0

                                                                                                                                  Да, так и делают в таких случаях. Необходимость написать один switch (который причем студия даже сгенерировать может) не перевешивает кучу плюсов от такого. У меня не один проект был, где люди пожидились на такой свитч, и я потом днями искал кто что создаёт, потому что через рефелкшн чето куда-то пролезает, куда-то ещё передается, и хрен что поймешь. Особенно, если проект для билда и запуска требует какой-то хитрой магии, поэтому локально не работает, и просто подебажить нельзя.

                                                                                                                                    0

                                                                                                                                    Не, у нас нету проблем. Рефлексия очень простая и всё очень надёжно и понятно. А в клиентском коде всё очень статично.

                                                                                                                                      +1

                                                                                                                                      И как выглядит решение с рефлексией?

                                                                                                                                        0

                                                                                                                                        Пишу по памяти. Рефлексией создается только Invoker эвента. Что-то вроде такого класса:


                                                                                                                                        class InvokerOfMySpecificEvent {
                                                                                                                                          public void TryInvoke (object listener, MySpecificEvent ev)
                                                                                                                                          {
                                                                                                                                            if (listener is IListener<MySpecificEvent> target) {
                                                                                                                                              target.On((MySpecificEvent) ev);
                                                                                                                                            }
                                                                                                                                          }
                                                                                                                                        }

                                                                                                                                        Теперь при Fire события ищется его инвоукер и событие вызывается на всех подписчиках.


                                                                                                                                        private interface IInvoker {
                                                                                                                                          void TryInvoke (IListener listener, IEvent iEvent);
                                                                                                                                        }
                                                                                                                                        
                                                                                                                                        private class Invoker<TEvent> : IInvoker where TEvent : IEvent {
                                                                                                                                          public void TryInvoke (IListener listener, IEvent ev)
                                                                                                                                          {
                                                                                                                                            if (listener is IListener<TEvent> target) {
                                                                                                                                              target.On((TEvent) ev);
                                                                                                                                            }
                                                                                                                                          }
                                                                                                                                        }
                                                                                                                                        
                                                                                                                                        public class Emitter {
                                                                                                                                          private readonly List<IListener> listeners = new List<IListener>();
                                                                                                                                        
                                                                                                                                          public void Subscribe (IListener listener)
                                                                                                                                          {
                                                                                                                                            listeners.Add(listener);
                                                                                                                                          }
                                                                                                                                        
                                                                                                                                          public void Unsubscribe(IListener listener)
                                                                                                                                          {
                                                                                                                                            listeners.Remove(listener);
                                                                                                                                          }
                                                                                                                                        
                                                                                                                                          public void Fire( IEvent ev )
                                                                                                                                          {
                                                                                                                                            var invoker = GetInvoker(ev);
                                                                                                                                        
                                                                                                                                            foreach (var lnr in listeners) {
                                                                                                                                              invoker.TryInvoke(lnr, ev);
                                                                                                                                            }
                                                                                                                                          }
                                                                                                                                        
                                                                                                                                          private readonly Dictionary<Type, IInvoker> invokers = new Dictionary<Type, IInvoker>();
                                                                                                                                        
                                                                                                                                          private IInvoker GetInvoker (IEvent ev)
                                                                                                                                          {
                                                                                                                                            IInvoker invoker;
                                                                                                                                            var type = ev.GetType();
                                                                                                                                        
                                                                                                                                            if (!invokers.TryGetValue(type, out invoker)) {
                                                                                                                                              var InvokerClass = typeof(Invoker<>).MakeGenericType(type);
                                                                                                                                              var invoker = (IInvoker) Activator.CreateInstance(InvokerClass);
                                                                                                                                              invokers[type] = invoker;
                                                                                                                                            }
                                                                                                                                            return invoker;
                                                                                                                                          }
                                                                                                                                        }

                                                                                                                                        На самом деле у нас событиями выступают команды в движке, потому как-то так выглядит использование в логике:


                                                                                                                                        class FireCommand : Command {
                                                                                                                                          public FireCommand (Unit unit)
                                                                                                                                        
                                                                                                                                          public void OnExec () {
                                                                                                                                            var weaponFireCmd = unit.weapon.Fire();
                                                                                                                                            commander.Exec( weaponFireCmd );
                                                                                                                                          }
                                                                                                                                        }
                                                                                                                                        
                                                                                                                                        class CannonWeapon : IWeapon {
                                                                                                                                          public CannonWeapon(int power)
                                                                                                                                        
                                                                                                                                          Fire() => new FireCannonCommand(power);
                                                                                                                                        }
                                                                                                                                        
                                                                                                                                        class RocketWeapon : IWeapon {
                                                                                                                                          Fire() => new FireRocketCommand();
                                                                                                                                        }
                                                                                                                                        
                                                                                                                                        class BulletWeapon : IWeapon {
                                                                                                                                          public CannonWeapon(int mass)
                                                                                                                                        
                                                                                                                                          Fire() => new FireBulletCommand(mass);
                                                                                                                                        }

                                                                                                                                        Теперь представим, что у нас есть как-то звуковик, который хочет проигрывать звуки при выстрелах.


                                                                                                                                        class SoundManager
                                                                                                                                        : IListener<FireCannonCommand>
                                                                                                                                        , IListener<FireRockerCommand>
                                                                                                                                        , IListener<FireBulletCommand> {
                                                                                                                                        
                                                                                                                                          void On (FireCannonCommand ev) {
                                                                                                                                            if (ev.power > 10) {
                                                                                                                                              Play("BigCannonSound.mp3");
                                                                                                                                            } else {
                                                                                                                                              Play("CannonSound.mp3");
                                                                                                                                            }
                                                                                                                                          }
                                                                                                                                        
                                                                                                                                          void On (FireRockerCommand ev) {
                                                                                                                                            Play("RocketSound.mp3");
                                                                                                                                          }
                                                                                                                                        
                                                                                                                                          void On (FireBulletCommand ev) {
                                                                                                                                            if (ev.mass > 200) {
                                                                                                                                              Play("BulletSound.mp3");
                                                                                                                                            } else {
                                                                                                                                              Play("SmallBulletSound.mp3");
                                                                                                                                            }
                                                                                                                                          }
                                                                                                                                        
                                                                                                                                        }

                                                                                                                                        Ну и где-то при инициализации саунд-менеджера:


                                                                                                                                        class Installer {
                                                                                                                                          void Install () {
                                                                                                                                            // bla-bla
                                                                                                                                            emitter.Subscribe(soundManager);
                                                                                                                                          }
                                                                                                                                        }

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


                                                                                                                                        Если кто-то пишет плагин для нашей игры и добавляет событие в своём плагине — ему не нужно делать манки патчинг и думать, как сделать, чтобы команда заработала. Любая команда уже работает.


                                                                                                                                        Ну и стек-трейс очень простой:


                                                                                                                                        Commander:Exec
                                                                                                                                        └─FireCommand:OnExec
                                                                                                                                          └─Commander:Exec
                                                                                                                                            ├─FireCannonCommand:OnExec
                                                                                                                                            └─Emitter:Fire
                                                                                                                                              └─Invoker<FireCannonCommand>:TryInvoke
                                                                                                                                                └─SoundManager:On(FireCannonCommand)

                                                                                                                                        Скажите честно, понятен ли для вам стек-трейс, архитектура приложения и зависимости модулей?


                                                                                                                                        Да, кстати, ещё у нас иерархия событий красиво выстраивается в логе.


                                                                                                                                        Вот пример из тестов

                                                                                                                                        Простите, что картинкой, но Хабра херит разметку


                                                                                                                                        Каждое из этих событий может дёрнуть эмиттер, а тот — найти листенер.

                                                                                                                                          0

                                                                                                                                          Мне всё понятно, но я и говорю: это экономия в один switch, который заменен на поиск через рефлекшн. Мне это кажется шагом назад, потому что очевидность что от чего зависит понижается. У нас сразу появляются кэши и вот это всё.


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




                                                                                                                                          Могу привести более приземлённый пример. Было у меня два почти одинаковых проекта (два человека в одной компании сели писать два симметричных сервиса, только один по заказам, а другой по другой сущности, но требования были очень похожи). В одном был свитч на ~50 веток который ставил в соответствие команду и действие. В другом через рефлекшн доставались все наследники ICommand и также через рефлешн определялось, что должно вызываться. Так вот, как раз с вариантом на свитче проблем было меньше всего — новые люди когда создавали по-аналогии новые команды находили свитч через find usages и добавляли ветку, и всё такое. Вариант на рефленше тоже в целом работал, но люди часто забывали или магический маркерный интерфейс унаследовать, или еще что-нибудь, и получали разлом в лучшем случае на этапе QA, а иногда и позже.




                                                                                                                                          В общем, если я вижу using System.Reflection в начале файла, у меня сразу возникает определенное сомнение в необходимости подобного. И как показывает практика — часто этого можно избежать. Я проходил этот путь: сначала рефлкшн, потом кодогенерация на Expression, в последнее время, если никуда не деваться и вот не получается выразить обычными средствами, — кодогенерация через roslyn api, такие вот процедурные макросы с немного страшноватым синтаксисом. То есть, если лень писать такие свитчи руками — пусть генерирует машина. Всё лучше, чем ошибиться в рантайме.

                                                                                                                                            +1
                                                                                                                                            Так вот, как раз с вариантом на свитче проблем было меньше всего — новые люди когда создавали по-аналогии новые команды находили свитч через find usages и добавляли ветку, и всё такое. Вариант на рефленше тоже в целом работал, но люди часто забывали или магический маркерный интерфейс унаследовать, или еще что-нибудь, и получали разлом в лучшем случае на этапе QA, а иногда и позже.

                                                                                                                                            Тоже был такой момент. Теперь у нас вариант со свитчем окончательно запрещён — если разрабатывается много модулей паралельно большой командой и в конец свича добавляется маппинг, то на мержах будут конфликты каждый раз. QA возвращает на программиста, тому приходится править коммит. Потеря времени и, что хуже — бессмысленное раздражение. Я уж молчу про моменты, когда свитч забывается.


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


                                                                                                                                            У нас так карточки создаются. Свитч в итоге проклинала вся команда, в т.ч. программисты и гейм-дизайнеры.

                                                                                                                                              0
                                                                                                                                              Тоже был такой момент. Теперь у нас вариант со свитчем окончательно запрещён — если разрабатывается много модулей паралельно большой командой и в конец свича добавляется маппинг, то на мержах будут конфликты каждый раз. QA возвращает на программиста, тому приходится править коммит. Потеря времени и, что хуже — бессмысленное раздражение. Я уж молчу про моменты, когда свитч забывается

                                                                                                                                              Странно, у нас гит догадывается помёржить всё как нужно, не раз проверяли.


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

                                                                                                                                              А вот тут у нас всё же умудрялись косячить + обучающий момент пропадает, человек конечно может догадаться, что там маркерный интерфейс, пойти посмотреть как он используется, и догадаться что к чему, но чаще просит конфлюенс где будет написать "а у нас на проекте вот этот магический интерфейс за это отвечает, а то — за то. Чтобы добавить такой-то функционал скопируйте тот NewCommandExample.cs и отредактируйте как нужно".


                                                                                                                                              У нас так карточки создаются. Свитч в итоге проклинала вся команда, в т.ч. программисты и гейм-дизайнеры.

                                                                                                                                              Забавно, что вы приняли противоположенное нашему решение. Не могу сказать, вопрос в культуре разработки, философии о назначении типизации (мне ближк идеология parse, don't validate) или просто размер команды.

                                                                                                                                                0

                                                                                                                                                Кстати, у меня вопрос, а почему не сделать что-то в духе:


                                                                                                                                                class SoundManager : IListener {
                                                                                                                                                  void On(IEvent event) {
                                                                                                                                                    switch (event) {
                                                                                                                                                      case FireCannonCommand ev:
                                                                                                                                                        if (ev.power > 10) {
                                                                                                                                                          Play("BigCannonSound.mp3");
                                                                                                                                                        } else {
                                                                                                                                                          Play("CannonSound.mp3");
                                                                                                                                                        }
                                                                                                                                                        break;
                                                                                                                                                      case FireRockerCommand ev:
                                                                                                                                                          Play("RocketSound.mp3");
                                                                                                                                                        break;
                                                                                                                                                      case FireBulletCommand ev:
                                                                                                                                                        if (ev.mass > 200) {
                                                                                                                                                          Play("BulletSound.mp3");
                                                                                                                                                        } else {
                                                                                                                                                          Play("SmallBulletSound.mp3");
                                                                                                                                                        }
                                                                                                                                                        break;
                                                                                                                                                    }
                                                                                                                                                  }
                                                                                                                                                }

                                                                                                                                                По сути у вас АДТ в IListener, ну и вы обрабатываете те что интересно.

                                                                                                                                                  0

                                                                                                                                                  Вот так можно, конечно. Но мы, по сути, эту логику вынесли в Emitter. Не люблю ни свичи, ни большие методы. Да и декларативность уже не та. И рефакторить легче. Стек Трейс поинтереснее. Тут уже вкусовщина начинается, а рефлексия всё-равно скрыта глубоко и на коде кроме Эмиттера никак не сказывается.


                                                                                                                                                  Вы, кстати, не ответили:


                                                                                                                                                  Скажите честно, понятен ли для вам стек-трейс, архитектура приложения и зависимости модулей?

                                                                                                                                                  Я вот ещё что вспомнил, у меня был вариант полностью статического подобного эмиттера:


                                                                                                                                                  public class IEvent {
                                                                                                                                                    void Notify (Emitter emitter);
                                                                                                                                                  }
                                                                                                                                                  
                                                                                                                                                  public class Emitter
                                                                                                                                                  {
                                                                                                                                                    private readonly List<IListener> listeners
                                                                                                                                                      = new List<IListener>();
                                                                                                                                                  
                                                                                                                                                    public void Subscribe (IListener listener)
                                                                                                                                                    {
                                                                                                                                                      listeners.Add(listener);
                                                                                                                                                    }
                                                                                                                                                  
                                                                                                                                                    public void Unsubscribe(IListener listener)
                                                                                                                                                    {
                                                                                                                                                      listeners.Remove(listener);
                                                                                                                                                    }
                                                                                                                                                  
                                                                                                                                                    public void Fire<TEvent>( TEvent ev )
                                                                                                                                                      where TEvent : IEvent
                                                                                                                                                    {
                                                                                                                                                      foreach (var lnr in listeners) {
                                                                                                                                                        if (lnr is IListener<TEvent>) {
                                                                                                                                                          lnr.On(ev);
                                                                                                                                                        }
                                                                                                                                                      }
                                                                                                                                                    }
                                                                                                                                                  }
                                                                                                                                                  
                                                                                                                                                  public class Commander
                                                                                                                                                  {
                                                                                                                                                    // blabla
                                                                                                                                                  
                                                                                                                                                    void Exec (ICommand cmd) {
                                                                                                                                                      cmd.OnExec();
                                                                                                                                                      cmd.Notify(emitter);
                                                                                                                                                    }
                                                                                                                                                  }
                                                                                                                                                  
                                                                                                                                                  interface ICommand : IEvent {
                                                                                                                                                    void OnExec();
                                                                                                                                                  }
                                                                                                                                                  
                                                                                                                                                  public class MoveCommand : ICommand
                                                                                                                                                  {
                                                                                                                                                    public void OnExec () {
                                                                                                                                                      // bla-bla
                                                                                                                                                    }
                                                                                                                                                  
                                                                                                                                                    public void Notify (Emitter em)
                                                                                                                                                      => em.Fire(this);
                                                                                                                                                  }
                                                                                                                                                  
                                                                                                                                                  public class AttackCommand : ICommand
                                                                                                                                                  {
                                                                                                                                                    public void OnExec () {
                                                                                                                                                      // bla-bla
                                                                                                                                                    }
                                                                                                                                                  
                                                                                                                                                    public void Notify (Emitter em)
                                                                                                                                                      => em.Fire(this);
                                                                                                                                                  }

                                                                                                                                                  Но, были причины, почему оставили рефлексию.

                                                                                                                                                    0
                                                                                                                                                    Вот так можно, конечно. Но мы, по сути, эту логику вынесли в Emitter. Не люблю ни свичи, ни большие методы. Да и декларативность уже не та. И рефакторить легче. Стек Трейс поинтереснее. Тут уже вкусовщина начинается, а рефлексия всё-равно скрыта глубоко и на коде кроме Эмиттера никак не сказывается.

                                                                                                                                                    Ну, с этим я могу согласиться.


                                                                                                                                                    Вы, кстати, не ответили:

                                                                                                                                                    Ответил, смотрите первое предложение: https://habr.com/ru/company/ruvds/blog/499670/#comment_21567940


                                                                                                                                                    Я вот ещё что вспомнил, у меня был вариант полностью статического подобного эмиттера:

                                                                                                                                                    Тоже неплохой вариант, и ничуть не менее декларативный, особенно если чутка переписать:


                                                                                                                                                    foreach (var lnr in listeners.OfType<Listener<TEvent>>()) {
                                                                                                                                                      lnr.On(ev);
                                                                                                                                                    }
                                                                                                                        +1

                                                                                                                        Далеко не любой язык со статической типизацией умеет в high order rank polymorphism, или алгебру типов. В свою очередь, чтобы обойти ограничения ты или cast-ишь (убегая от типов), либо копипастишь (или даже кодогенерируешь), или используешь рефлексию (ох...), или просто не используешь какие-то прикольные фишки. Чаще даже не догадываешься об их существовании, т.к. в материалах про твой язык тебе они не попадались, в коде библиотек с которыми ты работаешь — ничего подобного нет, а всякие там книжки про паттерны проектирования выглядят как-то очень надуманно.


                                                                                                                        И совсем другая картинка когда у тебя абсолютная анархия и развязаны руки. Ты можешь творить практически всё что угодно… и творишь. А твои коллеги творят ещё более дикую дичь, а авторы некоторых библиотек вытворяют такое, что волосы на заднице шевелется. Ну и ты за ними… А потом случайно выясняешь что всем этим штукам есть имена и они хорошо известны в более академической среде. А ты просто привык писать так, потому что, не ну а чо, удобно же.


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


                                                                                                                        Ну ты понял. Просто представь что ты 10 лет писал на Idris-е, а потом тебя посадили на pascal образца 90-х.

                                                                                                                          0

                                                                                                                          Ну так из этого ведь не следует, что статическая типизация плохая, а в её отсутствии наше спасение. Просто не надо о ней судить по тем языкам, где с выразительностью языка типов всё плохо.

                                                                                                                            0
                                                                                                                            Просто не надо о ней судить по тем языкам, где с выразительностью языка типов всё плохо

                                                                                                                            • Java
                                                                                                                            • Kotlin
                                                                                                                            • Go
                                                                                                                            • C#
                                                                                                                            • Swift
                                                                                                                            • Rust
                                                                                                                            • C
                                                                                                                            • C++

                                                                                                                            Ну вот я взял из списка самых популярных языков в 2020г (первая ссылка в гугле) те что со статической типизацией. Ни Scala ни Haskell тут нет. Вопрос в каких из этих языков есть типовая алгебра, сужение типов в if-ах и condition-ах, полиморфизм высшего порядка?


                                                                                                                            Ну так из этого ведь не следует, что статическая типизация плохая

                                                                                                                            С этим соглашусь, да.

                                                                                                                              0
                                                                                                                              Вопрос в каких из этих языков есть типовая алгебра, сужение типов в if-ах и condition-ах, полиморфизм высшего порядка?

                                                                                                                              Вы будете смеяться, но кусочки этого есть в плюсах. Но там и я буду смеяться.


                                                                                                                              Но вообще я не понимаю этого аргумента. То есть, если бы процесс работы был этаким случайным блужданием, если бы вы выбирали язык для следующего проекта случайным образом, с вероятностью выбора каждого языка, пропорциональной его позиции или весу, то да, это бы имело смысл. Но вы, наверное, всё же работаете не так?

                                                                                                                                0

                                                                                                                                Если честно, то я не смог распарсить второй абзац :)
                                                                                                                                Но на всякий случай повторю — я не против статически типизированных языков (я за). Но я бы предпочёл динамически-типизированный язык тем из них, которые имеют слабую недоразвитую систему типов. Вотъ.

                                                                                                                                  0

                                                                                                                                  И правда, как-то сумбурно получилось.


                                                                                                                                  Короче, это я всё к тому говорил, что судить о каком-то понятии по качеству его реализации в топ-N языков для меня странно.


                                                                                                                                  А если выбирать между питоном и C, например, для нетривиальной логики я выберу кодогенерацию в С уволиться.

                                                                                                                                    0
                                                                                                                                    Если честно, то я не смог распарсить второй абзац :)

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

                                                                                                                                      0

                                                                                                                                      О, спасибо за перевод. Нет, это не так. К примеру у нас на backend-е scala. И вроде как по итогу выбор скалы рассматривается как ошибка, т.к. очень сложно найти и нанять scala-разработчика. И не из любого java разработчика удаётся сделать scala-разработчика. Техлид говорит, что если бы он вернулся назад во времени то выбрал бы какой-нибудь go или java.


                                                                                                                                      Точно так же и я не могу взять для нового проекта какой-нибудь elm, потому что с точки зрения бизнеса это будет фиаско. Мы с огромным трудом даже javascript разработчиков находим, а на elm не найдём никогда.

                                                                                                                                        0

                                                                                                                                        Но вы можете устроиться в фирму, где уже пишут на скале.

                                                                                                                                          +1

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


                                                                                                                                          Скажем моим хотелкам удовлетворяют всего 3-4 города в Европе. Мюнхен и города Швейцарии. Будь я каким-нибудь Haskell программистом, насколько сократился бы мой выбор? :) А JS/TS разработчики нужны в любой дыре. И работы им лет на 20 вперёд заготовлено.


                                                                                                                                          Думаю у вас в США с этим получше (как и с IT в целом). Но тут уже сама страна и её бюрократические препоны вносят немалый вклад...

                                                                                                                                            +1

                                                                                                                                            Осталось понять, что лучше — хаскель-разработка в Москве или тс-разработка в Мюнхене )

                                                                                                                                +1

                                                                                                                                К примеру в моей конторе пишут на Objective C, на Swift, на Kotlin и на Scala. Даже Scala (2-ая) не умеет того, что умеет TypeScript.

                                                                                                                          0
                                                                                                                          Я как раз недавно писал хабрапост про артефактные цвета в CGA, и мне нужно было сгенерировать таблицу возможных цветов и соответствующих им композитных сигналов.
                                                                                                                          Эта программа, скорее всего, никогда больше никому не понадобится.
                                                                                                                          По-вашему, я её всё равно должен был писать на языке, позволяющем «читать, понимать и развивать, в том числе и тем, кто ее первый раз в жизни видит»?
                                                                                                                            0
                                                                                                                            Да для себя можно писать на чем угодно равно как и заниматься любым извратом. Речь идет о коллегах, нынешних и потенциально будущих, а не о мастерах по созданию произведений write&dump.
                                                                                                                              –1
                                                                                                                              То, что я писал генератор для таблицы цветов на языке с динамической типизацией — это изврат?
                                                                                                                                0
                                                                                                                                Повторюсь — я писал про промышленное производство, а не про поделки для себя. Для себя можно хоть на заборе, хоть на brainfuck.
                                                                                                                                  0

                                                                                                                                  Нет, почему? Задача ж решена.


                                                                                                                                  Но я даже одноразовые поделки делаю на языках со статической типизацией, даже если они сводятся к «прочитать файл, взять все до 95-го перцентиля, скормить гнуплоту».

                                                                                                                            0
                                                                                                                            for...in — это самый удобный перебор в js.
                                                                                                                            У .foreach и for слишком много неудобств и ограничений.
                                                                                                                              +1

                                                                                                                              Простите, а вы точно знаете чем отличается for-of, for-in, for;;, forEach? и знаете про методы .entries, .keys, values?

                                                                                                                            +1
                                                                                                                            Здесь в js плохо то, что если работаешь с массивом, в котором элементы пронумерованы от 0 до N, и по-другомы быть никак не может, то ожидаешь что эта прогрессия будет иметь числовое выражение, а не строковое.

                                                                                                                            В JS ключи бывают только строковыми. То, что вы можете искать и по цифрам — это динамическая природа языка. Это отвечает на много вопросов)

                                                                                                                          +2
                                                                                                                          Это нормальное поведение тех же перловых регекспов со /g (привет PCRE):
                                                                                                                          $ perl -le 'my $a = q{abcabdabe}; print $1 while ( $a =~ /(ab.)/g );'
                                                                                                                          abc
                                                                                                                          abd
                                                                                                                          abe

                                                                                                                          Без /g этого не происходит.