Восемь типов данных, typeof
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/types.
В JavaScript существует несколько основных типов данных.
В этой главе мы получим о них общее представление, а позже, в соответствующих главах подробно познакомимся с использованием каждого типа в отдельности.
Число «number»
var n = 123; n = 12.345;
Единый тип число используется как для целых, так и для дробных чисел.
Существуют специальные числовые значения Infinity (бесконечность) и NaN (ошибка вычислений).
Например, бесконечность Infinity получается при делении на ноль:
alert( 1 / 0 ); // Infinity
Ошибка вычислений NaN будет результатом некорректной математической операции, например:
alert( "нечисло" * 2 ); // NaN, ошибка
Эти значения формально принадлежат типу «число», хотя, конечно, числами в их обычном понимании не являются.
Особенности работы с числами в JavaScript разобраны в главе Числа.
Число «bigint»
В JavaScript тип «number» не может содержать числа больше, чем 2 53 (или меньше, чем -2 53 для отрицательных). Это техническое ограничение вызвано их внутренним представлением. 2 53 – это достаточно большое число, состоящее из 16 цифр, поэтому чаще всего проблем не возникает. Но иногда нам нужны действительно гигантские числа, например в криптографии или при использовании метки времени («timestamp») с микросекундами.
Тип BigInt был добавлен в JavaScript, чтобы дать возможность работать с целыми числами произвольной длины.
Чтобы создать значение типа BigInt , необходимо добавить n в конец числового литерала:
// символ "n" в конце означает, что это BigInt const bigInt = 1234567890123456789012345678901234567890n;
Более подробно тип данных BigInt мы рассмотрим в отдельной главе BigInt.
Строка «string»
var str = "Мама мыла раму"; str = 'Одинарные кавычки тоже подойдут';
В JavaScript одинарные и двойные кавычки равноправны. Можно использовать или те или другие.
Тип символ не существует, есть только строка.
В некоторых языках программирования есть специальный тип данных для одного символа. Например, в языке С это char . В JavaScript есть только тип «строка» string . Что, надо сказать, вполне удобно.
Более подробно со строками мы познакомимся в главе Строки.
Булевый (логический) тип «boolean»
У него всего два значения: true (истина) и false (ложь).
Как правило, такой тип используется для хранения значения типа да/нет, например:
var checked = true; // поле формы помечено галочкой checked = false; // поле формы не содержит галочки
О нём мы поговорим более подробно, когда будем обсуждать логические вычисления и условные операторы.
Специальное значение «null»
Значение null не относится ни к одному из типов выше, а образует свой отдельный тип, состоящий из единственного значения null :
var age = null;
В JavaScript null не является «ссылкой на несуществующий объект» или «нулевым указателем», как в некоторых других языках. Это просто специальное значение, которое имеет смысл «ничего» или «значение неизвестно».
В частности, код выше говорит о том, что возраст age неизвестен.
Специальное значение «undefined»
Значение undefined , как и null , образует свой собственный тип, состоящий из одного этого значения. Оно имеет смысл «значение не присвоено».
Если переменная объявлена, но в неё ничего не записано, то её значение как раз и есть undefined :
var x; alert( x ); // выведет "undefined"
Можно присвоить undefined и в явном виде, хотя это делается редко:
var x = 123; x = undefined; alert( x ); // "undefined"
В явном виде undefined обычно не присваивают, так как это противоречит его смыслу. Для записи в переменную «пустого» или «неизвестного» значения используется null .
Символы «symbol»
«Символ» представляет собой уникальный идентификатор.
Создаются новые символы с помощью функции Symbol() :
// Создаём новый символ - id let
При создании символу можно дать описание (также называемое имя), в основном использующееся для отладки кода:
// Создаём символ id с описанием (именем) "id" let
Символы гарантированно уникальны. Даже если мы создадим множество символов с одинаковым описанием, это всё равно будут разные символы. Описание – это просто метка, которая ни на что не влияет.
Например, вот два символа с одинаковым описанием – но они не равны:
let id1 = Symbol("id"); let id2 = Symbol("id"); alert(id1 == id2); // false
Более подробно c символами мы познакомимся в главе Тип данных Symbol.
Объекты «object»
Первые 7 типов называют «примитивными».
Особняком стоит восьмой тип: «объекты».
Он используется для коллекций данных и для объявления более сложных сущностей.
Объявляются объекты при помощи фигурных скобок <. >, например:
var user = < name: "Вася" >;
Мы подробно разберём способы объявления объектов и, вообще, работу с объектами, позже, в главе Объекты.
Оператор typeof
Оператор typeof возвращает тип аргумента.
У него есть два синтаксиса: со скобками и без:
- Синтаксис оператора: typeof x .
- Синтаксис функции: typeof(x) .
Работают они одинаково, но первый синтаксис короче.
Результатом typeof является строка, содержащая тип:
typeof undefined // "undefined" typeof 0 // "number" typeof 1n // "bigint" typeof true // "boolean" typeof "foo" // "string" typeof Symbol() // "symbol" typeof <> // "object" typeof null // "object" (1) typeof function()<> // "function" (2)
Последние две строки помечены, потому что typeof ведёт себя в них по-особому.
- Результат typeof null == «object» – это официально признанная ошибка в языке, которая сохраняется для совместимости. На самом деле null – это не объект, а отдельный тип данных.
- Функции мы пройдём чуть позже. Пока лишь заметим, что функции не являются отдельным базовым типом в JavaScript, а подвидом объектов. Но typeof выделяет функции отдельно, возвращая для них «function» . На практике это весьма удобно, так как позволяет легко определить функцию.
К работе с типами мы также вернёмся более подробно в будущем, после изучения основных структур данных.
Итого
Есть 5 «примитивных» типов: number , string , boolean , null , undefined и 6-й тип – объекты object .
Очень скоро мы изучим их во всех деталях.
Оператор typeof x позволяет выяснить, какой тип находится в x , возвращая его в виде строки.
typeof
Оператор typeof возвращает строку, указывающую тип операнда.
Интерактивный пример
Синтаксис
Операнд следует за оператором typeof :
typeof operand
Параметры
operand является выражением, представляющим объект или примитив, тип которого должен быть возвращён.
Описание
В следующей таблице приведены возможные возвращаемые значения typeof . Дополнительная информация о типах и примитивах находится на странице структуры данных JavaScript.
Type | Result |
---|---|
Undefined | «undefined» |
Null | «object» (смотрите ниже) |
Boolean | «boolean» |
Number | «number» |
String | «string» |
Symbol (новый тип из ECMAScript 2015) | «symbol» |
Host object (определено JS окружением) | Зависит от реализации |
Function object (реализует [[Call]] в терминах ECMA-262) | «function» |
Любой другой тип | «object» |
Примеры
// Числа typeof 37 === "number"; typeof 3.14 === "number"; typeof 42 === "number"; typeof Math.LN2 === "number"; typeof Infinity === "number"; typeof NaN === "number"; // несмотря на то, что это "Not-A-Number" (не число) typeof Number(1) === "number"; // никогда не используйте эту запись! // Строки typeof "" === "string"; typeof "bla" === "string"; typeof "1" === "string"; // обратите внимание, что число внутри строки всё равно имеет тип строки typeof typeof 1 === "string"; // typeof всегда вернёт в этом случае строку typeof String("abc") === "string"; // никогда не используйте эту запись! // Booleans typeof true === "boolean"; typeof false === "boolean"; typeof Boolean(true) === "boolean"; // никогда не используйте эту запись! // Символы typeof Symbol() === "symbol"; typeof Symbol("foo") === "symbol"; typeof Symbol.iterator === "symbol"; // Undefined typeof undefined === "undefined"; typeof declaredButUndefinedVariable === "undefined"; typeof undeclaredVariable === "undefined"; // Объекты typeof a: 1 > === "object"; // используйте Array.isArray или Object.prototype.toString.call // чтобы различить обычные объекты и массивы typeof [1, 2, 4] === "object"; typeof new Date() === "object"; // То что ниже приводит к ошибкам и проблемам. Не используйте! typeof new Boolean(true) === "object"; typeof new Number(1) === "object"; typeof new String("abc") === "object"; // Функции typeof function () > === "function"; typeof class C > === "function"; typeof Math.sin === "function";
null
// Это было определено с рождения JavaScript typeof null === "object";
В первой реализации JavaScript значения были представлены парой тип тега и значение. Тип тега для объектов равнялся 0. null был представлен как нулевой указатель (0x00 в большинстве платформ). Следовательно, тип тега для null равнялся нулю, поэтому возвращаемое значение typeof является фиктивным. (подробнее)
Исправление было предложено в ECMAScript (через отключение), но было отклонено. Это привело бы к тому, что typeof null === ‘null’ .
Использование оператора new
// Все функции-конструкторы, созданные с помощью 'new', будут иметь тип 'object' var str = new String("String"); var num = new Number(100); typeof str; // Вернёт 'object' typeof num; // Вернёт 'object' // Но существует исключение для конструктора Function var func = new Function(); typeof func; // Вернёт 'function'
Регулярные выражения
Вызываемые регулярные выражения были нестандартным дополнением в некоторых браузерах.
typeof /s/ === "function"; // Chrome 1-12 Не соответствует ECMAScript 5.1 typeof /s/ === "object"; // Firefox 5+ Соответствует ECMAScript 5.1
Ошибки, связанные с временными мёртвыми зонами
До ECMAScript 2015, гарантировалось, что оператор typeof вернёт строку для любого операнда, с которым он был вызван. Это изменилось после добавления не поднимающихся объявлений let and const с блочной областью видимости. Теперь, если переменные объявлены с помощью let и const , и для них вызывается typeof в блоке объявления переменных, но до объявления, то выбрасывается ReferenceError . Поведение отличается от необъявленных переменных, для которых typeof вернёт ‘undefined’. Переменные с блочной областью видимости находятся в «временной мёртвой зоне», которая длится от начала блока до момента объявления переменных. В этой зоне попытка доступа к переменным выбрасывает исключение.
typeof undeclaredVariable === "undefined"; typeof newLetVariable; let newLetVariable; // ReferenceError typeof newConstVariable; const newConstVariable = "hello"; // ReferenceError
Исключения
Во всех текущих браузерах существует нестандартный host-объект document.all , который имеет тип Undefined.
typeof document.all === "undefined";
Хотя спецификация разрешает собственные имена типов для нестандартных экзотических объектов, требуется чтобы эти имена отличались от предопределённых. Ситуация, когда document.all имеет тип undefined должна рассматриваться как исключительное нарушение правил.
Спецификации
Specification |
---|
ECMAScript Language Specification # sec-typeof-operator |
Совместимость с браузерами
BCD tables only load in the browser
IE-специфичные замечания
В IE 6, 7 и 8 многие host-объекты являются объектами, но не функциями. Например:
typeof alert === "object";
Смотрите также
- instanceof
- Why typeof is no longer «safe»
Found a content problem with this page?
- Edit the page on GitHub.
- Report the content issue.
- View the source on GitHub.
This page was last modified on 7 авг. 2023 г. by MDN contributors.
Your blueprint for a better internet.
MDN
Support
- Product help
- Report an issue
Our communities
Developers
- Web Technologies
- Learn Web Development
- MDN Plus
- Hacks Blog
- Website Privacy Notice
- Cookies
- Legal
- Community Participation Guidelines
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2023 by individual mozilla.org contributors. Content available under a Creative Commons license.
Задачи с собеседований (front-end)
Так получилось, что за свою карьеру front-end разработчика, я побывала на многих собеседованиях. Тема прохождения интервью не теряет своей актуальности, а в комментариях, когда речь заходит о собеседованиях, начинают ломаться копья. Хочу тоже внести свой вклад и поделиться накопившейся коллекцией вопросов. Прошу.
Лирическое отступление
На собеседования меня приглашают охотно, не знаю, виной этому симпатичная фоточка в резюме или его содержание. Если о резюме, то для описания технологий, используемых на каждой текущей работе, я трачу часа два, три. Порой просматриваю в чужие, как они оформлены, да и вдруг найду что-то полезное для себя…
Задачи
Для начала что-нибудь полегче.
1. Есть некоторая строка (var str = ‘fgfggg’;), что будет, если мы возьмем str[0]?
str[0] вернет символ строки, который находится по нулевому индексу. Строка в js — immutable, то есть из нее можно прочитать символ, а вот записать нет.
2. Реализовать методы, которые в процессе выполнения строки (2).plus(3).minus(1) дали бы на выходе 4.
Поскольку, мы работаем с числами, надо расширить прототип Number новыми методами.
Number.prototype.plus = function (value) < return this + value; >Number.prototype.minus = function (value)
Число два будет доступно через this в функции plus. Из нее мы возвращаем результат сложения числа, на которое указывает this и числа, переданного в качестве аргумента. Аналогично для minus.
3. Сейчас уже редко, но до сих еще спрашивают: «Почему плохо писать прямо в прототипы базовых типов?»
Array.prototype.sort = function () <> var t = [2, 1, 22]; t.sort()
Ожидаемый результат — [1, 2, 22], а вернется undefined.
Мы рассчитываем, что стандартные методы сработают согласно документации, но какой-то разработчик можем переопределить метод, и он вернет совершенно неожиданный результат.
Именно поэтому библиотека prototype.js уступила jQuery.
4. Дана функция, она принимает в качестве аргументов строки ‘*’, ‘1’, ‘b’, ‘1c’, реализуйте ее так, что бы она вернула строку ‘1*b*1c’
Ее можно решать прямо в лоб, перебирая в цикле все аргументы, но мы поступим умнее.
function getStr()
5. Дано дерево, надо найти сумму всех вершин.
Я решила задачу рекурсией, потом мы упростили решение, а затем переписали на очередь.
var sum = 0; function getSum(obj) < sum += obj.valueNode; if (obj.next != null) < for (var i = 0; i < obj.next.length; i++) < getSum(obj.next[i]); >> return sum; > var tree1 = < valueNode: 1, next: [ < valueNode: 3, next: null >, < valueNode: 2, next: null >] > var tree = < valueNode: 3, next: [< valueNode: 1, next: null >, < valueNode: 3, next: null >, < valueNode: 2, next: null >, < valueNode: 2, next: [ < valueNode: 1, next: null >, < valueNode: 5, next: null >] >] >; console.log(getSum(tree1)); sum = 0; console.log(getSum(tree));
function getSum(obj) < var arr = [obj], sum = 0, current; while(arr.length >0) < current = arr.shift(); sum += current.valueNode; if (current.next != null) < for (var i = 0; i < current.next.length; i++) < arr.push(current.next[i]); >> > return sum; > var tree = < valueNode: 3, next: [< valueNode: 1, next: null >, < valueNode: 3, next: null >, < valueNode: 2, next: null >, < valueNode: 2, next: [ < valueNode: 1, next: null >, < valueNode: 5, next: null >] >] >; getSum(tree)
6. Можно ли из js менять значения в before, after?
Нет, единственное что мы можем — это удалить класс, у которого указаны before или after, либо наоборот добавить.
Давайте, что-нибудь для разгрузки мозга, вопрос на верстку.
7. Вместить три блока 20X20px в ряд, в блок шириной 60px, при этом у блоков должны быть границы.
8. Как применяются скругленные углы для элементов и стили для текста(шрифт, тип шрифта, цветащте и тд)?
Стилевые свойства применятся ко всем элементам с текстом, если у тегов не указаны стилевые правила. К примеру у ссылок указан цвет текста по умолчанию и он более приоритетный, чем определенный нами в body. Дело в том, что для многих свойств по умолчанию стоит значение inherit, то есть как у родителя. Получается поднимается вверх, пока не дойдет до body.
Для свойства border-radius, наоборот, применяется только к тегу, у которого мы хотим что бы били скругленные углы.
9. Что такое ресет стилей?
Многим тегам стили прописаны по умолчанию, в процессе верстке нам приходится переопределять их. Что бы не делать это каждый раз, мы «скидываем» их «массово». Для этого заводим файл под стили, подключаем его первым или в первом стилевом файле, в самом верху указываем наши стили для базовых тегов.
К примеру. Мы часто используем список ul для верстки меню, для этого мы каждый раз вынуждены обнулять padding, margin и list-style-type. Можно один раз задать стили, и списки станут без внешней и внутренней границы, а так же без маркеров.
10. Требуется сверстать попап по центру, его размеры нам известны, но мы не хотим что бы он прокручивался вместе со страницей, причем по высоте может и не влезать в высоту экрана.
body < overflow: hidden; >.wrap < position: fixed; top: 0; left: 0; width: 100%; height: 100%; overflow-y: auto; background-color: rgba(230, 230, 230, .1); >.popup
11. Нарисовать стилями полукруг.
width: 100px; height: 100px; border-right: 1px solid #f00; border-radius: 0 50% 50% 0;
12. Есть массив в котором лежат объекты с датами, отсортировать по датам.
var arr = [, , ]; arr.forEach(function(item) < var arrDate = item.date.split('.'), date = new Date(Number(arrDate[2]), Number(arrDate[1]), Number(arrDate[0])); item.time = date.getTime(); >); arr.sort(function (a, b) < if (a.time - b.time < 0) < return false; >else < return true; >>); var res = arr.map(function (item) < return ; >); console.log(res);
13. Есть несколько слов, определить состоят ли они из одних и тех же букв(‘кот’, ‘ток’, ‘окт’)
var arr = ['kot', 'tok', 'okt'], arr1 = ['kot', 'tok', 'ott']; function sameWords(arr) < var word1, word2; for (var i = 0; i < arr.length-1; i++) < word1 = 0; word2 = 0; if (arr[i].length !== arr[i+1].length) < return false; >else < for (var j = 0; j < arr[i].length; j++) < word1 += arr[i][j].charCodeAt(0); word2 += arr[i+1][j].charCodeAt(0); >if (word1 !== word2) < return false; >> > return true; > console.log(sameWords(arr)); console.log(sameWords(arr1));
Про промисы
В последнее время часто стали задавать вопросы про promse, что это такое, какие методы обработки ошибок, можно ли обойтись без них.(14, 15, 16)
Начну издалека, так как js-асинхронен, то в результате отслеживания завершения определенного когда в качестве аргументов передавались callback-функции, которые и вызывались по мере готовности. Цепочка ассинхронных методов росла, что приводило к Callback Hell, что затрудныло работу с кодом, отлаживание ошибок и им на смену пришли промисы.
var promis = new Promise(function (resolve, reject) < . setTimeout(function () < resolve(res); >, 5000); >);
Несколько промисов можно объединить и получить разом ответ от них
var promisesImages = []; for (let i = 0; i < count; i++) < promisesImages.push(new Promise((resolveImg, rejectImg) => < let img = new Image(), startTime = Date.now(); img.src = this.imgUrl + '?' + Math.floor(Math.random() * 100000000); img.onload = () =>< resolveImg(Date.now() - startTime); >; img.onerror = rejectImg; >)); > Promise.all(promisesImages) .then((timings) => )
17. И конечно вопрос на засыпку: «Каким образом можно обойтись без промисов?».
По старинке, вводили переменную-счетчик и как-только наступало окончание очередного асинхронного действия, сравнивали переменную с общем количеством.
18. Еще вспомнился вопрос про обработку ошибок в промисах. У нас есть три запроса к серверу, один возвращает нам имя пользователя, второй его данные, а третий изображение для аватарки, мы для каждого запроса используем по промису, объединяя их в цепочку, что будет если в одном из запросов произойдет ошибка, довыполнится ли цепочка?
Промисы все разрастаются и на смену Callback Hell приходит Promise Hell. Что же делать?
function test() < return new Promise(function (resolve) < setTimeout (function () < resolve(1); >) >) > async function test1()
Фактически выполнение test1 как бы «останавливается» до того момета пока мы не получим ответ от test. Я и раньше читала про эти методы, но особо не обращала внимание, а как-то в разговоре речь зашла о них, я удивилась: «Как так, выполнение останавливает, это же не понятно, что за функция, где она определена, когда callback — здесь все понятно, видно что произойдет в функции по завершению операции, ну или всегда можем поискать по имени, опять же, привычно про контекст». На что мне ответили, что у меня javascript головного мозга и я на столько привыкла к асинхронности, что синхронное выполнение мне кажется чем-то за гранью, а ведь в в пыхе, на которой я некогда программировала все синхронно, там даже если чтение из файла, пока не завершиться, дальше не продолжится.
(19)Вот еще один примерчик на асинхронность. Объяснить в какой последовательности выведутся цифры и почему так.
console.log(1); setTimeout(function() < console.log(2); >, 0) console.log(3);
Достаточно популярный, надо сказать. Вот мне он и достался в очередной раз. Уже отработано начала отвечать: «1, 3, 2. Так как хоть js и асинхронен, но внутри его есть очередь выполнения и setTimeout и setInterval, если им указан 0, помещают вызов функции в конец очереди.»
Тут надо сказать ребята зафейлились, заявив, что я ответила правильно, про 132, но не объяснила почему.
(20)Вообще вопросы на setTimeout и setInterval весьма актуальны.Меня спрашивали: «Какое минимальное время можно задать?»
В каждом браузере есть свой минимум, если вы указываете меньше него, то все равно задержка будет не меньше минимуму. Иногда даже и больше указанного времени, так как задача попадает в очередь и время складывается из заданного плюс затраты в на выполнение на задач в очереди перед ней.
Куда же без замыканий
Не давно выделили целую статью для этого примера(тут), читатели в комментариях расписывали всевозможные способы решения, от традиционных до фантастических. Вот о которых я обычно рассказывала на собеседованиях.
for (var i = 0; i < 10; i++) < setTimeout(function () < console.log(i); >, 100); >
21. Что будет выведено в консоль, как можно модифицировать пример что бы он возвращал правильный результат(назовите как можно больше способов)?
Самый распространенный, обернуть в замыкание
for (var i = 0; i < 10; i++) < (function (i) < setTimeout(function () < console.log(i); >, 100); >)(i) >
Не все обращали внимание, что в i можно передать не только контекст
for (var i = 0; i < 10; i++) < setTimeout(function (i) < console.log(i); >.bind(this, i), 100); >
Так же методам setInterval и setTimeout можно передать аргументы, которые будут прокинуты в качестве аргументов калбек-функции
for (var i = 0; i < 10; i++) < setTimeout(function (i) < console.log(i); >, 100, i); >
for (let i = 0; i < 10; i++) < setTimeout(function () < console.log(i); >, 100); >
Одна из возможностей es6, в данном случае сработает несколько неожиданно, let не в блоке <>.
Давайте что-нибудь похардкорнее.
22. Надо написать функцию, которая вернет «hello world», но при этом в теле функции нельзя использовать ни цифры, ни буквы, а циклы, массивы, объекты можно, но без цифр.
Увы, эти интересные люде не сказали мне как ее решать, поэтому, подумав дома, я могу только предположить.
var zero = [].length, one = [<>].length, two = [,,].length, seven = [. ].length; console.log(String.fromCharCode(Number(String(seven) + String(two))));
Так я получила букву H, но это изврат еще тот, осталось сделать оставшиеся 10 знаков…
(23) От них же. Числа от 1 до 100 лежат в массиве, они хаотично перемешанные, от туда изъяли одно число, надо найти, что это за число. алгоритм не должен превышать O(n^2) сложности.
Пройти массив циклом и сложить все имеющиеся там цифры и вычесть из числа, полученного по формуле (n + 1) / (n / 2).
Мне пришло в голову более экзотическое решение. Детям и слабонервным лучше не смотреть.
var sum = 101*50, sumArr = eval([4, 2, . 3, 7].join('+').replace('++', '+')), res; res = sum-sumArr;
Просто мне вспомнилось, что когда-то спрашивали: «Как быстрее всего найти сумму элементов массива?»
eval([4, 2, . 3, 7].join('+')
Вот на тот момент вспомнилось и не пришло ни чего лучше.
(24) Вот еще фейл-задача. Приведу дословно, те напишу как написали мне.
function Book(name, author) < this.name = name; this.author = author; return this; >function Foo(Book, 'Учебник javascript', 'Петр Сергеев')
Меня сбила эта строчка function Foo(Book, ‘Учебник javascript’, ‘Петр Сергеев’). Только я вижу, что здесь что-то не так? Мне предложили решать через Object.create(), но я не согласна. Свойства и методы, записаные в саму функцию-конструктор, не будут «скопированы» Object.create.
function Book(name, author) < this.name = name; this.author = author; return this; >function Foo(Cclass, name, author) < return Object.create(Cclass.prototype); >var book = Foo(Book, 'js', 'petr'); console.log(book.name); -> undefined
Конечно меня не взяли, а на последок посоветовали лучше учить теорию. Хнык-хнык.
В любом случае, я бы предпочла решать так.
function Book(name, author) < this.name = name; this.author = author; return this; >function Foo(Cclass, name, author) < return Cclass.call(<>, name, author); > var book = Foo(Book, 'js', 'petr'); console.log(book.name);
Хочу посоветовать чувакам лучше готовиться к собеседованиям.
Задача про палиндром
(25) Да, до сих пор ее задают. Причем она попалась мне несколько раз. В первый раз я стала решать циклом, вроде бы получилось, только оставалась проблема с разными символами, писать для каждого символа replace — это не вариант, а в регулярках я не сильна, да и сработает он до первого подходящего вхождения. Я не привыкла оставлять за спиной что-то непонятное, поэтому разобралась как реализовать функцию.
function isPalindrom1(str) < if (str.toLowerCase().replace(/[^а-яА-ЯёЁ]/g, '') === str.toLowerCase().replace(/[^а-яА-ЯёЁ]/g, '').split('').reverse().join('')) < return true; >else < return false; >>
Красиво, просто, изящно.
Забавно, но где-то через год мне попался этот же вопрос. Я обрадовалась: «Свезло». Как оказалось, нет. Требовалось решить с помощью цикла, а регулярку можно было использовать только для одного символа. Собеседник мотивировал это тем, что replace с регуляркой по всей строке — слишком ресурсоемко.
Подумав дома, у меня получилось вот так:
function isPalindrom(str) < var str = str.toLowerCase(), lim = str.length - 1, i = 0, j = str.length - 1; while (i if (/[^а-яА-ЯёЁ]/.test(str[j])) < j -= 1; >if (str[i] != str[j]) < return false; >i += 1; j -= 1; > return true; > console.log(isPalindrom('А роза упала на лапу Азора'));
26. Как подключить js, css? Плюсы, минусы разных способов?
Можно с помощью тегов
прямо на странице или
подключаем из внешних файлов.
Минус подключения внешних файлов заключается в том, что для их скачки, открываются дополнительные соединения. Может случиться так, что мы не сможем закачать, а если это еще и js файл, подключенный где-то в начале, то мы рискуем показать пользователю пустую страницу.
Размещение стилей и скриптов в самом документе, в этом плане, надежнее. Но при этом скрипты и стили совершенно не кешируются.
27. Чем opacity отличается от visible: hidden и что это такое, отличие от overflow: hidden?
opacity отвечает за прозрачность элемента. Принимает значения от 0 до 1, при 0 — элемент не виден, .5 — полупрозрачен, 1 — полностью виден. Даже при 0 занимает место на странице.
Элемент со стилями visible: hidden так же занимает место, не видим. Но в отличие от элемента с opacity, js-события на нем не срабатывают.
display: none — полностью скрывает элемент, он не видим и не занимает место на странице. javascript не может получить ни width, height.
overflow: hidden; — скрывает все, что попадет за его пределы.
Модное слово — каррирование
28. Реализовать функцию f: f(2, 3) -> 5, при вызове f(2)(3), тоже вернет 5
function f(a, b) < if (b !== undefined) < return a + b; >else < return function (b) < return a + b; >> >
Хоть данный шаблон упоминается в книге Стефанова, на практике я ни разу не видела, что бы кто-то его использовал.
Более сложный вариант.
f(1)(2)(3)() -> 6, f(0)(3)(1)(5)() -> 8
function f(arg) < var value = arg; return function (arg) < if (arg !== undefined) < return f(value + arg); >else < return value; >> >
Реализовать функцию, которая возвращает результаты
foo(1)(2)(3) -> 6
foo(1)(2)(3)(4) -> 10
foo(-1)(2)(3)(4)(7) -> 15
foo(1)(2)(3). (n) вернет результатом сумму 1+2+3. +n
Самый сложный вариант, когда вызов этой конструкции не заканчивается пустыми скобками, а значит мы не имеем явного указателя на выход. Задача досталась не мне, в комментариях к статье мне любезно рассказали решение.
В итоге получилось
function foo(value) < var acc = value; function addNext(next) < acc += next; return addNext; >addNext.toString = addNext.valueOf = function() < return acc; >return addNext; >
29. На собесе на прошлую работу мне попался вопрос: ‘Что такое live/die’
Где то из глубины сознания всплыло, что это аналог on/off, с помощью них можно навешивать/удалять обработчики на события их. Но более старые методы, по сравнению on/off.
В проекте я столкнулась с еще и с bind/unbind
30. JSONP, как реализовать
Мы не хотим находясь на одном сайте, получать данные с другого и как-то их обрабатывать. Что же делать? Может ajax, но у него есть ограничение по политике бозопасности. Надо что-то совпадали протокол, домен и порт, даже на поддомены мы не можем слать ajax-запросы.
Все же разработчики нашли некий лайфхак для крос-доменных запросов.
Если вы используете jquery, то всего-то надо указать в параметре «dataType» — «jsonp».
Мало кто знает, что лежит в основе данного запроса, реализация нативным js приблизительно такая
var url = 'http://mydomen.com/news.php', script = document.createElement('script'), callbackName = 'jsonCallback' + Math.randome(); script.src = url + '?callback' + callbackName; window[callbackName] = function(response) < console.log(response); >document.header.appendChild(script);
тег script начинает выкачивать скрипт, лежащий по адресу в атрибуте src, а после закачки, происходит выполнение. Поэтому мы создаем тег script. Случайным образом генерируем имя функции. Формируем урл к ресурсу, где в качестве параметра передается имя нашей новой функции.
В глобальную область, по ключу callbackName помещается функция, которая вызовется, при получение данных от ресурса, они станут доступны внутри через параметр.
На сервере ресурса сработает что-то в этом роде
echo $_REQUEST[callback] + '(' + json_encode($arDate) + ')';
В window попадет
window.jsonCallback0.90428777 -> function (response)
Так как теги с атрибутом src способы посылать только GET запросы, то jsonp не поддерживает POST
31. Заголовки CORS?
Это альтернатива jsonp, в специальном заголовках перечисляются «доверенные» ресурсы, которые смогут получать данные от него, а если указать ‘*’, то любые сайты, при обращение, станут получать данные.
Так же как и с jsonp доступны только GET запросы.
Когда я рассказывала про альтернативу jsonp интервьюер от этого вариант «отмахнулся», сказав, что не надежный. Я с ним не согласна. JSONP легче подменить, чем CORS-заголовки
В date подпихнуть вут такую строчку
');alert(document.cookie);'
За место данных можно вернуть какой-то код, который закроет вызов функции и выполнится у пользователя.
Сейчас уже исправили, но раньше наблюдалась критическая уязвимость в json-е, с помощью символа переноса строки он подламывался.
32. Ускорение загрузки страницы.
1 минимизировать и склеить в один все js-файлы
2 то же и с css
3 собрать изображения в спрайты
4 кеширование
а) файлы js и css кешируются навсегда( или на длительный период, к примеру на год), когда в них вносятся какие-то изменения, в результате разработки, меняется номер версии, браузер считает, что это новые файлы и закачивает их. Время кеширование содержится в заголовке expires.
б) файл кешируется до тех пор, пока в нем не произошли изменения. Когда пользователь в первый раз заходит на сайт, у него еще нет кеша, файлы закачиваются и в следующий раз, когда происходит обмен заголовками происходит анализ ETag(хеш суммы содержимого содержимого файла или хеш последнего времени изменения, или номер версии) Если он изменился, то закачивается, в противном случае, берется из хеша.
Так же можно брать заголовки с датой последней модификации(Last_Modifed), но это менее надежно.
в) можно сжать данные перед отправкой, решается настройкой конфига в nginx. Пример. Content-Encoding: gzip
г) можно разбить js на модули и на разных страницах подключать по мере необходимости.
Можно найти еще несколько, но они менее популярны или устарели.
33. CommonJS и AMD или модульность?
Один принцип архетектры асинхронен, а второй синхронен. В качестве примера можно привести RequaerJs, по одному его виду можно сказать, что он асинхронен
define([ mod1, mod2, mod3 ], function (Mod1, Mod2, Mod3) < new Mod1(); . >);
define — фактически простая функция, все, что написано за ее приделами будет выполняться без оглядки на то, что происходит в define, поэтому одним аргументом идет callbeck-функция. Как только подгрузятся все зависимости, перечисленные в массиве, так они станут доступны внутри через аргументы callbeck-функции.
Система модулей в Node.Js наоборот синхронна. Мы пишем:
var mod = requaer('modul');
и сразу ниже с mod можно работать, все останавливается, пока модуль не загрузится
34. Еще интересный пример. Реализовать методы seven, plus, one, five, minus, two. seven(plus(one())) -> 8. five(minus(two())) -> 3
На собеседование я начала решать так:
function one(arg) < if (typeof arg === 'function') < return arg(1); >else < return 1; >> function seven(arg) < if (typeof arg === 'function') < return arg(7); >else < return 7; >> function plus(arg) < return function (a) < return a + arg; >>
Аналогично пишутся функции для five, minus, two. Уже дома, поразмышляв в спокойной обстановке, появилось такое решение
function one(arg) < return 1 + (arg || 0); >function two(arg) < return 2 + (arg || 0); >function five(arg) < return 5 + (arg || 0); >function seven(arg) < return 7 + (arg || 0); >function plus(arg) < return arg; >function minus(arg)
35. Сортировка пузырьком.
Да, да, ее еще спрашивают.
var m = [1, 7, 5, 13, 8], count = m.length - 1, max; for (var i = 0; i < count; i++) < for (var j = 0; j < count - i; j++) < if (m[j] >m[j + 1]) < max = m[j]; m[j] = m[j + 1]; m[j + 1] = max; >> >
36. Обратная польская нотация.
Основной принцип. Дана строчка 23+1-, начинаем двигаться по ней, как только доходим до первого арифметического знака, берем две цифры перед ним и на их место, записываем результат вычисления. Получится 51-. Начинаем все с начала.
Кому интересно, вот статья wikipedia
var notation = '23+1-', notation1 = '7 2 3 * -', notation2 = '1 2 + 4 * 3 +'; function getComputation(notation) < var queue = [], tmp, num1, num2; for (var i = 0; i < notation.length; i++) < if (/\d+/.test(notation[i]) === true) < queue.push(Number(notation[i])); >else < switch (notation[i]) < case '+': tmp = queue.pop() + queue.pop(); queue.push(tmp); break; case '-': num1 = queue.pop(); num2 = queue.pop(); if (num1 >num2) < tmp = num1 - num2; >else < tmp = num2 - num1; >queue.push(tmp); break; case '*': tmp = queue.pop() * queue.pop(); queue.push(tmp); break; case '/': tmp = queue.pop() / queue.pop(); queue.push(tmp); break; > > > return queue[0]; > console.log(getComputation(notation)); console.log(getComputation(notation1)); console.log(getComputation(notation2));
Мне рассказали ее принцип, поэтому только реализовать механизм.
37. Есть div, в нем другой div, у второго задан padding 50%, как это все будет выглядеть?
.wrap < width: 200px; border: 1px solid green; >.block
Собеседник предложил мне посмотреть дома как оно сработает, хотя мне стало очень интересно.
Как вы считаете, правильно ли на собеседование отвечать самому на вопрос, если кандидат с ним не справился?
38. Есть строка, состоящая из разных скобок, проверить закрыты ли все. Пример строки: «())(<>><()][]["
function validBraces(str) < var arrOpenSymbols = [], result = false, countOpenSymbols; if (str.length >0) < for (var i = 0; i < str.length; i++) < if (str[i] === 'else < countOpenSymbols = arrOpenSymbols.length; if ((str[i] === '>' && arrOpenSymbols[(countOpenSymbols-1)] === ' <') || (str[i] === ']' && arrOpenSymbols[(countOpenSymbols-1)] === '[') || (str[i] === ')' && arrOpenSymbols[(countOpenSymbols-1)] === '(') ) < arrOpenSymbols.pop(); >> > if (arrOpenSymbols.length === 0) < result = true; >else < result = false; >> return result; > console.log(''); console.log(validBraces('()')); console.log(validBraces('[)')); console.log(validBraces('<>[]()')); console.log(validBraces('([<>])')); console.log(validBraces('())(<>><()][]['));
Если бы передавались строки из скобок ''
function validBraces(str) < try < eval(str); return true; >catch (err) < return false; >>
39. Напишите код, который при клике на любой div внутри root будет выводить в консоль его id.
root id1 id2 id3
$('#root').on('click', function (event) < event.stopPropogation(); console.log($(event.target).attr('id')); >)
40. Напишите код, который сделает из массива объект
// на входе массив
var arr = [ , ]
// на выходе объект
function getObj(arr) < var obj = <>; arr.forEach(function(item)< obj[item.name] = item.value; >); return obj; >
41. Что выведется в результате?
var i = 10; var array = []; while (i--) < (function (i) < var i = i; array.push(function() < return i + i; >); >)(i); > console.log([ array[0](), array[1](), ])
[18, 16], так как за счет функции
(function (i) <>)(i);
создает замыкание, var i = i — уже принадлежат областям видимости в замыканиях.
function()
в начале поищет в своей области видимости i, не найдя, подымется на уровень выше и там найдет его. Из функции вернется сумма, которая будет положена в массив последним элементом.
42. Есть функция и объект. Напишите все известные вам способы, чтобы вывести в консоли значение x из объекта используя функцию
function f() < console.log(this.x); >var obj = ;
f.call(obj, a, b); f.apply(obj, [a, b]); obj.funk = function f() < console.log(this.x); >obj.funk(); function f() < console.log(this.x); >.bind(obj, a, b); f();
function Book() < this.name = 'foo' >Book.prototype = < getName: function() < return this.name; >>; var book = new Book(); Book.prototype.getUpperName = function() < return this.getName().toUpperCase(); >book.getUpperName();
Что вернет метод?
а) ошибку, т.к. метод объявлен позже, чем создание экземпляра
б) вернется 'FOO'
44. В js переменные объявленные следующим образом:
a=3; b=«hello»;
Укажите правильные утверждения.
а) создаются в локальном контексте
б) создаются в глобальном контексте
в) создание переменной без ключевого слова var — синтаксическая ошибка.
45. Что вернёт этот код — typeof (function()<>)()
а) callback
б) method
в) undefined
г) function
46. Что будет если задать margin: -10px;
47. Почему 0.1 + 0.2 даст 0.30000000000000004
Связано с системой представления десятичных чисел, не точностью их вычисления.
48. Моему коллеге на должность php-разраба, предложили такую
Есть два блока, второй скрытый, если в первом нет дочерних элементов, то показать второй.
Представляю вашему вниманию мое решение
.block1 < width: 150px; height: 20px; border: 1px solid red; >.block2
function showBlock() < var block1 = document.getElementsByClassName('block1')[0], block2 = document.getElementsByClassName('block2')[0]; if (!(block1.childNodes.length >0 )) < block2.style.display = 'block'; >> document.addEventListener("DOMContentLoaded", function () < showBlock(); >);
Конечно это не все задания, которые мне довелось решать, какие-то я благополучно забыла, какие-то слишком банальные, масса вопросов, просто на теорию, остались за кадром.
Прочее
Вот еще парочка вопросов, они на прямую не касаются фронта, но, наверно не лишне знать.
Идеальный интерфейс в вашем представление?
Как вы бы сделали отказоустойчивый сайт?
Какие типы уязвимостей знаете?
- JavaScript
- Учебный процесс в IT
- Карьера в IT-индустрии
70 вопросов по JavaScript для подготовки к собеседованию
Надеюсь, эта статья будет полезна как начинающим разработчикам, так и опытным.
В вопросах, которые показались мне сложнее прочих, приведены ссылки на дополнительную литературу.
Буду признателен за развернутые комментарии. Все замечания будут учтены при редактировании статьи.
70 вопросов по JavaScript для подготовки к собеседованию
1. В чем разница между null и undefined?
Для начала давайте поговорим о том, что у них общего.
Во-первых, они принадлежат к 7 «примитивам» (примитивным типам) JS:
let primitiveTypes = ['string', 'number', 'null', 'undefined', 'boolean', 'symbol', 'bigint']
Во-вторых, они являются ложными значениями, т.е. результатом их преобразования в логическое значение с помощью Boolean() или оператора "!!" является false:
console.log(!!null) // false console.log(!!undefined) // false console.log(Boolean(null)) // false console.log(Boolean(undefined)) // false
Ладно, теперь о различиях.
- переменной, которой не было присвоено значения, т.е. объявленной, но не инициализированной переменной;
- функции, которая ничего не возвращает явно, например, console.log(1);
- несуществующего свойства объекта.
let _thisIsUndefined const doNothing = () => <> const someObj = < a: 'ay', b: 'bee', c: 'si' >console.log(_thisIsUndefined) // undefined console.log(doNothing()) // undefined console.log(someObj['d']) // undefined
null — это «значение отсутствия значения». null — это значение, которое присваивается переменной явно. В примере ниже мы получаем null, когда метод fs.readFile отрабатывает без ошибок:
fs.readFile('path/to/file', (e, data) => < console.log(e) // здесь мы получаем null if(e) < console.log(e) >console.log(data) >)
При сравнении null и undefined мы получаем true, когда используем оператор "==", и false при использовании оператора "== javascript">console.log(null == undefined) // true console.log(null === undefined) // false
2. Для чего используется оператор "&&"?
Оператор "&&" (логическое и) находит и возвращает первое ложное значение либо последний операнд, когда все значения истинные. Он использует короткое замыкание во избежание лишних затрат:
console.log(false && 1 && []) // false console.log(' ' && true && 5) // 5
С оператором «if»:
const router: Router = Router() router.get('/endpoint', (req: Request, res: Response) => < let conMobile: PoolConnection try < // операции с базой данных >catch (e) < if (conMobile) < conMobile.release() >> >)
То же самое с оператором "&&":
const router: Router = Router() router.get('/endpoint', (req: Request, res: Response) => < let conMobile: PoolConnection try < // операции с базой данных >catch (e) < conMobile && conMobile.release() >>)
3. Для чего используется оператор "||"?
Оператор "||" (логическое или) находит и возвращает первое истинное значение. Он также использует короткое замыкание. Данный оператор использовался для присвоения параметров по умолчанию в функциях до того, как параметры по умолчанию были стандартизированы в ES6.
console.log(null || 1 || undefined) // 1 function logName(name) < let n = name || Mark console.log(n) >logName() // Mark
4. Является ли использование унарного плюса (оператор "+") самым быстрым способом преобразования строки в число?
Согласно MDN оператор "+" действительно является самым быстрым способом преобразования строки в число, поскольку он не выполняет никаких операций со значением, которое является числом.
5. Что такое DOM?
DOM или Document Object Model (объектная модель документа) — это прикладной программный интерфейс (API) для работы с HTML и XML документами. Когда браузер первый раз читает («парсит») HTML документ, он формирует большой объект, действительно большой объект, основанный на документе — DOM. DOM представляет собой древовидную структуру (дерево документа). DOM используется для взаимодействия и изменения самой структуры DOM или его отдельных элементов и узлов.
Допустим, у нас есть такой HTML:
Document Object Model
DOM этого HTML выглядит так:
В JS DOM представлен объектом Document. Объект Document имеет большое количество методов для работы с элементами, их созданием, модификацией, удалением и т.д.
6. Что такое распространение события (Event Propagation)?
Когда какое-либо событие происходит в элементе DOM, оно на самом деле происходит не только в нем. Событие «распространяется» от объекта Window до вызвавшего его элемента (event.target). При этом событие последовательно пронизывает (затрагивает) всех предков целевого элемента. Распространение события имеет три стадии или фазы:
- Фаза погружения (захвата, перехвата) — событие возникает в объекте Window и опускается до цели события через всех ее предков.
- Целевая фаза — это когда событие достигает целевого элемента.
- Фаза всплытия — событие поднимается от event.target, последовательно проходит через всех его предков и достигает объекта Window.
Подробнее о распространении событий можно почитать здесь и здесь.
7. Что такое всплытие события?
Когда событие происходит в элементе DOM, оно затрагивает не только этот элемент. Событие «всплывает» (подобно пузырьку воздуха в воде), переходит от элемента, вызвавшего событие (event.target), к его родителю, затем поднимается еще выше, к родителю родителя элемента, пока не достигает объекта Window.
Допустим, у нас есть такая разметка:
function addEvent(el, event, callback, isCapture = false) < if (!el || !event || !callback || typeof callback !== 'function') return if (typeof el === 'string') < el = document.querySelector(el) >el.addEventListener(event, callback, isCapture) > addEvent(document, 'DOMContentLoaded', () => < const child = document.querySelector('.child') const parent = document.querySelector('.parent') const grandparent = document.querySelector('.grandparent') addEvent(child, 'click', function(e) < console.log('child') >) addEvent(parent, 'click', function(e) < console.log('parent') >) addEvent(grandparent, 'click', function(e) < console.log('grandparent') >) addEvent('html', 'click', function(e) < console.log('html') >) addEvent(document, 'click', function(e) < console.log('document') >) addEvent(window, 'click', function(e) < console.log('window') >) >)
У метода addEventListener есть третий необязательный параметр — useCapture. Когда его значение равняется false (по умолчанию), событие начинается с фазы всплытия. Когда его значение равняется true, событие начинается с фазы погружения (для «прослушивателей» событий, прикрепленных к цели события, событие находится в целевой фазе, а не в фазах погружения или всплытия. События в целевой фазе инициируют все прослушиватели на элементе в том порядке, в котором они были зарегистрированы независимо от параметра useCapture — прим. пер.). Если мы кликнем по элементу child, в консоль будет выведено: child, parent, grandparent, html, document, window. Вот что такое всплытие события.
8. Что такое погружение события?
Когда событие происходит в элементе DOM, оно происходит не только в нем. В фазе погружения событие опускается от объекта Window до цели события через всех его предков.
function addEvent(el, event, callback, isCapture = false) < if (!el || !event || !callback || typeof callback !== 'function') return if (typeof el === 'string') < el = document.querySelector(el); >el.addEventListener(event, callback, isCapture) > addEvent(document, 'DOMContentLoaded', () => < const child = document.querySelector('.child') const parent = document.querySelector('.parent') const grandparent = document.querySelector('.grandparent') addEvent(child, 'click', function(e) < console.log('child'); >, true) addEvent(parent, 'click', function(e) < console.log('parent') >, true) addEvent(grandparent, 'click', function(e) < console.log('grandparent') >, true) addEvent('html', 'click', function(e) < console.log('html') >, true) addEvent(document, 'click', function(e) < console.log('document') >, true) addEvent(window, 'click', function(e) < console.log('window') >, true) >)
У метода addEventListener есть третий необязательный параметр — useCapture. Когда его значение равняется false (по умолчанию), событие начинается с фазы всплытия. Когда его значение равняется true, событие начинается с фазы погружения. Если мы кликнем по элементу child, то увидим в консоли следующее: window, document, html, grandparent, parent, child. Это и есть погружение события.
9. В чем разница между методами event.preventDefault() и event.stopPropagation()?
Метод event.preventDefault() отключает поведение элемента по умолчанию. Если использовать этот метод в элементе form, то он предотвратит отправку формы (submit). Если использовать его в contextmenu, то контекстное меню будет отключено (данный метод часто используется в keydown для переопределения клавиатуры, например, при создании музыкального/видео плеера или текстового редактора — прим. пер.). Метод event.stopPropagation() отключает распространение события (его всплытие или погружение).
10. Как узнать об использовании метода event.preventDefault()?
Для этого мы можем использовать свойство event.defaulPrevented, возвращающее логическое значение, служащее индикатором применения к элементу метода event.preventDefault.
11. Почему obj.someprop.x приводит к ошибке?
const obj = <> console.log(obj.someprop.x)
Ответ очевиден: мы пытается получить доступ к свойству x свойства someprop, которое имеет значение undefined. obj.__proto__.__proto = null, поэтому возвращается undefined, а у undefined нет свойства x.
12. Что такое цель события или целевой элемент (event.target)?
Простыми словами, event.target — это элемент, в котором происходит событие, или элемент, вызвавший событие.
Имеем такую разметку:
И такой простенький JS:
function clickFunc(event)
Мы прикрепили «слушатель» к внешнему div. Однако если мы нажмем на кнопку, то получим в консоли разметку этой кнопки. Это позволяет сделать вывод, что элементом, вызвавшим событие, является именно кнопка, а не внешний или внутренние div.
13. Что такое текущая цель события (event.currentTarget)?
Event.currentTarget — это элемент, к которому прикреплен прослушиватель событий.
И немного видоизмененный JS:
function clickFunc(event)
Мы прикрепили слушатель к внешнему div. Куда бы мы ни кликнули, будь то кнопка или один из внутренних div, в консоли мы всегда получим разметку внешнего div. Это позволяет заключить, что event.currentTarget — это элемент, к которому прикреплен прослушиватель событий.
14. В чем разница между операторами "==" и "== ==" (абстрактное или нестрогое равенство) и оператором "== ==" производит так называемое неявное сравнение. Оператор "= == https://habrastorage.org/r/w1560/webt/yd/xe/tn/ydxetnfghjtfuex_p-em8v4emck.png" data-src="https://habrastorage.org/webt/yd/xe/tn/ydxetnfghjtfuex_p-em8v4emck.png"/>
Все примеры возвращают true.
Первый пример — первое условие алгоритма.
Второй пример — четвертое условие.
Третий — второе.
Четвертый — седьмое.
Пятый — восьмое.
И последний — десятое.
15. Почему результатом сравнения двух похожих объектов является false?
let a = < a: 1 >let b = < a: 1 >let c = a console.log(a === b) // false console.log(a === c) // true хм.
В JS объекты и примитивы сравниваются по-разному. Примитивы сравниваются по значению. Объекты — по ссылке или адресу в памяти, где хранится переменная. Вот почему первый console.log возвращает false, а второй — true. Переменные «a» и «c» ссылаются на один объект, а переменные «a» и «b» — на разные объекты с одинаковыми свойствами и значениями.
16. Для чего используется оператор "!!"?
Оператор "!!" (двойное отрицание) приводит значение справа от него к логическому значению.
console.log(!!null) // false console.log(!!undefined) // false console.log(!!'') // false console.log(!!0) // false console.log(!!NaN) // false console.log(!!' ') // true console.log(!!<>) // true console.log(!![]) // true console.log(!!1) // true console.log(!![].length) // false
17. Как записать несколько выражений в одну строку?
Для этого мы можем использовать оператор "," (запятая). Этот оператор «двигается» слева направо и возвращает значение последнего выражения или операнда.
let x = 5 x = (x++, x = addFive(x), x *= 2, x -= 5, x += 10) function addFive(num)
Если мы выведем значение x в консоль, то получим 27. Сначала мы увеличиваем значение x на единицу (x = 6). Затем вызываем функцию addFive() с параметром 6, к которому прибавляем 5 (x = 11). После этого мы умножаем значение x на 2 (x = 22). Затем вычитаем 5 (x = 17). И, наконец, прибавляем 10 (x = 27).
18. Что такое поднятие (Hoisting)?
Поднятие — это термин, описывающий подъем переменной или функции в глобальную или функциональную области видимости.
Для того, чтобы понять, что такое Hoisting, необходимо разобраться с тем, что представляет собой контекст выполнения.
Контекст выполнения — это среда, в которой выполняется код. Контекст выполнения имеет две фазы — компиляция и собственно выполнение.
Компиляция. В этой фазе функциональные выражения и переменные, объявленные с помощью ключевого слова «var», со значением undefined поднимаются в самый верх глобальной (или функциональной) области видимости (как бы перемещаются в начало нашего кода. Это объясняет, почему мы можем вызывать функции до их объявления — прим. пер.).
Выполнение. В этой фазе переменным присваиваются значения, а функции (или методы объектов) вызываются или выполняются.
Запомните: поднимаются только функциональные выражения и переменные, объявленные с помощью ключевого слова «var». Обычные функции и стрелочные функции, а также переменные, объявленные с помощью ключевых слов «let» и «const» не поднимаются.
Предположим, что у нас есть такой код:
console.log(y) y = 1 console.log(y) console.log(greet('Mark')) function greet(name) < return 'Hello ' + name + '!' >var y
Получаем undefined, 1 и 'Hello Mark!'.
Вот как выглядит фаза компиляции:
function greet(name) < return 'Hello ' + name + '!' >var y // присваивается undefined // ожидается завершение фазы компиляции // затем начинается фаза выполнения /* console.log(y) y = 1 console.log(y) console.log(greet('Mark')) */
После завершения фазы компиляции начинается фаза выполнения, когда переменным присваиваются значения и вызываются функции.
Дополнительно о Hoisting можно почитать здесь.
19. Что такое область видимости (Scope)?
Область видимости — это место, где (или откуда) мы имеем доступ к переменным или функциям. JS имеем три типа областей видимости: глобальная, функциональная и блочная (ES6).
Глобальная область видимости — переменные и функции, объявленные в глобальном пространстве имен, имеют глобальную область видимости и доступны из любого места в коде.
// глобальное пространство имен var g = 'global' function globalFunc() < function innerFunc() < console.log(g) // имеет доступ к переменной g, поскольку она является глобальной >innerFunc() >
Функциональная область видимости (область видимости функции) — переменные, функции и параметры, объявленные внутри функции, доступны только внутри этой функции.
function myFavouriteFunc(a) < if (true) < var b = 'Hello ' + a >return b > myFavouriteFunc('World') console.log(a) // Uncaught ReferenceError: a is not defined console.log(b) // не выполнится
Блочная область видимости — переменные (объявленные с помощью ключевых слов «let» и «const») внутри блока (< >), доступны только внутри него.
function testBlock() < if (true) < let z = 5 >return z > testBlock() // Uncaught ReferenceError: z is not defined
Область видимости — это также набор правил, по которым осуществляется поиск переменной. Если переменной не существует в текущей области видимости, ее поиск производится выше, во внешней по отношению к текущей области видимости. Если и во внешней области видимости переменная отсутствует, ее поиск продолжается вплоть до глобальной области видимости. Если в глобальной области видимости переменная обнаружена, поиск прекращается, если нет — выбрасывается исключение. Поиск осуществляется по ближайшим к текущей областям видимости и останавливается с нахождением переменной. Это называется цепочкой областей видимости (Scope Chain).
// цепочка областей видимости // внутренняя область видимости -> внешняя область видимости -> глобальная область видимости // глобальная область видимости var variable1 = 'Comrades' var variable2 = 'Sayonara' function outer() < // внешняя область видимости var variable1 = 'World' function inner() < // внутренняя область видимости var variable2 = 'Hello' console.log(variable2 + ' ' + variable1) >inner() > outer() // в консоль выводится 'Hello World', // потому что variable2 = 'Hello' и variable1 = 'World' являются ближайшими // к внутренней области видимости переменными
20. Что такое замыкание (Closures)?
Наверное, это самый сложный вопрос из списка. Я постараюсь объяснить, как я понимаю замыкание.
По сути, замыкание — это способность функции во время создания запоминать ссылки на переменные и параметры, находящиеся в текущей области видимости, в области видимости родительской функции, в области видимости родителя родительской функции и так до глобальной области видимости с помощью цепочки областей видимости. Обычно область видимости определяется при создании функции.
Примеры — отличный способ объяснить замыкание:
// глобальная область видимости var globalVar = 'abc' function a() < // область видимости функции console.log(globalVar) >a() // 'abc' // цепочка областей видимости // область видимости функции a -> глобальная область видимости
В данном примере, когда мы объявляем функцию, глобальная область видимости является частью замыкания.
Переменная «globalVar» не имеет значения на картинке, потому что ее значение может меняться в зависимости от того, где и когда будет вызвана функция. Но в примере выше globalVar будет иметь значение «abc».
Теперь пример посложнее:
var globalVar = 'global' var outerVar = 'outer' function outerFunc(outerParam) < function innerFunc(innerParam) < console.log(globalVar, outerParam, innerParam) >return innerFunc > const x = outerFunc(outerVar) outerVar = 'outer-2' globalVar = 'guess' x('inner')
В результате получаем «guess outer inner». Объяснение следующее: когда мы вызываем функцию outerFunc и присваиваем переменной «x» значение, возвращаемое функцией innerFunc, параметр «outerParam» равняется «outer». Несмотря на то, что мы присвоили переменной «outerVar» значение «outer-2», это произошло после вызова функции outerFunc, которая «успела» найти значение переменной «outerVar» в цепочке областей видимости, этим значением было «outer». Когда мы вызываем «x», которая ссылается на innerFunc, значением «innerParam» является «inner», потому что мы передаем это значение в качестве параметра при вызове «x». globalVar имеет значение «guess», потому что мы присвоили ей это значение перед вызовом «x».
Пример неправильного понимания замыкания.
const arrFunc = [] for (var i = 0; i < 5; i++) < arrFunc.push(function() < return i >) > console.log(i) // 5 for (let i = 0; i < arrFunc.length; i++) < console.log(arrFunc[i]()) // все 5 >
Данный код работает не так, как ожидается. Объявление переменной с помощью ключевого слова «var» делает эту переменную глобальной. После добавления функций в массив «arrFunc» значением глобальной переменной «i» становится «5». Поэтому когда мы вызываем функцию, она возвращает значение глобальной переменной «i». Замыкание хранит ссылку на переменную, а не на ее значение во время создания. Эту проблему можно решить, используя IIFE или объявив переменную с помощью ключевого слова «let».
Подробнее о замыкании можно почитать здесь и здесь.
21. Какие значения в JS являются ложными?
const falsyValues = ['', 0, null, undefined, NaN, false]
Ложными являются значения, результатом преобразования которых в логическое значение является false.
22. Как проверить, является ли значение ложным?
Следует использовать функцию Boolean или оператор "!!" (двойное отрицание).
23. Для чего используется директива «use strict»?
«use strict» — это директива ES5, которая заставляет весь наш код или код отдельной функции выполняться в строгом режиме. Строгий режим вводит некоторые ограничения по написанию кода, тем самым позволяя избегать ошибок на ранних этапах.
Вот какие ограничения накладывает строгий режим.
Нельзя присваивать значения или обращаться к необъявленным переменным:
function returnY() < 'use strict' y = 123 return y >returnY() // Uncaught ReferenceError: y is not defined
Запрещено присваивать значения глобальный переменным, доступным только для чтения или записи:
'use strict' var NaN = NaN // Uncaught TypeError: Cannot assign to read only property 'NaN' of object '#' var undefined = undefined var Infinity = 'and beyond'
Нельзя удалить «неудаляемое» свойство объекта:
'use strict' const obj = <> Object.defineProperties(obj, 'x', < value: 1 >) delete obj.x // Uncaught TypeError: Property description must be an object: x
Запрещено дублирование параметров:
'use strict' function someFunc(a, b, b, c) <> // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
Нельзя создавать функции с помощью функции eval:
'use strict' eval('var x = 1') console.log(x) // Uncaught ReferenceError: x is not defined
Значением «this» по умолчанию является undefined:
'use strict' function showMeThis() < return this >showMeThis() // undefined
24. Какое значение имеет this?
Обычно this ссылается на значение объекта, который в данный момент выполняет или вызывает функцию. «В данный момент» означает, что значение this меняется в зависимости от контекста выполнения, от того места, где мы используем this.
const carDetails = < name: 'Ford Mustang', yearBought: 2005, getName() < return this.name >isRegistered: true > console.log(carDetails.getName()) // Ford Mustang
В данном случае метод getName возвращает this.name, а this ссылается на carDetails, объект, в котором выполняется getName, который является ее «владельцем».
Добавим после console.log три строчки:
var name = 'Ford Ranger' var getCarName = carDetails.getName console.log(getCarName()) // Ford Ranger
Второй console.log выдает Ford Ranger, и это странно. Причина такого поведения заключается в том, что «владельцем» getCarName является объект window. Переменные, объявленные с помощью ключевого слова «var» в глобальной области видимости, записываются в свойства объекта window. this в глобальной области видимости ссылается на объект window (если речь не идет о строгом режиме).
console.log(getCarName === window.getCarName) // true console.log(getCarName === this.getCarName) // true
В этом примере this и window ссылаются на один объект.
Одним из способов решения данной проблемы является использование методов call или apply:
console.log(getCarName.apply(carDetails)) // Ford Mustang console.log(getCarName.call(carDetails)) // Ford Mustang
Call и apply принимают в качестве первого аргумента объект, который будет являться значением this внутри функции.
В IIFE, функциях, которые создаются в глобальном области видимости, анонимных функциях и внутренних функциях методов объекта значением this по умолчанию является объект window.
(function() < console.log(this) >)() // window function iHateThis() < console.log(this) >iHateThis() // window const myFavouriteObj = < guessThis() < function getName() < console.log(this.name) >getName() >, name: 'Marko Polo', thisIsAnnoying(callback) < callback() >> myFavouriteObj.guessThis() // window myFavouriteObj.thisIsAnnoying(function() < console.log(this) // window >)
Существует два способа получить «Marko Polo».
Во-первых, мы можем сохранить значение this в переменной:
const myFavoriteObj = < guessThis() < const self = this // сохраняем значение this в переменной self function getName() < console.log(self.name) >getName() >, name: 'Marko Polo', thisIsAnnoying(callback) < callback() >>
Во-вторых, мы можем использовать стрелочную функцию:
const myFavoriteObj = < guessThis() < const getName = () => < // копируем значение this из внешнего окружения console.log(this.name) >getName() >, name: 'Marko Polo', thisIsAnnoying(callback) < callback() >>
Стрелочные функции не имеют собственного значения this. Они копируют значение this из внешнего лексического окружения.
25. Что такое прототип объекта?
В двух словах, прототип — это план (схема или проект) объекта. Он используется как запасной вариант для свойств и методов, существующих в данном объекте. Это также один из способов обмена свойствами и функциональностью между объектами. Это основная концепция прототипного наследования в JS.
const o = <> console.log(o.toString()) // [object Object]
Несмотря на то, что объект «о» не имеет свойства toString, обращение к этому свойству не вызывает ошибки. Если определенного свойства нет в объекте, его поиск осуществляется сначала в прототипе объекта, затем в прототипе прототипа объекта и так до тех пор, пока свойство не будет найдено. Это называется цепочкой прототипов. На вершине цепочки прототипов находится Object.prototype.
console.log(o.toString === Object.prototype.toString) // true
Подробнее о прототипах и наследовании можно почитать здесь и здесь.
26. Что такое IIFE?
IIFE или Immediately Invoked Function Expression — это функция, которая вызывается или выполняется сразу же после создания или объявления. Для создания IIFE необходимо обернуть функцию в круглые скобки (оператор группировки), превратив ее в выражение, и затем вызвать ее с помощью еще одних круглых скобок. Это выглядит так: (function()<>)().
(function( ) < >( )) (function( ) < >)( ) (function named(params) < >)( ) (( ) => < >) (function(global) < >)(window) const utility = (function( ) < return < // утилиты >>)
Все эти примеры являются валидными. Предпоследний пример показывает, что мы можем передавать параметры в IIFE. Последний пример показывает, что мы можем сохранить результат IIFE в переменной.
Лучшее использование IIFE — это выполнение функций настройки инициализации и предотвращение конфликтов имен с другими переменными в глобальной области видимости (загрязнение глобального пространства имен). Приведем пример.
У нас есть ссылка на библиотеку somelibrary.js, которая предоставляет некоторые глобальные функции, которые мы можем использовать в нашем коде, но в этой библиотеке есть два метода, createGraph и drawGraph, которые мы не используем, потому что они содержат ошибки. И мы хотим реализовать эти функции самостоятельно.
Одним из способов решить данную проблему является изменение структуры наших скриптов:
Таким образом, мы переопределяем методы, предоставляемые библиотекой.
Вторым способом является изменение имен наших функций:
Третий способ — использование IIFE:
В этом примере мы создаем служебную переменную, которая содержит результат IIFE, возвращающий объект, содержащий методы createGraph и drawGraph.
Вот еще одна проблема, которую можно решить с помощью IIFE:
val li = document.querySelectorAll('.list-group > li') for (var i - 0, len = li.length; i < len; i++) < li[i].addEventListener('click', function(e) < console.log(i) >) >
Допустим, у нас есть элемент «ul» с классом «list-group», содержащий 5 дочерних элементов «li». И мы хотим выводить в консоль значение «i» при клике по отдельному «li». Однако вместо этого в консоль всегда выводится 5. Виной всему замыкание.
Одним из решений является IIFE:
var li = document.querySelectorAll('.list-group > li') for (var i = 0, len = li.length; i < len; i++) < (function(currentIndex) < li[currentIndex].addEventListener('click', function(e) < console.log(currentIndex) >) >)(i) >
Причина, по которой этот код работает, как задумано, состоит в том, что IIFE создает новую область видимости на каждой итерации, и мы записываем значение «i» в currentIndex.
27. Для чего используется метод Function.prototype.apply?
Apply используется для привязки определенного объекта к значению this вызываемой функции.
const details = < message: 'Hello World!' >function getMessage() < return this.message >getMessage.apply(details) // Hello World!
Этот метод похож на Function.prototype.call. Единственное отличие состоит в том, что в apply аргументы передаются в виде массива.
const person = < name: 'Marko Polo' >function greeting(greetingMessage) < return `$$` > greeting.apply(person, ['Hello']) // Hello Marko Polo
28. Для чего используется метод Function.prototype.call?
Call используется для привязки определенного объекта к значению this вызываемой функции.
const details = < message: 'Hello World!' >; function getMessage() < return this.message; >getMessage.call(details); // Hello World!
Этот метод похож на Function.prototype.apply. Отличие состоит в том, что в call аргументы передаются через запятую.
const person = < name: 'Marko Polo' >; function greeting(greetingMessage) < return `$$`; > greeting.call(person, 'Hello'); // Hello Marko Polo
29. В чем разница между методами call и apply?
Отличие между call и apply состоит в том, как мы передаем аргументы в вызываемой функции. В apply аргументы передаются в виде массива, в call — через запятую.
const obj1 = < result: 0 >const obj2 = < result: 0 >function reduceAdd() < let result = 0 for (let i = 0, len = arguments.length; i < len; i++) < result += arguments[i] >this.result = result > reduceAdd.apply(obj1, [1, 2, 3, 4, 5]) // 15 reduceAdd.call(obj2, 1, 2, 3, 4, 5) // 15
30. Для чего используется метод Function.prototype.bind?
Bind возвращает новую функцию, значением this которой является объект, указанный в качестве первого параметра. В отличие от bind, call и apply сразу же вызывают функцию.
import React from 'react' class MyComponent extends React.Component < constructor(props) < super(props) this.state = < value: '' >this.handleChange = this.handleChange.bind(this) // привязываем метод handleChange к компоненту MyComponent > handleChange(e) < // код >render() < return ( < > < input type = < this.props.type >value = < this.state.value >onChange = < this.handleChange >/> > ) > >
31. Что такое функциональное программирование и какие особенности JS позволяют говорить о нем как о функциональном языке программирования?
Функциональное программирование — это декларативная концепция программирования или образец (паттерн) того, как строятся приложения, как используются функции, содержащие выражения, которые вычисляют значения без изменения аргументов, которые им передаются.
Объект Array содержит методы map, filter и reduce, которые являются самыми известными функциями в мире функционального программирования из-за их полезности, а также потому, что они не изменяют массив, что делает эти функции «чистыми». Также в JS имеются замыкание и функции высшего порядка, которые являются характеристиками функционального языка программирования.
Метод map возвращает новый массив с результатами вызова callback для каждого элемента массива:
const words = ['Functional', 'Procedural', 'Object-Oriented'] const wordsLength = words.map(word => word.length)
Метод filter создает новый массив со всеми элементами, которые удовлетворяют условию, указанному в callback:
const data = < < name: 'Mark', isRegistered: true > < name: 'Mary', isRegistered: false > < name: 'Mae', isRegistered: true >> const registeredUsers = data.filter(user => user.isRegistered)
Метод reduce выполняет callback один раз для каждого элемента массива, за исключением пустот, принимая четыре аргумента: начальное значение (или значение от предыдущего callback), значение текущего элемента, текущий индекс и итерируемый массив:
const strs = ['I', ' ', 'am', ' ', 'Iron', ' ', 'Man'] const result = strs.reduce((acc, currentStr) => acc + str, '')
32. Что такое функции высшего порядка (Higher Order Functions)?
Функция высшего порядка — это функция, возвращающая другую функцию или принимающая другую функцию в качестве аргумента.
function higherOrderFunction(param, callback)
33. Почему функции в JS называют объектами первого класса (First-class Objects)?
Функции называют объектами первого класса, потому что они обрабатываются также, как и любое другое значение в JS. Они могут присваиваться переменным, быть свойством объекта (методом), элементом массива, аргументом другой функции, значением, возвращаемым функцией. Единственным отличием функции от любого другого значения в JS является то, что функция может быть выполнена или вызвана.
34. Как бы Вы реализовали метод Array.prototype.map?
function map(arr, mapCallback) < // проверяем переданные параметры if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') < return [] >else < let result = [] // мы создаем массив с результатами при каждом вызове функции // поскольку мы не хотим менять оригинальный массив for (let i = 0, len = arr.length; i < len; i++) < result.push(mapCallback(arr[i], i, arr)) // помещаем результаты mapCallback в result >return result > >
Метод map создает новый массив с результатом вызова указанной функции для каждого элемента массива.
35. Как бы Вы реализовали метод Array.prototype.filter?
function filter(arr, filterCallback) < // проверяем передаваемые параметры if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') < return [] >else < let result = [] // . for (let i = 0, len = arr.length; i < len; i++) < // определяем соответствие возвращаемого результата заданному условию if (filterCallback(arr[i], i, arr)) < // помещаем значение, прошедшее фильтр, в result result.push(arr[i]) >> return result > >
Метод filter создает новый массив со всеми элементами, прошедшими проверку, задаваемую в передаваемой функции.
36. Как бы Вы реализовали метод Array.prototype.reduce?
function reduce(arr, reduceCallbak, initialValue) < // .. if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') < return [] >else < // если в функцию не было передано значения initialValue, то let hasInitialValue = initialValue !== undefined let value = hasInitialValue ? initialValue : arr[0] // мы будем использовать первый элемент initialValue // затем мы перебираем массив, начиная с 1, если в функцию не передавалось значения initialValue, либо с 0, если значение было передано for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) < // затем на каждой итерации мы присваиваем результат вызова reduceCallback переменной value = reduceCallback(value, arr[i], i, arr) >return value > >
Метод reduce применяет функцию reducer к каждому элементу массива (слева-направо), возвращая одно результирующее значение.
37. Что такое объект arguments?
Arguments — это коллекция аргументов, передаваемых функции. Это объект, подобный массиву, у него есть свойство length, мы можем получить доступ к определенному значению с помощью arguments[i], но у него отсутствуют методы forEach, reduce, filter и map. Он позволяет узнать количество параметров функции.
Преобразовать arguments в массив можно с помощью Array.prototype.slice:
Array.prototype.slice.call(arguments)
Запомните: в стрелочных функциях объект arguments не работает.
function one() < return arguments >const two = function() < return arguments >const three = function three(< return arguments >) const four = () => arguments four() // arguments is not defined
Вызов функции four приводит к ошибке ReferenceError: arguments is not defined. Эту проблему можно решить с помощью оператора rest:
const four = (. args) => args
Это автоматически поместит все параметры в массив.
38. Как создать объект, не имеющий прототипа?
Это можно сделать с помощью Object.create:
const o1 = <> console.log(o1.toString) // [object Object] const o2 = Object.create(null) // в качестве первого параметра методу Object-create передается объект-прототип // нам не нужен объект-прототип, поэтому передаем null console.log(o2.toString) // o2.toString is not a function
39. Почему в представленном коде переменная b становится глобальной при вызове функции?
function myFunc() < let a = b = 0 >myFunc()
Так происходит, потому что оператор присваивания (" javascript">function myFunc() < let a = (b = 0) >myFunc()
Сначала значение 0 присваивается переменной «b», которая не объявлена. Движок JS делает ее глобальной. Возвращаемое выражением b = 0 значение (0) затем присваивается локальной переменной «a».
Эту проблему можно решить сначала объявив локальные переменные, а затем присвоив им значения:
function myFunc() < let a, b a = b = 0 >myFunc()
40. Что такое ECMAScript?
ECMAScript — это спецификация, стандарт скриптовых языков программирования, он является основой JS, поэтому любые изменения ECMAScript отражаются на JS.
Последний вариант спецификации ECMA-262 можно посмотреть здесь.
41. Что нового привнес в JS стандарт ES6 или ECMAScript2015?
- Стрелочные функции (Arrow Functions).
- Классы (Classes).
- Шаблонные строки (Template Strings).
- Расширенные объектные литералы (Enhanced Object literals).
- Деструктуризация (Object Destructuring).
- Промисы (Promises).
- Генераторы (Generators).
- Модули (Modules).
- Symbol.
- Прокси (Proxies).
- Множества (Sets).
- Параметры по умолчанию.
- Операторы rest и spread.
- Блочная область видимости (ключевые слова «let» и «const»).
42. В чем разница между ключевыми словами «var», «let» и «const»?
Переменные, объявленные с помощью ключевого слова «var», являются глобальными. Это означает, что они доступны из любого места в коде:
function giveMeX(showX) < if(showX)< var x = 5 >return x > console.log(giveMeX(false)) console.log(giveMeX(true))
Результатом первого console.log будет undefined, второго — 5. Мы имеем доступ к переменной «x» из-за ее всплытия в глобальную область видимости. Код из примера выше интерпретируется следующим образом:
function giveMeX(showX) < var x // имеет значение undefined if(showX)< x = 5 >return x >
Результатом первого console.log является undefined, поскольку объявленные переменные, которым не присвоено значения, имеют значение undefined по умолчанию.
Переменные, объявленные с помощью ключевых слов «let» и «const» имеют блочную область видимости. Это означает, что они доступны только внутри блока (< >):
function giveMeX(showX) < if(showX)< let x = 5 >return x > function giveMeY(showY) < if(showY)< let y = 5 >return y >
Вызов этих функций с параметром false приведет к ошибке ReferenceError, потому что к переменным «x» и «y» нет доступа снаружи блока и их значения не возвращаются (не всплывают).
Разница между «let» и «const» состоит в том, что в первом случае мы может менять значение переменной, а во втором — нет (константа). При этом, мы можем менять значение свойства объекта, объявленного с помощью const, но не само свойство (переменную).
43. Что такое стрелочные функции (Arrow Functions)?
Стрелочная функция — это относительно новый способ создания функций в JS. Стрелочные функции создаются быстрее и имеют более читаемый синтаксис, чем функциональные выражения. В стрелочных функциях опускается слово «function»:
// ES5 var getCurrentDate = function() < return new Date() >// ES6 const getCurrentDate = () => new Date()
В функциональном выражении мы используем ключевое слово «return» для возврата значения. В стрелочной функции мы этого не делаем, поскольку стрелочные функции неявно возвращают значения при условии, что мы возвращаем одно выражение или значение:
// ES5 function greet(name) < return 'Hello ' + name + '!' >// ES6 const greet = (name) => `Hello $` const greet2 = name = > `Hello $`
Мы также можем передавать параметры стрелочным функциям. Если мы передаем один параметр, его можно не оборачивать в круглые скобки:
const getArgs = () => arguments const getArgs2 = (. rest) => rest
У стрелочных функций нет доступа к объекту arguments. Поэтому вызов первой функции приведет к ошибке. Для получения параметров, переданных функции, мы можем использовать оператор rest.
const data = < result: 0 nums: [1,2,3,4,5] computeResult()< // this ссылается на объект data const addAll = () => < // стрелочные функции копируют значение this из лексического окружения return this.nums.reduce((total, cur) =>total + cur, 0) > this.result = addAll() > >
44. Что такое классы (Classes)?
Классы — это относительно новый способ написания функций-конструкторов в JS. Это синтаксический сахар для функций-конструкторов. В основе классов лежат те же прототипы и прототипное наследование:
// ES5 function Person(firstName, lastName, age, address) < this.firstName = firstName this.lastName = lastName this.age = age this.address = address >Person.self = function() < return this >Person.prototype.toString = function() < return '[object Person]' >Person.prototype.getFullName = function() < return this.firstName + ' ' + this.lastName >// ES6 class Person < constructor(firstName, lastName, age, address)< this.firstName = firstName this.lastName = lastName this.age = age this.address = address >static self() < return this >toString() < return '[object Person]' >getFullName() < return `$$` > >
Переопределение методов и наследование от другого класса:
// ES5 Employee.prototype = Object.create(Person.prototype) function Employee(firstName, lastName, age, address, jobTitle, yearStarted) < Person.call(this, firstName, lastName, age, address) this.jobTitle = jobTitle this.yearStarted = yearStarted >Employee.prototype.describe = function() < return `I am $and I have a position of # and I started at $>` > Employee.prototype.toString = function() < return '[object Employee]' >// ES6 class Employee extends Person < // наследуемся от Person constructor(firstName, lastName, age, address, jobTitle, yearStarted)< super(firstName, lastName, age, address) this.jobTitle = jobTitle this.yearStarted = yearStarted >describe() < return `I am $and I have a position of # and I started at $>` > toString() < // переопределяем метод toString класса Person return '[object Employee]' >>
Как узнать об использовании прототипов?
class Something < >function AnotherSomething() < >const as = new AnotherSomething() const s = new Something() console.log(typeof Something) // function console.log(typeof AnotherSomething) // function console.log(as.toString()) // [object Object] console.log(a.toString()) // [object Object] console.log(as.toString === Object.prototype.toString) console.log(a.toString === Object.prototype.toString) // в обоих случаях получаем true // Object.prototype находится на вершине цепочки прототипов // Something и AnotherSomething наследуют от Object.prototype
45. Что такое шаблонные литералы (Template Literals)?
Шаблонные литералы — относительно новый способ создания строк в JS. Шаблонные литералы создаются с помощью двойных обратных кавычек (``):
// ES5 var greet = 'Hi I\'m Mark' // ES6 let greet = `Hi I'm Mark`
В шаблонных литералах нам не нужно экранировать одинарные кавычки.
// ES5 var lastWords = '\n' + ' I \n' + ' am \n' + 'Iron Man \n' // ES6 let lastWords = ` I am Iron Man `
В ES6 нам не нужно использовать управляющую последовательность "\n" для перевода строки.
// ES5 function greet(name) < return 'Hello ' + name + '!' >// ES6 function greet(name)< return `Hello $!` >
В ES6 нам не нужно использовать конкатенацию строк для объединения текста с переменной: мы можем использовать выражение $ для получения значения переменной.
46. Что такое деструктуризация объекта (Object Destructuring)?
Деструктуризация — относительно новый способ получения (извлечения) значений объекта или массива.
Допустим, у нас есть такой объект:
const employee =
Раньше для получения свойств объекта мы создавали переменные для каждого свойства. Это было очень скучно и сильно раздражало:
var firstName = employee.firstName var lastName = employee.lastName var position = employee.position var yearHired = employee.yearHired
Использование деструктуризации позволяет сделать код чище и отнимает меньше времени. Синтаксис деструктуризации следующий: заключаем свойства объекта, которые хотим получить, в фигурные скобки (< >), а если речь идет о массиве — в квадратные скобки ([ ]):
let < firstName, lastName, position, yearHired >= employee
Для изменения имени переменной следует использовать «propertyName: newName»:
let < firstName: fName, lastName: lName, position, yearHired >= employee
Для присвоения переменным значения по умолчанию следует использовать «propertyName = 'defaultValue'»:
let < firstName = 'Mark', lastName: lName, position, yearHired >= employee
47. Что такое модули (Modules)?
Модули позволяют объединять (использовать) код из разных файлов и избавляют нас от необходимости держать весь код в одном большом файле. До появления модулей в JS существовало две популярные системы модулей для поддержки кода:
- CommonJS — Nodejs
- AMD (AsyncronousModuleDefinition) — Browsers
Экспорт функциональности в другой файл (именной экспорт):
// ES5 CommonJS - helpers.js exports.isNull = function(val) < return val === null >exports.isUndefined = function(val) < return val === undefined >exports.isNullOrUndefined = function(val) < return exports.isNull(val) || exports.isUndefined(val) >// ES6 модули export function isNull(val) < return val === null; >export function isUndefined(val) < return val === undefined; >export function isNullOrUndefined(val)
Импорт функциональности в другой файл:
// ES5 CommonJS - index.js const helpers = require('./helpers.js') const isNull = helpers.isNull const isUndefined = helpers.isUndefined const isNullOrUndefined = helpers.isNullOrUndefined // либо с помощью деструктуризации const < isNull, isUndefined, isNullOrUndefined >= require('./helpers.js') // ES6 модули import * as helpers from './helpers.js' // helpers - это объект // либо import < isNull, isUndefined, isNullOrUndefined as isValid>from './helpers.js' // используем "as" для переименовывания
Экспорт по умолчанию:
// ES5 CommonJS - index.js class Helpers < static isNull(val)< return val === null >static isUndefined(val) < return val === undefined >static isNullOrUndefined(val) < return this.isNull(val) || this.isUndefined(val) >> module.exports = Helpers // ES6 модули class Helpers < static isNull(val)< return val === null >static isUndefined(val) < return val === undefined >static isNullOrUndefined(val) < return this.isNull(val) || this.isUndefined(val) >> export default Helpers
// ES5 CommonJS - index.js const Helpers = require('./helpers.js') console.log(Helpers.isNull(null)) // ES6 модули import Helpers from './helpers.js' console.log(Helpers.isNull(null))
Это базовое использование модулей. Я не стал вдаваться в подробности, поскольку мой пост и без того получается слишком большим.
48. Что такое объект Set?
Объект Set позволяет хранить уникальные значения, примитивы и ссылки на объекты. Еще раз: в Set можно добавлять только уникальные значения. Он проверяет хранящиеся в нем значения с помощью алгоритма SameZeroValue.
Экземпляр Set создается с помощью конструктора Set. Мы также можем передать ему некоторые значения при создании:
const set1 = new Set() const set2 = new Set(['a','b','c','d','d','e']) // вторая "d" не добавится
Мы можем добавлять значения в Set, используя метод add. Поскольку метод add является возвращаемым, мы может использовать цепочку вызовов:
set2.add('f') set2.add('g').add('h').add('i').add('j').add('k').add('k') // вторая "k" не добавится
Мы можем удалять значения из Set, используя метод delete:
set2.delete('k') // true set2.delete('z') // false, потому что в set2 нет такого значения
Мы можем проверить наличие свойства в Set, используя метод has:
set2.has('a') // true set2.has('z') // false
Для получения длины Set используется метод size:
set2.size // 10
Метод clear очищает Set:
set2.clear() // пусто
Мы можем использовать Set для удаления повторяющихся значений в массиве:
const nums = [1,2,3,4,5,6,6,7,8,8,5] const uniqNums = [. new Set(nums)] // [1,2,3,4,5,6,7,8]
49. Что такое функция обратного вызова (Callback Function)?
Функция обратного вызова — это функция, вызов которой отложен на будущее (происходит при некоторых условиях, например, при наступлении события).
const btnAdd = document.getElementById('btnAdd') btnAdd.addEventListener('click', function clickCallback(e)) < // делаем нечто полезное >
В примере мы ждем события «клик» на элементе с идентификатором «btnAdd». По клику вызывается функция clickCallback. Функция обратного вызова добавляет некоторый функционал данным или событию. Методам reduce, filter и map в качестве второго аргумента передается функция обратного вызова. Хорошей аналогией callback является следующая ситуация: Вы звоните кому-то, он не отвечает, Вы оставляете ему сообщение и ждете, когда он перезвонит. Звонок или сообщение — это событие или данные, а callback — это ожидание (предвосхищение) встречного звонка.
50. Что такое промисы (Promises)?
Промисы — это один из приемов работы с асинхронным кодом в JS. Они возвращают результат асинхронной операции. Промисы были придуманы для решения проблемы так называемого «ада функций обратного вызова».
fs.readFile('somefile.txt', function(e, data) < if(e)< console.log(e) >console.log(data) >)
Проблемы при таком подходе начинаются, когда нам необходимо добавить еще одну асинхронную операцию в первую (внутрь первой), затем еще одну и т.д. В результате мы получаем беспорядочный и нечитаемый код:
fs.readFile('somefile.txt', function(e,data)< // код fs.readFile('directory', function(e, files)< // код fs.mkdir('directory', function(e)< // код >) >) >)
А вот как это выглядит с промисами:
promReadFile('file/path') .then(data => < return promReaddir('directory') >) .then(data => < return promMkdir('directory') >) .catch(e => < console.error(e) >)
У промиса есть четыре состояния:
- Ожидание — начальное состояние промиса. Результата промиса неизвестен, поскольку операция не завершена.
- Выполнено — асинхронная операция выполнена, имеется результат.
- Отклонено — асинхронная операция не выполнена, имеется причина.
- Завершено — выполнено или отклонено.
const myPromiseAsync = (. args) => < return new Promise((resolve, reject) => < doSomeAsync(. args, (error, data) => < if(error)< reject(error) >else < resolve(data) >>) >) > myPromiseAsync() .then(result => < console.log(result) >) .catch(reason => < console.error(reason) >)
Мы можем создать вспомогательную функцию для преобразования асинхронной операции с callback в промис. Она будет работать наподобие util из Node.js («промисификация»):
const toPromise = (asyncFuncWithCallback) => < return (. args) => < return new Promise((res, rej) => < asyncFuncWithCallback(. args, (e, result) =>< return e ? rej(e) : res(result) >) >) > > const promiseReadFile = toPromise(fs.readFile) promiseReadFile('file/path') .then((data) => < console.log(data) >) .catch(e => console.error(e))
Подробнее о промисах можно почитать здесь и здесь.
51. Что такое async/await?
Async/await — относительно новый способ написания асинхронного (неблокирующего) кода в JS. Им оборачивают промис. Он делает код более читаемым и чистым, чем промисы и функции обратного вызова. Однако для использования async/await необходимо хорошо знать промисы.
// промис function callApi() < return fetch('url/to/api/endpoint') .then(resp =>resp.json()) .then(data => < // работаем с данными >).catch(err => < // работаем с ошибкой >) > // async/await // для перехвата ошибок используется try/catch async function callApi() < try< const resp = await fetch('url/to/api/endpoint') const data = await res.json() // работаем с данными >catch(e) < // работаем с ошибкой >>
Запомните: использование ключевого слова «async» перед функцией заставляет ее возвращать промис:
const giveMeOne = async () = 1 giveMeOne() .then((num) => < console.log(num) // 1 >)
Ключевое слово «await» можно использовать только внутри асинхронной функции. Использование «await» внутри другой функции приведет к ошибке. Await ожидает завершения выражения справа, чтобы вернуть его значение перед выполнением следующей строчки кода.
const giveMeOne = async() => 1 function getOne() < try< const num = await giveMeOne() console.log(num) >catch(e) < console.log(e) >> // Uncaught SyntaxError: await is only valid in an async function async function getTwo() < try< const num1 = await giveMeOne() const nm2 = await giveMeOne() return num1 + num2 >catch(e) < console.log(e) >> await getTwo() // 2
Подробнее об async/await можно почитать здесь и здесь.
52. В чем разница между spread-оператором и rest-оператором?
Операторы spread и rest имеют одинаковый синтаксис (". "). Разница состоит в том, что с помощью spread мы передаем или распространяем данные массива на другие данные, а с помощью rest — получаем все параметры функции и помещаем их в массив (или извлекаем часть параметров).
function add(a, b) < return a + b >const nums = [5, 6] const sum = add(. nums) console.log(sum) // 11
В этом примере мы используем spread при вызове функции add с данными массива nums. Значением переменной «a» будет 5, b = 6, sum = 11.
function add(. rest) < return rest.reduce((total, current) =>total + current) > console.log(add(1, 2)) // 3 console.log(add(1, 2, 3, 4, 5)) // 15
Здесь мы вызываем функцию add с любым количеством аргументов. Add возвращает сумму этих аргументов.
const [first, . others] = [1, 2, 3, 4, 5] console.log(first) // 1 console.log(others) // [2, 3, 4, 5]
В этом примере мы используем rest для помещения любого количества параметров, кроме первого, в массив others.
53. Что такое параметры по умолчанию (Default Parameters)?
Это относительно новый способ определения значений переменных по умолчанию.
// ES5 function add(a,b) < a = a || 0 b = b || 0 return a + b >// ES6 function add(a = 0, b = 0) < return a + b >// если мы не присвоим переменным "a" и "b" какие-нибудь значения, они будут равняться 0 add(1) // 1
Можно использовать деструктуризацию:
function getFirst([first, . rest] = [0, 1]) < return first >getFirst() // 0 getFirst([10,20,30]) // 10 function getArr( < nums >= < nums: [1,2,3,4] >) < return nums >getArr // [1,2,3,4] getArr() // [5,4,3,2,1]
Мы даже можем использовать по умолчанию параметры, объявленные в том же месте:
function doSomethingWithValue(value = 'Hello World', callback = () => < console.log(value) >) < callback() >doSomethingWithValue() // Hello World
54. Что такое объектная обертка (Wrapper Objects)?
Примитивы строка, число и boolean имеют свойства и методы, несмотря на то, что они не являются объектами:
let name = 'marko' console.log(typeof name) // string console.log(name.toUpperCase()) // MARKO
Name — это строка (примитивный тип), у которого нет свойств и методов, но когда мы вызываем метод toUpperCase(), это приводит не к ошибке, а к «MARKO».
Причина такого поведения заключается в том, что name временно преобразуется в объект. У каждого примитива, кроме null и undefined, есть объект-обертка. Такими объектами являются String, Number, Boolean, Symbol и BigInt. В нашем случае код принимает следующий вид:
console.log(new String(name).toUpperCase()) // MARKO
Временный объект отбрасывается по завершении работы со свойством или методом.
55. В чем разница между явным и неявным преобразованием или приведением к типу (Implicit and Explicit Coercion)?
Неявное преобразование — это способ приведения значения к другому типу без нашего ведома (участия).
Предположим, у нас есть следующее:
console.log(1 + '6') console.log(false + true) console.log(6 * '2')
Результатом первого console.log будет 16. В других языках это привело бы к ошибке, но в JS 1 конвертируется в строку и конкатенируется (присоединяется) c 6. Мы ничего не делали, преобразование произошло автоматически.
Результатом второго console.log будет 1. False было преобразовано в 0, true — в 1. 0 + 1 = 1.
Результатом третьего console.log будет 12. Строка 2 была преобразована в число перед умножением на 6.
Явное преобразование предполагает наше участие в приведении значения к другому типу:
console.log(1 + parseInt('6'))
В этом примере мы используем parseInt для приведения строки 6 к числу, затем складываем два числа и получаем 7.
56. Что такое NaN? Как проверить, является ли значение NaN?
NaN или Not A Number (не число) — это значение, получаемое в результате выполнения числовой операции над нечисловым значением:
let a console.log(parseInt('abc')) console.log(parseInt(null)) console.log(parseInt(undefined)) console.log(parseInt(++a)) console.log(parseInt(<> * 10)) console.log(parseInt('abc' - 2)) console.log(parseInt(0 / 0)) console.log(parseInt('10a' * 10))
В JS есть встроенный метод isNaN, позволяющий проверять, является ли значение NaN, но он ведет себя довольно странно:
console.log(isNaN()) // true console.log(isNaN(undefined)) // true console.log(isNaN(<>)) // true console.log(isNaN(String('a'))) // true console.log(isNaN(() => < >)) // true
Результатом всех console.log является true, несмотря на то, что ни одно из значений не является NaN.
ES6 для проверки, является ли значение NaN, рекомендует использовать метод Number.isNaN. Мы также можем написать вспомогательную функцию для решения проблемы «неравенства NaN самому себе»:
function checkIsNan(value)
57. Как проверить, является ли значение массивом?
Для этого следует использовать метод Array.isArray:
console.log(Array.isArray(5)) // false console.log(Array.isArray('')) // false console.log(Array.isArray()) // false console.log(Array.isArray(null)) // false console.log(Array.isArray( )) // false console.log(Array.isArray([])) // true
Если среда, в которой Вы работаете, не поддерживает данный метод, можете использовать такой полифил:
function isArray(value)
58. Как проверить, что число является четным, без использования деления по модулю или деления с остатком (оператора "%")?
Для решения данной задачи можно использовать оператор "&" (бинарное и). Оператор "&" сравнивает операнды как бинарные значения.
function isEven(num) < if(num & 1)< return false >else < return true >>
0 в бинарной системе счисления это 000
1 — это 001
2 — 010
3 — 011
4 — 100
5 — 101
6 — 110
7 — 111
и т.д.
Console.log(5 & 1) вернет 1. Сначала оператор "&" конвертирует оба числа в бинарные значения, 5 превращается в 101, 1 — в 001. Затем производится побитовое сравнение:
Сравниваем 1 и 0, получаем 0.
Сравниваем 0 и 0, получаем 0.
Сравниваем 1 и 1, получаем 1.
Преобразуем бинарное значение в целое число, получаем 1.
Если эта информация кажется Вам слишком сложной, мы можем решить поставленную задачу с помощью рекурсивной функции:
function isEven(num)
59. Как определить наличие свойства в объекте?
Существует три способа это сделать.
Первый способ состоит в использовании оператора «in»:
const o = < 'prop': 'bwahahah', 'prop2': 'hweasa' >console.log('prop' in o) // true console.log('prop1' in o) // false
Второй — использовать метод hasOwnProperty:
console.log(o.hasOwnProperty('prop2')) // true console.log(o.hasOwnProperty('prop1')) // false
Третий — индексная нотация массива:
console.log(o['prop']) // bwahahah console.log(o['prop1']) // undefined
60. Что такое AJAX?
AJAX или Asyncronous JavaScript and XML — это набор взаимосвязанных технологий, которые позволяют работать с данными в асинхронном режиме. Это означает, что мы можем отправлять данные на сервер и получать данные с него без перезагрузки веб-страницы.
AJAX использует следующие технологии:
HTML — структура веб-страницы.
CSS — стили веб-страницы.
JavaScript — поведение страницы и работа с DOM.
XMLHttpRequest API — отправка и получение данных с сервера.
PHP, Python, Nodejs — какой-нибудь серверный язык.
61. Как в JS создать объект?
const o = < name: 'Mark', greeting()< return `Hi, I'm $` > > o.greeting // Hi, I'm Mark
function Person(name) < this.name = name >Person.prototype.greeting = function()< return `Hi, I'm $` > const mark = new Person('Mark') mark.greeting() // Hi, I'm Mark
const n = < greeting()< return `Hi, I'm $` > > const o = Object.create(n) o.name = 'Mark' console.log(o.greeting) // Hi, I'm Mark
62. В чем разница между методами Object.freeze и Object.seal?
Разница заключается в том, что при использовании метода Object.freeze мы не можем менять или редактировать свойства объекта, а при использовании Object.seal у нас такая возможность имеется.
63. В чем разница между оператором «in» и методом hasOwnProperty?
Отличие состоит в том, что оператор «in» проверяет наличие свойства не только в самом объекте, но и в его прототипах, а метод hasOwnProperty — только в объекте.
console.log('prop' in o) // true console.log('toString' in o) // true console.log(o.hasOwnProperty('prop')) // true console.log(o.hasOwnProperty('toString')) // false
64. Какие приемы работы с асинхронным кодом в JS Вы знаете?
- Функции обратного вызова (Callbacks).
- Промисы (Promises).
- Async/await.
- Библиотеки вроде async.js, blueprint, q, co.
65. В чем разница между обычной функцией и функциональным выражением?
Допустим, у нас есть следующее:
hoistedFunc() notHoistedFunc() function hoistedFunc() < console.log('I am hoisted') >var notHoistedFunc = function()
Вызов notHoistedFunc приведет к ошибке, а вызов hoistedFunc нет, потому что hoistedFunc «всплывает», поднимается в глобальную область видимости, а notHoistedFunc нет.
66. Как в JS вызвать функцию?
В JS существует 4 способа вызвать функцию. Вызов определяет значение this или «владельца» функции.
Вызов в качестве функции. Если функция вызывается как метод, конструктор или с помощью методов apply или call, значит она вызывается как функция. Владельцем такой функции является объект window:
function add(a,b) < console.log(this) return a + b >add(1,5) // window, 6 const o = < method(callback)< callback() >> o.method(function()< console.log(this) // window >)
Вызов в качестве метода. Когда функция является свойством объекта, мы называем ее методом. Когда вызывается метод, значением this становится объект этого метода:
const details = < name: 'Marko', getName()< return this.name >> details.getName() // Marko, значением this является объект details
Вызов в качестве конструктора. Когда функция вызывается с использованием ключевого слова «new», мы называем такую функцию конструктором. При этом создается пустой объект, являющийся значением this:
function Employee(name, position, yearHired) < // создается пустой объект, являющийся значением this // this = <>this.name = name this.position = position this.yearHired = yearHired // наследование от Employee.prototype неявно возвращает this, если не указано иное > const emp = new Employee('Marko Polo', 'Software Development', 2017)
Вызов с помощью методов apply или call. Мы используем эти методы, когда хотим явно определить значение this или владельца функции:
const obj1 = < result: 0 >const obj2 = < result: 0 >function reduceAdd() < let result = 0 for(let i = 0, len = arguments.length; i < len; i++)< result += arguments[i] >this.result = result > reduceAdd.apply(obj1, [1,2,3,4,5]) // значением this является obj1 reduceAdd.call(obj2, 1,2,3,4,5) // значением this является obj2
67. Что такое запоминание или мемоизация (Memoization)?
Мемоизация — это прием создания функции, способной запоминать ранее вычисленные результаты или значения. Преимущество мемоизации заключается в том, что мы избегаем повторного выполнения функции с одинаковыми аргументами. Недостатком является то, что мы вынуждены выделять дополнительную память для сохранения результатов.
68. Как бы Вы реализовали вспомогательную функцию запоминания?
function memoize(fn) < const cache = <>return function(param) < if(cache[param])< console.log('cached') return cache[param] >else < let result = fn(param) cache[param] = result console.log('not cached') return result >> > const toUpper = (str = '') => str.toUpperCase() const toUpperMemoized = memoize(toUpper) toUpperMemoized('abcdef') toUpperMemoized('abcdef') // не выполнится
Мы реализовали функцию мемоизации с одним аргументом. Сделаем ее «мультиаргументной»:
const slice = Array.prototype.slice function memoize(fn) < const cache = <>return (. args) => < const params = slice.call(args) console.log(params) if(cache[params])< console.log('cached') return cache[params] >else < let result = fn(. args) cache[params] = result console.log('not cached') return result >> > const makeFullName = (fName, lName) => `$ $` const reduceAdd = (numbers, startValue = 0) => numbers.reduce((total, cur) => total + cur, startValue) const memoizedFullName = memoize(makeFullName) const memoizeReduceAdd = memoize(reduceAdd) memoizedFullName('Marko', 'Polo') memoizedFullName('Marko', 'Polo') // не выполнится memoizeReduceAdd([1,2,3,4],5) memoizeReduceAdd([1,2,3,4],5) // не выполнится
69. Почему typeof null возвращает object? Как проверить, является ли значение null?
typeof null == 'object' всегда будет возвращать true по историческим причинам. Поступало предложение исправить эту ошибку, изменив typeof null = 'object' на typeof null = 'null', но оно было отклонено в интересах сохранения обратной совместимости (такое изменение повлекло бы за собой большое количество ошибок).
Для проверки, является ли значение null можно использовать оператор строгого равенства (===):
function isNull(value)
70. Для чего используется ключевое слово «new»?
Ключевое слово «new» используется в функциях-конструкторах для создания нового объекта (нового экземпляра класса).
Допустим, у нас есть такой код:
function Employee(name, position, yearHired) < this.name = name this.position = position this.yearHired = yearHired >const emp = new Employee('Marko Polo', 'Software Development', 2017)
Ключевое слово «new» делает 4 вещи:
- Создает пустой объект.
- Привязывает к нему значение this.
- Функция наследует от functionName.prototype.
- Возвращает значение this, если не указано иное.
- javascript
- программирование
- разработка