Pull to refresh

Comments 158

А объясните мне кто-нибудь — зачем это спрашивать на собеседовании?
Задачки, кстати, тривиальные.
Чтобы понять, знаком ли человек именно с Javascript, или только с jQuery API. И насколько хорошо знаком.
Т.е. знать JavaScript == быть в курсе, каков порядок инициализации переменных и функций?
А у вас в проекте где-то есть код, где это важно?
В том числе – да. Этот самый Hoisting, при незнании, может много времени на дебаг отнять… Некоторые, например, ещё любят объявлять переменные\функции внутри блоков if..else.

Лично я когда собеседовал на JS-вакансии спрашивал в первую очередь про понимание разницы между == и ===, про замыкания, hoisting ну и дальше уже по мелочи.

С другой стороны, даже если в проекте подобного кода нет, такие вопросы позволяют в какой-то степени оценить опыт разработчика.
UFO just landed and posted this here
Допустим, это возможно на каком-то отдельном проекте. Но вопрос-то про собеседование. Цель – оценить опыт человека, понять, сможет ли он работать сразу, или будет по каждой мелочи спрашивать «а как оно работает?».
Смотри предыдущие работы человека.
Всё, по другому никак.
Я нескольких нулевых ребят учил проходит такие интервью, и они учились дальше прямо на работе. Никто ничего так и не заметил.
Безусловно, другие работы тоже имеют значение. На какие-то очень крутые вакансии даже можно давать собственное тестовое задание (хотя лично я только 1-2 раза брался за тестовое задание, т.к. обычно было много других предложений, без него).

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

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

Если речь идет о стартапе, где всё сейчас и срочно, то ещё возможно. И то примеры нужны не как в статье, а более очевидные — потому что именно такие будут в коде. Если в проекте встречается повсеместно то, что в посте, и надо всё-всё срочно, то лучше отказаться от работы в такой компании, насколько бы привлекательно это не было.
Ну вот видите, и для собеседуемых, подобных вам, такие вопросы полезны в какой-то степени. Позволяют оценить компанию.
По поводу таких вопросов — я на собеседовании также такие вопросы задаю, но лишь чтобы услышать как человек думает и рассуждает. Не всегда обязательно ответить. Почему я их задаю: когда человек может на такое ответить — он сам сможет создать стайл гайд, а не будет выучивать правила. На проекте часто нет времени на то, чтобы апдейтить стайл гайд увидев очередной «перл» гугл-драйвен девелопмента. Человек сам должен осознавать, почему это включено в гайд и почему так не стоит делать.
И еще, на больших проектах не всегда все медленно и спокойно. Разработка всегда зависит от потребностей бизнеса. А в бизнесе важно вовремя выпустить продукт.
Именно что первично это выпускать продукт :-)
Очень часто в крупных проектах делают «не то» годами, не говоря уже про какой-то там месяц. Это издержки масштаба.
Чего тут учить. Открываешь Флэнагана и заучиваешь. Фактически, как экзамен сдать.
Надо ли уточнять, что сдать экзамен != наличию знаний по предмету?
Весь тред я и пытаюсь это донести norlin'у :-)
UFO just landed and posted this here
Ок. Что делать, если нет хорошего кодстайла? Что делать, если проект только стартует и набирают команду? Как вообще оценить навыки человека? А если у него нет кода, который может показать?
1) Узнать, почему у него совсем нет кода, который может показать.
2) Забить на код и смотреть на РЕЗУЛЬТАТ его работ.
1. Ок, весь код под NDA. Что дальше?
2. То есть, принять на работу вслепую и надеяться на лучшее? Вы оптимист! Давайте я к вам приду на 150к в месяц, поговнокожу, получу 150к и уйду?
1) Смотреть на результат работ, а не на код. Пишу в 4й раз уже.
2) При чем тут в слепую? Можно нормально пообщаться и понять подходит ли вам кандидат.

Например у меня почти весь код под NDA, однако это не мешает мне рассуждать как можно решить тот или иной класс задач. При этом не показывая код. Код и особо нужен, на самом деле. Это как просить сантехника гаечный ключ показывать. Можно же узнать о репутации человека, позвонить по предыдущим местам работы и т.п. Способов масса, зачем зацикливаться на коде вообще?
1. Вот этого я никак не пойму. Как можно вслепую взять человека, и лишь потом оценивать его? Это ещё возможно на Junior позициях, но никак не на полноценного разработчика, не говоря уже о Senior.
2. Как это «нормально пообщаться», не задавая технических вопросов? За кружкой пива, что ли: «свой кореш – берём!»?

С этим я и не спорю, множество раз тут написал – эти вопросы лишь часть собеседования.
зачем зацикливаться на коде вообще?
Уже откровенный троллинг, погляжу. Ок, спасибо за дискуссию.
1. Ещё раз. Не в слепую, а посмотрев на его результат работы.
2. Не стоит перескакивать с темы на тему. Технические вопросы — не обязательно вопросы про то, как код писать. Обычно достаточно концептуального описания как решать ту или иную задачу.

Никакого тролинга, вам ниже уже писали схожее. Я говорил о том, что кроме кода есть масса критериев отбора.
Пожалуйста.
1. Откуда возьмётся «результат» до приёма человека на работу? Или предлагается оценивать некий готовый продукт с предыдущего места? А если там работало несколько десятков человек? А если наш кандидат исключительно серверный код писал?
2. Не «обычно», а «иногда». Иногда достаточно. А иногда не достаточно и нужно, чтобы человек знал, как задачу решить не «концептуально», а на конкретном языке.

Я говорил о том, что кроме кода есть масса критериев отбора.
Я это чуть ли не в каждом комменте твержу. Но не задавать вопросы по коду на собеседовании разработчика – это ОЧЕНЬ странный подход. Возможен, только когда вообще нет никого, способного оценить его ответы (и нет никого, кто мог бы погуглить подобные задачки с формальными ответами).
1) Да, по предыдущим местам. По картине в целом. По отзывам о этом человеке с предыдущих мест.
2) Думаю что это только для стартапов актуально, в которых срочность превыше всего.

Я видел что ты пишешь в других комментариев, но, всё же, создается впечатление что это один из основных криериев. Можно тогда уж с такими вопросами давать человеку доступ к интернету и смотреть как быстро он найдет ответ :-)
Безусловно, при приёме на работу программиста, одним из важных критериев является знание кандидатом языка программирования. Вряд ли кто-то возьмёт на вакансию Javascript профессионала в C++, который JS первый раз видит.
На удивление я подобное встречал далеко не один раз и далеко не в маленьких компаниях.
Точнее, в маленьких я такого не встречал, только в крупных.
UFO just landed and posted this here
Очевидно, надо почитать лучшие наработки и ввести кодстайл.
Спасибо, Кэп. А кто будет читать наработки и вводить кодстайл, если нет спецов?

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

Например, дать тестовое задание
По себе скажу, что тестовое задание делать очень лениво. Обычно проще пойти на другое собеседование. Надо очень вкусные условия предлагать, либо иметь большой поток желающих.
В первом случае, почему бы и не задать «каверзные» вопросы.
Во-втором, возможно, это вакансия Junior, куда чуть ли не любой кодер может подойти. Там да, имеет смысл не про Hoisting спрашивать, а давать задачки на общее мышление\любознательность и т.д.
UFO just landed and posted this here
Вы как собираетесь людей нанимать
Вот подобные вопросы и позволяют оценить кандидатов «в данной области».
Ещё раз, на всякий случай – не только такие вопросы. Но как часть оценки – очень полезны.
> Спасибо, Кэп. А кто будет читать наработки и вводить кодстайл, если нет спецов?
А кто будет тогда контролировать ваши заковыристые задания? :-D

Это вопросы далеко не на 150к в месяц :-) Если к вам придёт действительно крутой специалист, то после непродолжительной беседы без единой строчки кода вы уже поймёте, что все эти вопросы нет смысла задавать. Нужны они лишь случае сомнений в компетентности вида «а не Junior ли это под Middle косит?»
Именно! Я уже много раз тут написал – эти вопросы далеко не единственные. Да, возможна ситуация, когда окажется, что их нет смысла задавать (причём, с обеих концов – либо кандидат очевидно крутой, либо очевидно ничего не знает).

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

Logger = function(logFn) {
    
    _logFn = logFn;

    ...
}

в котором объявляется по-сути глобальная переменная _logFn
м… ну, знать про function declaration все же надо
У меня долгое время на при виде таких задач всплывала в голове фраза «Не надо писать странный код», но потом я понял для себя две вещи:
  • Частенько нам приходится работать с чужим, не всегда годным, кодом, и чем круче скилл «компилирования кода в голове» тем проще проходит этот процесс
  • Такие вопросы могут показать опыт человека, ибо если человек при виде странного кода сходу говорит, что тут не так и как это исправить — скорее всего он когда-то уже так напоролся и не поленился разобраться в вопросе
Буквально позавчера соискатель с готовностью отвечал на вопрос: что лучше Jquery или angular.
Вы хотели сказать «что хуже»? :-)
UFO just landed and posted this here
> Функции объявленный при помощи function declaration имеют больший приоритет и понимаются выше var. Поэтому интерпретатор сначала выполнит function x() {};, а затем var x = 1;

Вообще-то интерпретатор выполнит сначала function x() {};, затем var x; и только потом x=1;
Ну впринципе справедливо, хотя в контексте этой задачи большой роли не играет.
+1, тоже зацепился за этот момент.
Декларация переменных и их инициализация проходят отдельно хотя это одна синтаксическая конструкция.
В данной задаче может и не играет роли, но можно придумать такую, где играет.
Да пожалуйста:
(function() {
    x = 1;

    function x() {};
    var x;
    
    console.log(x);	
})()

Тут ещё можно теперь спросить доступна ли «x» снаружи.
Вообще-то, var x будет «выполнен» одновременно с function x() {}. Слово «выполнен» я взял в кавычки, потому что объявления не выполняются.
Вообще-то интерпретатор выполнит сначала function x() {};, затем var x; и только потом x=1;


Вообще-то интерпретатор _сначала_ выполнит var x; потом function x() {} и только потом x = 1.

Both function declaration and variable declarations are hoisted to the top of the containing scope. And function declaration takes precedence over variable declarations (but not over variable assignment)
Такие задачи решаются их недопущением в проекте.
Я обычно именно так и отвечаю на собеседованиях, с комментариями, как избежать такого написания.
Разбирать их смысла нет.
Ну и ещё момент, что оно дебажится за минуту и переписывается по нормальному.
Заморачиваться заковыристой компиляцией в голове не стоит — лучше подумать о более высокоуровневых моментах.
Оно дебажится за минуту, если человек знаком с такими особенностями. Если же нет, он может кучу времени убить, пытаясь понять в чём проблема.
Допустим, человек потратит 3 часа на то, чтобы разобраться с этим в первый раз — в итоге разберется. Тогда весь смысл 10*n собеседований может свестись к найму человека, который вам эти 3 часа сэкономит. Чушь, на мой взгляд. Не лучше ли выяснить насколько человек целеустремленный и любознательный, насколько быстро перестраивается и какие у него вообще цели и ценности?

Хотя здесь конечно важны цели нанимателя тоже. Если вы рекрутинговое агенство и вам нужно продать человека поскорее, либо вам нужен кодер на 2 недели — тогда — да. Почему бы и нет.
Не лучше ли выяснить насколько человек целеустремленный и любознательный
А одно другому не мешает.
Просто иногда нужен любознательный и целеустремлённый человек, который УЖЕ разобрался с большинством аспектов, а не только-только начинает осваивать JS.

Более того, если человек «целеустремленный и любознательный» – он наверняка потрудится заранее узнать особенности языка. Хотя бы, ради собеседования.

Честно, некоторые знают только $(...).show/.hide и $.ajax. И когда начинаешь спрашивать, оказывается, что не понимают ни ==/===, ни о замыканиях не могут рассказать/показать (хотя бы насчёт замыканий, надеюсь, вы согласитесь, что это часто используемая «фишка»)…
Ну на самом деле, как выше уже писали, человек может не знать ответов на вопросы, но узнает всё за 1 рабочий день. При этом он высококлассный специалист. И вот из-за какого-то глупого вопроса есть шанс его упустить. Это не круто.
Лучше смотреть предыдущие работы. Причем можно даже не код, а результат.
Ниже ответил – никто не говорит, что подобные задачки это единственный критерий.
Не, ну я согласен на какие-либо базовые вопросы — замыкания, наследования и т.п., но вот всякие «хитрые» случаи вообще ничего не показывают.
Да и даже спрашивая что-то совсем базовое, можно сильно ошибиться, если давно разрабатываешь на значительно более высоком уровне, чем решение проблем с замыканиями, и просто «подзабыл» данный момент, но найти ответ займет пару минут.
Это тоже может иметь значение. Бывают задачи, где надо работать именно с «низкоуровневыми» вещами.

Никто не говорит, что это единственный критерий. Просто подобные вопросы помогают оценить реальный опыт. Если человек не знает, или не помнит – можно оценить хотя бы его рассуждения, настойчивость\целеутстремлённость и всё-такое.
Один только лишь увидев подобное сразу скажет «нет, не знаю, давайте дальше».
А другой попытается понять, даже если не встречал такую конструкцию раньше.

Первый на данном вопросе пролетит полностью, а второй получит плюс, даже если ответит лишь с помощьбю наводящих вопросов. Или даже если не ответит, но покажет, что умеет логично мыслить.
Низкоуровневые вещи != гавнокод :-)
Те ситуации, что описаны в посте, должны избегаться не зависимо от уровня.
Вопросы из поста – это тестовые задания. Искуственные куски кода, специально написанные, чтобы проверить, знает ли человек тот или иной аспект языка. Речь про собеседование, а не про «хороший и качественный js в проекте».
Это плохие тестовые задания. Они тебе покажут ничего.
Можно научиться проходить такие «задачи» даже не понимая сути вещей.
Я обучал нескольких ребят проходить интервью ещё когда они не были даже junior разработчиками. И именно благодаря подходу выше их брали, уверенными что человек всё знает, когда это было не так. Тем не менее на работе быстро изучали что надо, и это не было замечено никак.
Они все, в принципе, схожи, и ходят вокруг одних и тех же принципов.
Я уже сказал выше, что они мне показывают. Зачем вы пытаетесь доказать обратное, если оно мне уже помогало на собеседованиях?

Я обучал нескольких ребят проходить интервью ещё когда они не были даже junior разработчиками.
А вот это как раз опровергает ваши доводы. Если человек, освоивший хотя бы такие «плохие» тестовые примеры уже может работать с этим языком – значит эти примеры как раз и проверяют в той или иной степени знание языка.
Тесты на IQ отражают способность человека проходить тесты на IQ и не более. Тут тоже самое.
Разница в том, что если человек поймёт замыкания, приведение типов, function declaration, hoisting и т.д. – он по-определению будет знать JS.

Возможно, у него не будет реального опыта – но это уточняется уже другими средствами (резюме, примеры работ, тестовые задания, задачки на архитектурные вещи и т.д.)
Да ладно тебе.
Это всё «понимается» за день изучения. Какие тут реальные знания то?
Понимание этого всего не спасает от плохого кода, ошибок такого рода и т.п.

Неужели 1 день изучения темы является критерием опытного специалиста? Ну это печально тогда.

Могу предложить вариант такой: спрашивать у человека какую библиотеку он использует, потом открывать её код, и просить объяснить как она устроена и работает. Вот это может сработать ещё как-то.
Более того, если найдётся человек, впервые увидевший JS, который за один день поймёт замыкания – уверен, любая команда будет счастлива заполучить его себе (как Junior, который с такими-то способностями уже через месяц-два станет полноценным разработчиком).

Понимание этого всего не спасает от плохого кода, ошибок такого рода и т.п.
Эээ… Значит, это было не понимание, а тупое заучивание шаблонов.
Тут согласен :-) Но момент в том, что можно упустить достаточно хорошего специалиста, не разбиравшегося глубоко в данных вопросах, либо который просто забыл как правильно, но при доступе к интернету даст быстрый ответ.
Так я про это и пишу, ура!
Я немного не то имел в виду про «заучивание». Если человек тупо заучил подобные шаблоны – это не спасёт его от ошибок. Если же он реально понимает такие моменты – то спасёт. В худшем случае, это будет не ошибка, которую надо дебажить, а «опечатка», исправленная за минуту.
Я говорю о заучивании без понимания. Собеседования, о которых говоришь, могут быть пройдены заучиванием без понимания.
Вспомни школьных зубрил, которые преподавателю отвечают на ура, а ты, со своим глубоким пониманием, иногда, плаваешь. В этом и суть.
Я уже несколько раз тут писал, что подобные вопросы – это далеко не всё собеседование. Лишь его часть. Да, они в какой-то степени могут быть пройдены заучиванием. И то, код-то в таких вопросах может быть разным.

В школе как раз и заметил, что если понимаешь – то проблем ответить никаких нет. Пусть даже своими словами. А если не понимаешь, но считаешь, что знаешь лучше всех – тогда да, «зубрилы» будут впереди.
Если понимаешь — то всегда есть сомнения.
Если не понимаешь, то сомнений нет.
Тут почти как с религией.
Программирование почти как религия. Докатились… Спасибо, буду теперь знать, как отсеивать подобных кандидатов, если снова придётся кого-то собеседовать.
Очень здорово, наверное, слова собеседника передёргивать.
Обрати внимание на контекст, пожалуйста.
Очень интересно в чём именно заключалось обучение.
UFO just landed and posted this here
Именно так, + тебе.

Но, на самом деле, это не очень хороший подход. Тебя могут нанимать именно что бы исправить ситуацию, а ты поводишь носом и скажешь «Фу, фу, фу».
Хотя при таком подходе было бы как раз правильно спрашивать как это переоформить и как избежать подобных проблем :-)
Тут проблема в том, что собеседование должен проводить реальный спец, а его может и не быть. Точнее, если он есть, то как он сам всё это допустил? :-)
Мне больше нравятся задачи на собеседовании, которые помогают понять как человек будет решать проблемы, с которыми он будет сталкиваться на этой работе.
Пример «реализовать элемент прогресс бара 200px, заполняется за 5 сек», последущий вопрос «реализовать кнопку, добавляющую на страницу элемент прогресс бара, который начинает анимироваться сразу как добавлен», и последующее: «модифицировать этот код так, чтобы ново-добавленый прогресс бар, начинал анимацию, только когда закончиться анимация предыдущего»
Задача приближенная к боевой, и раскрывает разные стороны работы javascript программиста.
Лучше посмотреть на предыдущие работы :-)
Никакие задания всё равно не покажут как будет человек на данном месте работать.
Ну это я за себя говорю, когда я собеседовался, мне дали такую задачку.
А как посмотреть на предыдущие работы? Если весь код проприетарный?
Ну прямо весь-весь код редко у кого бывает под НДА, хотя бы потому, что у НДА, обычно, ограничение по времени ~3 года.
Если уж прям всё так плохо, можно продемонстрировать не ключевой код, без передачи (лично с планшета, например) — за такое никто не накажет.
Про «Контекст выполнения». Это поведение хрома. Там log нужен верный this. В firefox работает и тот пример что надо «исправлять».
В огнелисе поведение аналогично хрому. Работает в IE, фаербаге (может его имели в виду?) и на ноде.
Нет, он всё правильно сказал.
В FF (32, win 8) пример работает «как есть» без всяких ошибок.

Прошу прощения, действительно только в фаербаге.
Актуальный FF33:

FF35:
Фаербаг, да. Нода — тот же V8. С фаербагом как-то забываешь что это не нативный дебаггер. А когда натянешь на него ещё и AceBug, то это становится единственной причиной, по которой я окончательно не перешел на хром для разработки.
> Наверное все знают что все объекты передаются в javascript по ссылке
Ответ-то простой — pass-by-reference не существеует в javascript'e.
Передаётся значение — «указатель» на объект, но не ссылка. (если исходить из тепрминов C/C++).
Я мыслил именно в этом направлении, когда сам эту задачку решал :) Но всё равно можно запутаться, если не знать что аргументы становятся локальными переменными ф-ции.
А в каком языка не используя pass-by-reference это не так?
Не нужно путать причину со следствием.
Блин, это не совсем правильно. Переменная-аргумент obj внутри функции локальна, но суть в том, что ей присваивают указатель на новый объект — {a: 2}.

Если сделать так:
var obj = {
    a: 1
};

(function(obj) {
    obj.a = 2;
})(obj);

console.log(obj.a);//, то здесь будет «2».
Передаётся значение — «указатель» на объект, но не ссылка. (если исходить из тепрминов C/C++).


На указатель оно ещё меньше похоже.

Если пытаться быть до конца точным, то использованная в JS техника называется call by sharing
Нет, именно на указатель оно и похоже. Возможно, вам мешает провести аналогию отсутствие оператора разыменования * — зато аналогия между . в javascript и -> в С/С++ — полная.
Так точно, именно отсутствие оператора `*` и мешает. Но, слава богу, у этой реализации есть настоящее имя, которое позволяет нам использовать именно его, избегая вещей вроде

«типа ссылки, но не можем изменять весь объект, только мутировать»
или
«типа указателя, но нет оператора его разыменования»
И это настоящее имя называется «передача по значению»…
Лично мне больше нравится объяснение из википедии:

The description «call-by-value where the value is a reference» is common (but should not be understood as being call-by-reference); another term is call-by-sharing.
Давайте на нем и остановимся.
PS: и, да, я согласен, что оператор `.` в JS и `->` в С++ ведут себя похоже. Мой поинт был лишь о переменной и её поведении, а не об операторах к ней применяемым.
Это именно pass-by-reference, но на уровне абстракции языка Javascript этого понятия не существует. Для понимания, достаточно разобраться в том, что такое boxed и unboxed types.

Вообще, на github'е есть совершенно замечательная статья Cпенсера Типпинга: Js in 10 minutes. Там отлично разбираются все «приколы» js'а и способы их решения. После нее такие тесты проходятся на ура.

Как говорится, must have для любого js-ера…
function foo(arg) { arg = 5; }

var bar = 10;
foo(bar);

Если бы это была передача по ссылке, то переменная bar стала бы равна 5.
Прочитайте всё-таки, что такое boxed/unboxed types…
Спасибо, я знаю что такой упакованные и распакованные типы. Но от упаковки ничего не изменится:
function foo(arg) { arg = 5; }

var bar = new Number(10);
foo(bar);
bar все равно не стал равен 5.

function foo(arg) { arg = {value: 5}; }

var bar = {value: 10};
foo(bar);
и даже вот так переменная не изменилась. Такое поведение — это и есть передача по значению.

Если же вы решите сделать так:
function foo(arg) { arg.value = 5; }

var bar = {value: 10};
foo(bar);
то я вам скажу, что изменилось содержимое объекта, но переменная все еще продолжает указывать на тот же самый объект. Это все равно передача по значению.
Дык, вопрос в том, что является значением в первом и во втором случае… То что js «распаковывает» переменные базовых типов при передаче в качестве параметра в функцию, это тоже один из «приколов» js'а…
В общем, про распаковку я фигню написал, каюсь. Что происходит хорошо видно в этом коде:

function foo(arg) { 
  arg.gotcha = "bye-bye"
  arg = 5; 
}

var bar = new Number(10);
bar.gotcha = "hello";
foo(bar);

console.log(bar);        // => 10
console.log(bar.gotcha); // => "bye-bye"

Можно так исхитриться:

function foo(object, value) {
   object.valueOf = function () {
     return value;
   };
}

var bar = new Number(0);
foo(bar, 100);

bar + 1; // 101



Забавно, но это просто поведение оператора сложения, но никак не передача по ссылке :)
Кажется, для последнего примера достаточно и такого кода (убрал ненужное присвоение):

Logger = function(logFn) {
  this.log = function(message) {
    logFn(new Date() + ": " + message);
  };
}


Если я правильно разобрался, то это лишь касается только встроенных функций, так как такие функции ожидают, что this равен window (или, к примеру, console). Достаточно обернуть встроеную функцию в свою, чтобы это исправить. Например, такой аргумент сработает.

console.myLog = function(message) { console.log(message) };
var logger2 = new Logger(console.myLog);

logger2.log("Hi!");
logger2.log("Wazzup?");

// Sat Oct 04 2014 09:30:04 GMT+0300 (EEST): Hi!
// Sat Oct 04 2014 09:30:04 GMT+0300 (EEST): Wazzup?


Однако оба таких решения не самые изящные, так как нам везде придётся использовать либо bind(), либо function. Можно пойти другим путём и байндить встроенные функции к window внутри тела log. Функция isNative взята из внешних источников.

var Logger = function(logFn) {
  if (isNative(logFn)) {
    this.log = function(message) {
      logFn.bind(window)(new Date() + ": " + message);
    };
  } else {
    this.log = function(message) {
      logFn(new Date() + ": " + message);
    };
  }
}

var logger3 = new Logger(alert);

logger3.log("Hi!");
logger3.log("Wazzup?");


Однако такой трюк с console не пройдёт (и другими встроенными объектами, содержащими встроенные своёства наподобие console.log. Я не придумал, как это обойти.
Однако оба таких решения не самые изящные, так как нам везде придётся использовать либо bind(), либо function.

А чем вам bind не изящный?

Однако такой трюк с console не пройдёт (и другими встроенными объектами, содержащими встроенные своёства наподобие console.log. Я не придумал, как это обойти.

Передавать в логгер вместе с функцией скоп вызова?

Я уж молчу о том, что пример хреновый, хотя бы потому, что console.log умеет несколько аргументов, а логгер в примере — нет.
А чем вам bind не изящный?


Когда человек, который хочет инициализировать объект Logger увидит такой код, ему придётся потратить время, чтобы разобраться, как работает конструктор, что неоправданно усложняет код. Я попытался скрыть детали реализации. :)

Передавать в логгер вместе с функцией скоп вызова?


Тоже вариант. Но, по-видимому, лучше использовать bind(), так как с ним знакомо несравнимо большее количество людей, чем с Logger (смысл-то остаётся такой же).
Когда человек, который хочет инициализировать объект Logger увидит такой код, ему придётся потратить время, чтобы разобраться, как работает конструктор, что неоправданно усложняет код. Я попытался скрыть детали реализации. :)

Не очень понял, о чем вы. Имхо, любой джуниор, передавая куда-то функцию, должен позаботиться о корректном скопе вызова, если для передаваемой функции это критично (что, в общем случае, не всегда так).
Если я правильно разобрался, то это лишь касается только встроенных функций, так как такие функции ожидают, что this равен window (или, к примеру, console). Достаточно обернуть встроеную функцию в свою, чтобы это исправить. Например, такой аргумент сработает.
Неверно. Любой метод ожидает корректного this, не только встроенный.

var obj = {
    value: 5,
    foo: function() { console.log(value); }
};

obj.foo(); // 5
var foo = obj.foo();
foo(); // undefined
Подозреваю, что имелся в виду этот код.

var obj = {
    value: 5,
    foo: function() { console.log(this.value); } // [исправлено]
};

obj.foo(); // 5
var foo = obj.foo; // [исправлено]
foo(); // undefined


Отличный пример, спасибо! Стало понятнее. Можно упростить до такого:

var log = console.log;
log('Hello'); // TypeError: Illegal invocation

log.bind(console)('World'); // => World


Теперь очень ясно видно, почему bind() так называется: он как бы склеивает методы.
Да, конечно, это я и имел в виду. Похоже, что пятница для меня еще не закончилась :)
Ну и чтобы совсем мозг вывихнуть, можно обсудить, почему не работает

[function(){}, function(){}].forEach(Function.prototype.call.call)

и как его можно вылечить (из этого маленького примера можно очень продуктивно побеседовать о JS)
Скрытый текст
[function(){console.log(1)}, function(){console.log(2)}].forEach(Function.prototype.call.call.bind(Function.prototype.call))
Ага, а я на stackoverflow вот так ответил:

Скрытый текст
[ function(){console.log(1)}, function(){console.log(2)} ].forEach(Function.prototype.call.call, Function.prototype.call)

Теперь я тоже знаю что форич умеет this: З
А вот так тоже работает.

Скрытый текст
[ function(){console.log(1)}, function(){console.log(2)} ].forEach(Function.prototype.call, Function.prototype.call)



По поводу вашего оригинального вопроса. Не совсем понимаю, почему нужно call вызывать дважды. Первый раз мы получаем тело объекта-функции Function.prototype.call. Второй call опять же получает себя, так как мы обращаемся к тому же самому методу но через экземпляр нашего объекта-функции.
Ага, он там лишний. Спасибо :-)
Теперь очень ясно видно, почему bind() так называется: он как бы склеивает методы.

О_о
s/склеивает/приклеивает/
Ничего он не склеивает.

func.bind(scope) «преобразуется» в func.call(scope, ...)
В противном случае будет так: func.call(null, ...)
Любой метод ожидает корректного this, не только встроенный.

Да ну?

var obj = {
  value: 5,
  foo: function() { console.log(value); }
  foo2: function(msg) { console.log(msg); }
};


Методу foo2 тоже нужен «корректный» this?
Любой метод ожидает корректного this, не только встроенный, если другое не оговорено в документации на него.
Нет. Ожидания метода не зависят от наличия документации. Методы из Array.prototype часто можно успешно вызывать на объекты и они даже сделают именно то что должны.
Хорошо, как насчет такой формулировки: «Любой незнакомый метод следует по-умолчанию считать требующим контекста»?
А всё потому что при вызове logger.log(), контекст выполнения функции — logger
Это вы зря так говорите, может показаться, будто контексты выполнения неявно наследуются — а это не так.
При вызове logger.log() функция, обозванная logFn вызывается без контекста (или в контексте window, что примерно одно и то же) — и проблема именно в этом.
Контекст же, в котором вызывается функция logger.log нам вообще не интересен, потому что эта функция к своему контексту не обращается.
UFO just landed and posted this here
Хм. Не понимаю каким боком моя статья похожа на перевод вашей)
Но ссылку добавлю — вопросы годные.
UFO just landed and posted this here
Когда изучал js, подобные вещи мне просто голову взрывали, на практике же оказалось что если явно и исчерпывающе всё обзывать, то с этим не столкнешься. Ох не дай бог встретить в проекте код любителя таких головоломок)))
Почти все статьи о вопросах на интервью состоят из выдуманных проблем (особо упоротые еще пишут вопросы по знанию определений терминов), которые в реальности не встречаются, просто потому что никто такой неясной фиги не будет кодить. Но всегда интересно почитать комментарии, часто узнается что-то новое и/или полезное по теме.
Мы у себя из интересного задаем такие вопросы:
  • Чем отличается setTimeout от setInterval? Если зациклить setTimeout чем он будет отличаться от setInterval?
  • Как отменить вызов всех callback-функций переданных в setTimeout?
  • Как определить глобальную переменную внутри функции?
UFO just landed and posted this here
Например тем, что вместо одного таймера будет создана целая куча :)
UFO just landed and posted this here
Ответы на 1 и 3 вопросы:
Скрытый текст
1) setTimeout выполяется один раз, setInterval — бесконечное количество раз, пока не отменят. Если зациклить setTimeout, то код, вызываемый setTimeout, будет выполняться последовательно, даже если он выполняется дольше чем интервал, указанный в setTimeout

3) Просто: написать «переменная = ...» или «window.<переменная> = 1;» если включен строгий режим (use strict);


Второй вопрос: clearTimeout? но он отменяет только один вызов setTimeout
Ответ на второй вопрос:
Скрытый текст
Нужно создать свой setTimeout чтобы узнать последний ID таймаута, а потом от него и до 0 всё отменить.
В ноде не взлетит.
Скрытый текст
Там setTimeout возвращает не число, а объект.
Код всегда будет выполняться последовательно, потому что параллельное выполнение в javascript не предусмотрено в принципе. Зацикленный setTimeout будет отличаться от setInterval более длинным промежутком между вызовами. Это удлинение вызвано тем, что setInterval начинает отсчет сделующего интервала сразу же после окончанию предыдущего — а setTimeout еще надо успеть вызвать повторно.
ВебВоркеры на сколько я помню уходят в отдельный процесс и выполняются асинхронно (хз на сколько это идентично определению параллельно в JS)
Именно что в отдельный процесс. WebWorker, во-первых, надо создать явно — он не может быть создан «втихую» при вызове setInterval. А во-вторых, со страницей он может общаться только через сообщения, разделять же общие объекты невозможно.
Последний пример:

function Class1 () {
        var a = 1;
        this.log = function () {
              console.log(a);
        }
}

var obj = new Class1();
function Class2 (func) {
    this.log2 = function() {
          func();
    }
}
var obj2 = new Class2(obj.log);
obj.log();
obj2.log2();


Нормальный вывод:
1 1

что здесь не так? (Кастую mayorovp, kyrylo, другие тоже могут ответить)
Удивлён, если первый класс заменить на

function Class1 () {
        this.a = 1;
        this.log = function () {
              console.log(this.a);
        }
}


то obj2.log2 выводит undefined, то есть this.a не попадает в замыкание?

и всё же почему «TypeError: Illegal invocation» здесь не выводится?
new Class2(obj.log) — когда передаём функцию, то она берётся в вакууме с this === window || GLOBAL. Соответственно this.a выведет window.a.
TypeError: Illegal invocation вообще не о том.
function Class1 () {
        this.a = 1;
        this.log = function () {
              if (!(a instanceof Class1))
                    throw TypeError ("Illegal invocation");
              console.log(this.a);
        }
}

var obj = new Class1();
function Class2 (func) {
    this.log2 = function() {
          func();
    }
}
var obj2 = new Class2(obj.log);
obj.log();
obj2.log2();

Так устроит? :)
нет, код выдаёт «ReferenceError: a is not defined», а не «throw TypeError (»Illegal invocation");" ))
Это опять опечатка. if (!(this instanceof Class1)), конечно же
а, и всё что ли? чтобы не выдавать реализацию console.log, например, какое-то свойство объекта this не существует, выдают эту ошибку?
Спасибо
Дело в том, что console — это нативный объект. Соответственно, console.log обращается к его внутренним полям, минуя интерфейс интерпретатора Javascript, а потому просто не сможет работать с объектом другого типа в качестве контекста.
Потому что мы не отрываем console.log от контекста console. Я не силён в C+, но ответ на все вопросы и всё такое существует.

Хоть там и объясняется что к чему, однако всё равно не понятна разница между legal и illegal.

Что интересно, такой трюк должен работать в Firefox. В багтрекере Chrome знают об этом с 2010 года. Любопытно, сначала они закрыли тикет (wontfix), а потом всё же решили разобраться с этой ситуацией. Пока что разбираются.
Удивлён, если первый класс заменить на ... то obj2.log2 выводит undefined, то есть this.a не попадает в замыкание?

Разумеется, this.a не попадает в замыкание, потому что это не локальная переменная. У вложенной функции может быть свой контекст — и свой this. Для того, чтобы передать this в замыкание, часто первой строчкой функции пишут var that = this и дальше используют переменную that (ни разу не встречали такого?)
(function() { f(); f = function() { console.log(1); } })() function f() { console.log(2) } f();

Что мы увидим в консоли?
Ответ
Объявленная в gs function f() всплывёт, соответственно при вызове f внутри анонимной функции мы увидим не ReferenceError, как кто-то мог предположить, а двойку в консоли, при повторном вызове переменная f уже ссылается на функцию которая печатает 1.


И ведь все равно неправильно.
Порядок работы этого кода такой:
1. В глобальном скоупе появляется f = function f () (где console.log(2))
2. При создании анонимной функции создается её личный скоуп (который, конечно, ссылается на глобальный), в нем сразу создается переменная f.
3. При вызове созданной f получаем Uncaught TypeError: undefined is not a function. Потому что в личном скоупе уже есть f = undefined, а значит не нужно подниматься по скоупу выше.
4. Переменной f в скоупе анонимной функции присваивается значение function () { console.log(1)}
5. В глобальном скоупе выполняется f(). Так как ссылки на нее не менялись (только в анонимной функции, которая не влияет на gs) — выведет 2

Запустите в браузере.
Запустил. Увидел ровным счетом то, что было написано автором — 2 1.

Теперь вопросы к вам. С чего вы взяли, что в личном скоупе анонимной функции появится переменная f? Локальная переменная может быть создана через объявление переменной (var f) или объявление функции (function f). Ничего из этого в анонимной функции нет.

Вот если бы перед f = function() было написано var — то все работало бы так, как вы написали.
Каюсь. Невнимательность.
Показалось, что внутри написано var f = function (). Тогда было ровно так.
В посте все работает правильно.

Вопрос снимается.
Мне тоже там примерещился фантомный var. При помощи IIFE создаётся отдельный скоп, но он никак не используется. Пуф-ф-ф!, разрыв шаблона. Шаблона проектирования если быть точнее.
Добрый день

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

Что то вроде, я ему:

for(var i = 0; i < 5; i++)
    console.log(i)

А он мне:

var i = undefined;
for(i = 0; i < 5; i++)
    console.log(i);
Последний пример не совсем удачный.
Созданный в примере логгер из-за особенности реализации console.log() все равно будет работать в Firefox и Node.js.
Наверное, стоит указать в статье, что пример с TypeError предполагает то, что он будет запускаться в Chrome.
в последний версии Chrome уже нет этой проблемы — пример рабочий
Sign up to leave a comment.

Articles