Pull to refresh

Comments 61

очень не хватает статьи китайца, который будет рассказывать о проблемах связанных с вилкой.
Похожим образом много пишут в тайпскрипте, в том смысле что его компилятор так написан много где.
Когда в функции объявляются переменные а потом гора вложенных функций используется. По сути как инстанс класса со своими полями, только всё приватно и вложено тут.
Пример. Другие примеры не отображаются на гихабе ввиду огромности размера файла.
https://github.com/Microsoft/TypeScript/blob/master/src/compiler/tsc.ts#L99
И работая с таким кодом повседневно, я не скажу что это лучшее решение. Хотя в некоторых местах катит. Но расширяемость и переиспользуемость сильно страдают. Иногда вынужден копипастить куски из компилера вместо того чтобы позвать.

А ещё где-то два года назад код без this перестал быть быстрее кода с ним.

UFO just landed and posted this here
Это точно. Очень жаль, что разрабы не придумали чего-то подобного для super.

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

А вы думаете он знает секрет идеального кода?

Действительно, ведь делегирование в JS не нужно, bind, call и apply придумали неизвестно для кого, а new — вообще бесполезно использовать
UFO just landed and posted this here

Крокфорд как-то ответил на ваш вопрос: в JS есть неоднозначные вещи, использование которых требует дисциплины и сакрального знания. this — одна из таких вещей (требует постоянно держать в уме контекст использования). То что требует повышенной дисциплины подвержено поломкам в результате человеческого фактора. Такие дела.


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

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

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

Теперь подумайте вот о чем:
1. На каждый экземпляр объекта созданного таким образом будут создаваться новые функции-методы. Это засоряет память, это убивает оптимизацию
Функции в js компилируются в момент первого вызова и оптимизируются при нескольких последующих вызовах. В случае Вашего подхода это будет делаться для каждого инстанса, что дорого, очень
Если же использовать правильный подход с прототипами функции будут созданы и скомпилированы только 1 раз, все инстансы будут использовать одну и ту же функцию. Мы экономим память. Инстанс создается быстрее. Инстанс работает быстрее, когда его методы уже скомпилированы при работе с предыдущим инстансом.
2. Как быть с наследованием? Допустим я хочу на базе Вашего двухсвязного списка сделать список с произвольным доступом на чтение и возможностью итерации. Если бы были прототипы — я бы относледовался от Вашего прототипа и добавил бы необходимые мне методы, но Ваш код я не смогу переиспользовать, это плохо

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

UFO just landed and posted this here

Замыкания, вопреки всеобщему заблуждению, — не функции. Это — структуры, хранящие две ссылки: на контекст и на собственно функцию (исполняемый код). Функция создаётся единожды при парсинге исходников.

UFO just landed and posted this here
Чудесно. А причём здесь замыкание?
UFO just landed and posted this here
Вы путаете функцию и замыкание.
UFO just landed and posted this here
капец как внезапно…

typeof null // object (баг в ECMAScript, должно быть null)
typeof undefined // undefined

источник: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/null
UFO just landed and posted this here
Замыкание — это внутренний объект движка. Он не тождественен функции. Я лично, не знаю, что Вам показать, но может, кто-то из присутствующих копался в потрохах движка и знает способ увидеть замыкание непосредственно.
UFO just landed and posted this here
Там никакого специального объекта нет.

Боюсь ошибиться, но кажется именно из спеки я о нём и узнал.
UFO just landed and posted this here

Как вы думаете, что означает слово "let" в данном контексте?


То, что мы привыкли называть "замыканием" в спеке называется "объект функции", а собственно код функции находится в скрытом свойстве [[Code]] этого объекта и он общий для всех замыканий из одного места в исходниках: http://www.ecma-international.org/ecma-262/5.1/#sec-13.2

UFO just landed and posted this here

Вы на вопрос ответьте.

Ну и развели же спор, попробуйте открыть консоль и выполнить функцию makeAdder из статьи.
А потом вот это
makeAdder(1) === makeAdder(1)

странно что по вашему мнению одна и та же функция возвращаемая при выполнении не равна сама себе

Это объект функции. Сама функция как последовательность операторов — используется повторно.

Но ведь в данном случае инстансы не содержат функций. Вся реализация собрана в одном месте, а в инстансах только данные.
UFO just landed and posted this here
Вот здесь подробно про this и замыкания https://habrahabr.ru/company/hexlet/blog/266443/
Отказом от использования this, объекты в JS легким движением руки превращаются в структуры из C.
Никаких методов. Никаких конструкторов.

И стиль из ООП превращается в процедурное программирование
Они скорее ближе к ассоциативным массивам, чем к структурам.

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

Чёт больше похоже на: Не знаю ООП, использовать не хочу, разбираться в тонкостях языка не хочу, буду делать так, как получается.
Например — стрелочные функции и привязка this. Как результат, при разработке можно практически полностью обойтись без this.

Вообще-то если не обращаться к this, то стрелочная функция ровно ничем не отличается от обычной:)


Дальше можно не читать, в общем.

стрелочная функция еще не имеет своей arguments, а наследует его из замыкания

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


Но, повторюсь, если кто-то ниасилил this в JS по десяткам статей, появляющихся каждый год, то разница минимальна.

Э… Я вижу просто явную передачу this в качестве аргумента функции (вместо обычной неявной), и всё. Что изменилось, кроме увеличения размера кода?

Не понял, что нового в статье. Так писали 7-8 лет назад (а может и раньше), чтобы не заморачиваться с наследованием и чтобы симулировать private. Особенно такой стиль любят выходцы из С#/С++. На сегодня, с приходом классов, этот стиль уходит. Возможно в одном из следующих релизов можно будет декларировать в качестве члена класса что нибудь типа стрелочных функций с привязкой this

Уже можно через class properties:


class Foo {
  bar = 'bar';
  bound = () => console.log(this.bar);
}

Очень часто используется в React.

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

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

Но в случаи с связыванием контекста не дублируется логика находящаяся в функциональном выражении.

Пример того как писать неподдерживаемый/корявый код.

из функции можно возвратить объект. Этот объект будет иметь доступ к локальному окружению

Это неверно. Объект ни к чему доступа иметь не может в принципе. Доступ к скоупу будут иметь только функции, в нём созданные. Всё. Они могут быть методами объекта, который вы вернёте, но сам объект тут ни при чём. Если вернуть объект вида { foo: 1 }, скоуп почистится сборщиком мусора. Если присвоить в качестве метода объекта функцию, объявленную снаружи, доступа к скоупу у неё тоже не будет.
шило на мыло… если сложно javscript-программисту использовать this, то в реальном проекте с кучей вот таких абстракций — получится работа кода проекта не более предсказуемо чем с this.
Вы посвятили параграф рассказывая про замыкания, но конкретно в вашем примере нет никакого смысла в замыкании.

Тут не имеет смысла ничего замыкать, модуль в котором вы пишите этот код делает это за вас:
module.exports = LinkedListDeque = (function() {
  let Node = {
    next: null,
    prev: null,
    data: null
  };
  let Deque = {
    head: null,
    tail: null,
    length: 0
  };
 // тут нужно вернуть общедоступное API
})();

Этот код, во всех случаях, будет работать так-же:
let Node = {
  next: null,
  prev: null,
  data: null
};
let Deque = {
  head: null,
  tail: null,
  length: 0
};
module.exports = {
  // тут нужно вернуть общедоступное API
};
Были минусы, и наверное, они по делу, я не удосужился объяснить детальнее. Посмотрите на код внимательнее. Это не фабрика, это не функция конструктор, это анонимная функция, которая сразу же, при запросе модуля, вызывается, и результат работы которой присваивается переменной LinkedListDeque, которая потом экспортируется. Замыкание которая эта функция создаёт, не несет никакого смысла к контексте модулей commonJS которые тут используются. Оно не предотвращает side effects и не инкасплуриует какой-либо функционал.
Писать в таком стиле есть смысл, только, если помимо этого кода, в файле модуля есть еще какой-то функционал, от которого следует изолироваться, что уже было бы неправильно с точки зрения cohesion элементов внутри модуля.
Статья приводит хороший пример из функционального программирования, в частности, о преимуществе pure functions. К основному примеру, замыкания описанные выше, не имеют никакого отношения. Но этот пример, мог бы отлично продемонстрировать то, как можно применить каррирование, которое является отличным примером паттерна функционального программирования, который может помочь избавиться от this.
Краткий курс по основам ООП, но без ООП? ))
На самом деле это паттерн модуль + паттерн открытия модуля.
Такой подход позволяет работать и с this (он есть в замыкании, просто в своем коде вы его не применяете).
Такой подход хорош для эмуляции защищенных свойств и методов. Но отсутсвие вынести общее поведение в прототип приводит к тому, что каждый инстанс расходует больше памяти, нежели работа с цепочкой прототипов.

Еще в примере кода возвращается инстанс, лучше возвращать конструктор. Чтобы можно было создать свой инстанс, если потребуется более 1 инстанса модуля и провайдить настройки в конструктор, если модуль настраиваемый.
Как раз недавно завёл issue. https://github.com/Microsoft/TypeScript/issues/17680

Посмотрите например на такой код:
https://github.com/Microsoft/TypeScript/blob/ceae613e4c0ba36829a2381687883ecdc6b169c3/src/services/services.ts#L1056
Такой код не расширяем и не переиспользуем от слова совсем. Есть примеры лучше, но там гитхаб не хочеть отображать большой файл.
Sign up to leave a comment.