Замыкания — это вообще основа для позиции, которую вы обозначили ;) Понимание, как они работают и почему — это самые азы для «крепкого JS программера».
Я чисто объективно предложил вам переименовать вакансию в более подходящую — «Фронтэнд разработчик».
Ну да, прикладной повседневный (возможно, поверхностный) JS, как дополнение к вакансии «Frontend developer». Я говорю, последнее определение больше подходит для вакансии. Вы из JS вообще мало, что спросили по пунктам (в сущности, если говорить о «крепком» JS программере — вообще ничего не спросили). С другой стороны — для прикладного эффективного Frontend программера, вопросы подходящие.
Вакансия в меньшей мере на JS программера (и тем более «крепкого»). Скорее, на «всё-в-одном фронтэндера»: верстальщика, дизайнера, чего-то там знающего про HTTP протокол, ну и JS вдогонку, чтобы всё это свёрстанное оживить.
Но в целом, для вакансии «всё-в-одном на фронтэнд позицию» может и подойдёт. Однако, прошедший все эти пункты кандидат, может быть весьма посредственным JS программером. Поэтому, возможно лучше изменить вакансию на «Frontend developer» — это будет подразумевать знания JS + всё перечисленное в пунктах.
Да, но это уже полноценные Function Expression (FE), которые будут созданы на этапе интерпретации кода. А в описанном выше случае, реализации трактуют функции, как Function Declaration (FD) и создают их на этапе входа в контекст (т.е. ещё до начала интерпретации кода), поэтому функция «а» будет доступна даже выше if-а. Firefox поступил логичней — воспользовался правом на расширение синтаксиса (см. 16. Errors ECMA-262-6) и ввёл Function statements ;)
Только надо быть осторожным даже при описании Function Expression в этом случае. В IE есть баг, когда FE содержит опциональное имя. В этом случае, Jscript (IE) трактует такие функции, как FD и создаёт их на этапе входа в контекст:
a(); // 2 - FD, созданная при входе
if (true) {
a = function a() {
alert(1);
}
} else {
a = function a() {
alert(2);
}
}
a(); // 1 - FE, созданная при интерпретации
Более того, IE создаёт в этом случае два объекта — и FD и FE. Подробней можно почитать здесь.
А, пожалуй именно возможность писать код после написания кода
Да, это непосредственно связано с динамикой языка ;) Можно менять объекты в рантайме, создавать/удалять/модифицировать свойства/методы, менять тела функций и т.д. Схожую идеологию (в некоторых аспектах) можно наблюдать во многих динамических языках, например, в Ruby или Python.
А безумие-то в чём? В смысле, конкретно — что-то понравилось или наоборот, что-то не понравилось, что-то не понятно? Или что? Я смутно уловил посыл статьи ;)
Как часто JS программисты задумываются над [function statement]?
А вот Function statement — это расширение Gecko для описания функций в блоках. Формально, функция не может быть описана в виде декларации в блоке, по грамматике должна быть ошибка. Но ни одна из реализаций не выбрасывает исключение, а обрабатывает этот случай. И только в Firefox (Gecko, *monkey) обработка этого случае наиболее логичная:
if (true) {
function a() {
alert(1);
}
} else {
function a() {
alert(2);
}
}
a();
Потестируйте в разных браузерах. Во всех, кроме Firefox, функция «а» будет выводить 2, даже, несмотря на то, что ветвь else никогда не выполнится. А дело в том, что реализации создают здесь Function Declaration. И только Firefox использует расширение синтаксиса, создавая правильно функцию по условию. Это расширение называется у них Function statement. Подробней можно почитать в конце этого раздела.
Поэтому, все эти виды функций имеют принципиальное отличие друг от друга.
Персональное безумие началось с «Вики-англичанки»: статя
про Javascript.
Там внизу есть Syntax and semantics — замечательный пример.
После этого «щелкнула» известная многим
набла.
И, главное — последующий
тред.
«Известная набла» неточная и содержит кучу недочётов.
И в частности этого: var My_Function_Name = function([parametrs]){… var My_Function_Name = new function([parametrs]){… очевидного смысла в последней конструкции нет совсем, но, может, есть неочевидный…
В первом случае создаётся функция (Function Expression), во втором — порождается объект от анонимного конструктора. Это два разных случая.
mix[i] = new Function(pf_T, pf_B);
Функции, созданные с помощью конструктора Function, имеют ряд особенностей, не всегда полезных (особенно в описываемом случае). Во-первых, их [[Scope]] содержит всегда только глобальный объект:
var a = 10;
function x() {
var a = 20;
var b = 30;
var y = new Function('alert(a); alert(b);');
y(); // 10, "b" is not defined
}
Видно, что в [[Scope]] функции «y» отсутствует объект активации контекста функции «x»: не доступна переменная «b», переменная «а» — берётся из глобального контекста, а не из обрамляющего.
А какие решения? И почему плохо? Связываются прототипы, тем самым наследуются. Что здесь не так? Это заложено в идеологию ES. Опять же, чтобы увидеть объектно-ориентированную суть и показать наследование, можно даже не писать какой-то сложный код с конструкторами, иерархией и т.д. Достаточно простейшего примера:
alert(1..toString()); // "1"
Пример показывает, что ES изначально пронизан наследованием и это — в его идеологии. Чтобы поддержать эту идеологию и реализовать её в своих объектах, нужно связать прототипы. Различные решения это и предлагают. Почему же они «плохо работают»?
> Не нужно пытаться всю логику приложения положить на «классы» с наследованием — этот подход идет вразрез с идеологией JS
А почему вразрез? Если цепь прототипов неизменна, то она вполне подходит по цепь наследования. Более того, как я отмечал, разница «класс vs. прототип» в определённых случаях может быть вообще не важна.
Для оптимизации, функцию F в данном случае можно создать один раз и использовать повторно.
> Кажется что весь пример состоит из бесконечного повторения одних и тех же имен, одних и тех же многословных конструкций.
Да, для этих целей обычно выносят этот повторяющийся код по связке прототипов в обёртку (в простейшем случае — в обычную функцию), что повышает абстракцию (мы не завязаны на конкретные имена конструкторов) и улучшает реюз кода.
> И сталкиваемся с тем, что, во-первых, загрязняется наследование свойств (это не легко «ухватить», но свойства получаются из последнего прототипа в цепочке, вместо нужного), а во-вторых и в главных, отрабатывает совершенно не нужная на этом этапе и потенциально ресурсоемкая инициализация.
Да, верно. Но эти причины не столь критичны, как например, проверка входного параметра в родительском конструкторе. Возможны случаи, когда подобным образом вообще не отнаследоваться. Например:
function A(param) {
if (!param) {
throw 'Param required';
}
this.param = param;
}
A.prototype.x = 10;
var a = new A(20);
alert([a.x, a.param]); // 10, 20
function B() {}
B.prototype = new A(); // Ошибка
Поэтому, опять же, для этих целей использую обычно методику, предложенную Lasse Reichstein Nielsen с промежуточным пустым конструктором, который и свяжет прототипы. Объект для наследования создаётся именно от этого пустого конструктора, что позволяет избавится от недочётов, описанных выше.
> При всем обилии материалов по данному вопросу, я просто не смог найти в сети достаточно полного обобщающего анализа
Да хватит уже эти фразы слащаво-шаблонные повторять, все всё понимают. Автор лишь анализирует и пытается классифицировать подходы — и в этом, разбирается в ООП в JS.
> автор был уверен что редактирует черновик, пока не увидел эти комментарии. Каким образом автор нажал на кнопочку опубликовать, для самого автора остается загадкой
Говорите о себе в третьем лице? =) Признак мании величия. Шутка ;)
> Человек, возможно не очень хорошо знающий нюансы языка
Ну так а в чём проблема? Значит надо садиться и изучать язык.
Это как, например, математика. Есть школьный уровень. Есть уровень техникума. Университета. Академический уровень.
Так вот, скажите мне, — с какой стати я должен писать на уровне математики 6 класса школы, чтобы код был понят каждым новичком после прочтения книги «Этот занимательный Javascript»? И это не относится сейчас к конкретному случаю с «x++» или «x += 1».
Естественно, основным критерием, в общем случае, должна являться простота чтения кода. Но только не надо перебарщивать, приводя аргументом, что код не поймут новички, которые только прочли первую главу книжки.
В частных случаях, где критерием может быть максимальный выжим ресурсов, могут использоваться нестандартные хакерские способы — но здесь нечитаемость будет уже оправдана.
Дедушка Крокфорд, действительно, внёс немалый вклад в JS (в основном, прикладного характера). Но только вот не делайте из него «икону», а? Он далеко не безупречен в JS, тоже ошибается, местами знает JS поверхностно, и мысли его порой субъективные.
Если вы будете постоянно «полагаться на истину авторитета, вместо того, чтобы полагаться на авторитет истины, вам будет нелегко» донести свою точку зрения.
Иногда не только экономия размера кода имеет место быть, но и, также, экономия в скорости, как, например, в преобразовании строкового представления в число. Когда однозначно известен формат числа (включая систему счисления), то часто быстрей и удобней использовать ведущий плюс для преобразования (в альтернативу, например parseInt)
Ой, да ладно, минусовать-то сразу ;) Я хотел сказать, что не нужно её воспринимать как «спасение» от «выдуманных проблем». В целом же — тулза, конечно, полезная.
Забавный тест ;)
Ещё как варинат: (для квадратных скобок суть меняется):
!+[0]
!-[1]
Я чисто объективно предложил вам переименовать вакансию в более подходящую — «Фронтэнд разработчик».
Однако, решать вам ;)
Успехов.
Ну да, прикладной повседневный (возможно, поверхностный) JS, как дополнение к вакансии «Frontend developer». Я говорю, последнее определение больше подходит для вакансии. Вы из JS вообще мало, что спросили по пунктам (в сущности, если говорить о «крепком» JS программере — вообще ничего не спросили). С другой стороны — для прикладного эффективного Frontend программера, вопросы подходящие.
Но в целом, для вакансии «всё-в-одном на фронтэнд позицию» может и подойдёт. Однако, прошедший все эти пункты кандидат, может быть весьма посредственным JS программером. Поэтому, возможно лучше изменить вакансию на «Frontend developer» — это будет подразумевать знания JS + всё перечисленное в пунктах.
Не стоит. Object.prototype.toString — особенный ;)
Только надо быть осторожным даже при описании Function Expression в этом случае. В IE есть баг, когда FE содержит опциональное имя. В этом случае, Jscript (IE) трактует такие функции, как FD и создаёт их на этапе входа в контекст:
Более того, IE создаёт в этом случае два объекта — и FD и FE. Подробней можно почитать здесь.
Да, это непосредственно связано с динамикой языка ;) Можно менять объекты в рантайме, создавать/удалять/модифицировать свойства/методы, менять тела функций и т.д. Схожую идеологию (в некоторых аспектах) можно наблюдать во многих динамических языках, например, в Ruby или Python.
Это два разных типа функций, с принципиальными отличиями.
Лучше не путать терминологию. В ECMAscript существует три вида функций: декларации функции (Function Declaration), функции-выражения (Function Expression) и функци, созданные конструктором Function.
А вот Function statement — это расширение Gecko для описания функций в блоках. Формально, функция не может быть описана в виде декларации в блоке, по грамматике должна быть ошибка. Но ни одна из реализаций не выбрасывает исключение, а обрабатывает этот случай. И только в Firefox (Gecko, *monkey) обработка этого случае наиболее логичная:
Потестируйте в разных браузерах. Во всех, кроме Firefox, функция «а» будет выводить 2, даже, несмотря на то, что ветвь else никогда не выполнится. А дело в том, что реализации создают здесь Function Declaration. И только Firefox использует расширение синтаксиса, создавая правильно функцию по условию. Это расширение называется у них Function statement. Подробней можно почитать в конце этого раздела.
Поэтому, все эти виды функций имеют принципиальное отличие друг от друга.
«Известная набла» неточная и содержит кучу недочётов.
Если заинтересовались Javascript-ом глубоко, могу порекомендовать эти статьи — javascript.ru/ecmascript-in-detail.
В первом случае создаётся функция (Function Expression), во втором — порождается объект от анонимного конструктора. Это два разных случая.
Функции, созданные с помощью конструктора Function, имеют ряд особенностей, не всегда полезных (особенно в описываемом случае). Во-первых, их [[Scope]] содержит всегда только глобальный объект:
Видно, что в [[Scope]] функции «y» отсутствует объект активации контекста функции «x»: не доступна переменная «b», переменная «а» — берётся из глобального контекста, а не из обрамляющего.
А какие решения? И почему плохо? Связываются прототипы, тем самым наследуются. Что здесь не так? Это заложено в идеологию ES. Опять же, чтобы увидеть объектно-ориентированную суть и показать наследование, можно даже не писать какой-то сложный код с конструкторами, иерархией и т.д. Достаточно простейшего примера:
Пример показывает, что ES изначально пронизан наследованием и это — в его идеологии. Чтобы поддержать эту идеологию и реализовать её в своих объектах, нужно связать прототипы. Различные решения это и предлагают. Почему же они «плохо работают»?
А почему вразрез? Если цепь прототипов неизменна, то она вполне подходит по цепь наследования. Более того, как я отмечал, разница «класс vs. прототип» в определённых случаях может быть вообще не важна.
> function F() {}
Для оптимизации, функцию F в данном случае можно создать один раз и использовать повторно.
> Кажется что весь пример состоит из бесконечного повторения одних и тех же имен, одних и тех же многословных конструкций.
Да, для этих целей обычно выносят этот повторяющийся код по связке прототипов в обёртку (в простейшем случае — в обычную функцию), что повышает абстракцию (мы не завязаны на конкретные имена конструкторов) и улучшает реюз кода.
> И сталкиваемся с тем, что, во-первых, загрязняется наследование свойств (это не легко «ухватить», но свойства получаются из последнего прототипа в цепочке, вместо нужного), а во-вторых и в главных, отрабатывает совершенно не нужная на этом этапе и потенциально ресурсоемкая инициализация.
Да, верно. Но эти причины не столь критичны, как например, проверка входного параметра в родительском конструкторе. Возможны случаи, когда подобным образом вообще не отнаследоваться. Например:
Поэтому, опять же, для этих целей использую обычно методику, предложенную Lasse Reichstein Nielsen с промежуточным пустым конструктором, который и свяжет прототипы. Объект для наследования создаётся именно от этого пустого конструктора, что позволяет избавится от недочётов, описанных выше.
> При всем обилии материалов по данному вопросу, я просто не смог найти в сети достаточно полного обобщающего анализа
Могу порекомендовать Тонкости ECMA-262-3. Часть 7. ООП.
В целом, спасибо за статью и анализ.
Говорите о себе в третьем лице? =) Признак мании величия. Шутка ;)
Ну так а в чём проблема? Значит надо садиться и изучать язык.
Это как, например, математика. Есть школьный уровень. Есть уровень техникума. Университета. Академический уровень.
Так вот, скажите мне, — с какой стати я должен писать на уровне математики 6 класса школы, чтобы код был понят каждым новичком после прочтения книги «Этот занимательный Javascript»? И это не относится сейчас к конкретному случаю с «x++» или «x += 1».
Естественно, основным критерием, в общем случае, должна являться простота чтения кода. Но только не надо перебарщивать, приводя аргументом, что код не поймут новички, которые только прочли первую главу книжки.
В частных случаях, где критерием может быть максимальный выжим ресурсов, могут использоваться нестандартные хакерские способы — но здесь нечитаемость будет уже оправдана.
Дедушка Крокфорд, действительно, внёс немалый вклад в JS (в основном, прикладного характера). Но только вот не делайте из него «икону», а? Он далеко не безупречен в JS, тоже ошибается, местами знает JS поверхностно, и мысли его порой субъективные.
Если вы будете постоянно «полагаться на истину авторитета, вместо того, чтобы полагаться на авторитет истины, вам будет нелегко» донести свою точку зрения.
Иногда не только экономия размера кода имеет место быть, но и, также, экономия в скорости, как, например, в преобразовании строкового представления в число. Когда однозначно известен формат числа (включая систему счисления), то часто быстрей и удобней использовать ведущий плюс для преобразования (в альтернативу, например parseInt)