70 https://habr.com/ru/articles/486820/ Flashcards
В чем разница между методами Object.freeze и Object.seal?
Разница заключается в том, что при использовании метода Object.freeze мы не можем менять или редактировать свойства объекта, а при использовании Object.seal у нас такая возможность имеется. При использовании Obkect.seal нельзя добавлять или удалять поля
В чем разница между оператором 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
В чем разница между обычной функцией и функциональным выражением?
hoistedFunc()
notHoistedFunc()
function hoistedFunc(){
console.log(‘I am hoisted’)
}
var notHoistedFunc = function(){
console.log(‘I will not be hoisted!’)
}
Вызов notHoistedFunc приведет к ошибке, а вызов hoistedFunc нет, потому что hoistedFunc «всплывает», поднимается в глобальную область видимости, а notHoistedFunc нет.
Как в 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
Что такое запоминание или мемоизация (Memoization)?
Мемоизация — это прием создания функции, способной запоминать ранее вычисленные результаты или значения. Преимущество мемоизации заключается в том, что мы избегаем повторного выполнения функции с одинаковыми аргументами. Недостатком является то, что мы вынуждены выделять дополнительную память для сохранения результатов.
Как бы Вы реализовали вспомогательную функцию запоминания?
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) => '${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) // не выполнится
Почему typeof null возвращает object? Как проверить, является ли значение null?
typeof null == ‘object’ всегда будет возвращать true по историческим причинам. Поступало предложение исправить эту ошибку, изменив typeof null = ‘object’ на typeof null = ‘null’, но оно было отклонено в интересах сохранения обратной совместимости (такое изменение повлекло бы за собой большое количество ошибок).
Для проверки, является ли значение null можно использовать оператор строгого равенства (===):
function isNull(value){ return value === null }
Для чего используется ключевое слово «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, если не указано иное.
В чем разница между 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
Ладно, теперь о различиях.
undefined («неопределенный») представляет собой значение по умолчанию:
переменной, которой не было присвоено значения, т.е. объявленной, но не инициализированной переменной;
функции, которая ничего не возвращает явно, например, console.log(1);
несуществующего свойства объекта.
В указанных случаях движок JS присваивает значение undefined.
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 при использовании оператора “===”. О том, почему так происходит, см. ниже.
console.log(null == undefined) // true
console.log(null === undefined) // false
Для чего используется оператор “&&”?
Оператор “&&” (логическое и) находит и возвращает первое ложное значение либо последний операнд, когда все значения истинные. Он использует короткое замыкание во избежание лишних затрат:
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()
}
})
Для чего используется оператор “||”?
Оператор “||” (логическое или) находит и возвращает первое истинное значение. Он также использует короткое замыкание. Данный оператор использовался для присвоения параметров по умолчанию в функциях до того, как параметры по умолчанию были стандартизированы в ES6.
console.log(null || 1 || undefined) // 1 function logName(name) { let n = name || Mark console.log(n) } logName() // Mark
Является ли использование унарного плюса (оператор “+”) самым быстрым способом преобразования строки в число?
Согласно MDN оператор “+” действительно является самым быстрым способом преобразования строки в число, поскольку он не выполняет никаких операций со значением, которое является числом.
Что такое DOM?
DOM или Document Object Model (объектная модель документа) — это прикладной программный интерфейс (API) для работы с HTML и XML документами. Когда браузер первый раз читает («парсит») HTML документ, он формирует большой объект, действительно большой объект, основанный на документе — DOM. DOM представляет собой древовидную структуру (дерево документа). DOM используется для взаимодействия и изменения самой структуры DOM или его отдельных элементов и узлов.
В JS DOM представлен объектом Document. Объект Document имеет большое количество методов для работы с элементами, их созданием, модификацией, удалением и т.д.
Что такое распространение события (Event Propagation)?
огда какое-либо событие происходит в элементе DOM, оно на самом деле происходит не только в нем. Событие «распространяется» от объекта Window до вызвавшего его элемента (event.target). При этом событие последовательно пронизывает (затрагивает) всех предков целевого элемента. Распространение события имеет три стадии или фазы:
Фаза погружения (захвата, перехвата) — событие возникает в объекте Window и опускается до цели события через всех ее предков.
Целевая фаза — это когда событие достигает целевого элемента.
Фаза всплытия — событие поднимается от event.target, последовательно проходит через всех его предков и достигает объекта Window.
Подробнее о распространении событий можно почитать здесь и здесь.
Что такое всплытие события?
Когда событие происходит в элементе DOM, оно затрагивает не только этот элемент. Событие «всплывает» (подобно пузырьку воздуха в воде), переходит от элемента, вызвавшего событие (event.target), к его родителю, затем поднимается еще выше, к родителю родителя элемента, пока не достигает объекта Window.
Допустим, у нас есть такая разметка:
<div class="grandparent"> <div class="parent"> <div class="child">1</div> </div> </div>
И такой JS:
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. Вот что такое всплытие события.
Что такое погружение события?
Когда событие происходит в элементе DOM, оно происходит не только в нем. В фазе погружения событие опускается от объекта Window до цели события через всех его предков.
Разметка:
<div class="grandparent"> <div class="parent"> <div class="child">1</div> </div> </div>
JS:
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. Это и есть погружение события.
В чем разница между методами event.preventDefault() и event.stopPropagation()?
Метод event.preventDefault() отключает поведение элемента по умолчанию. Если использовать этот метод в элементе form, то он предотвратит отправку формы (submit). Если использовать его в contextmenu, то контекстное меню будет отключено (данный метод часто используется в keydown для переопределения клавиатуры, например, при создании музыкального/видео плеера или текстового редактора — прим. пер.). Метод event.stopPropagation() отключает распространение события (его всплытие или погружение).
Как узнать об использовании метода event.preventDefault()?
Для этого мы можем использовать свойство event.defaulPrevented, возвращающее логическое значение, служащее индикатором применения к элементу метода event.preventDefault.
Почему obj.someprop.x приводит к ошибке?
const obj = {}
console.log(obj.someprop.x)
Ответ очевиден: мы пытается получить доступ к свойству x свойства someprop, которое имеет значение undefined. obj.__proto__.__proto = null, поэтому возвращается undefined, а у undefined нет свойства x.
Что такое цель события или целевой элемент (event.target)?
Простыми словами, event.target — это элемент, в котором происходит событие, или элемент, вызвавший событие.
Имеем такую разметку:
<div onclick="clickFunc(event)" style="text-align: center; margin: 15px; border: 1px solid red; border-radius: 3px;"> <div style="margin: 25px; border: 1px solid royalblue; border-radius: 3px;"> <div style="margin: 25px; border: 1px solid skyblue; border-radius: 3px;"> <button style="margin: 10px"> Button </button> </div> </div> </div>
И такой простенький JS:
function clickFunc(event) { console.log(event.target) }
Мы прикрепили «слушатель» к внешнему div. Однако если мы нажмем на кнопку, то получим в консоли разметку этой кнопки. Это позволяет сделать вывод, что элементом, вызвавшим событие, является именно кнопка, а не внешний или внутренние div.
Что такое текущая цель события (event.currentTarget)?
Event.currentTarget — это элемент, к которому прикреплен прослушиватель событий.
Аналогичная разметка:
<div onclick="clickFunc(event)" style="text-align: center;margin:15px; border:1px solid red;border-radius:3px;"> <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;"> <div style="margin:25px;border:1px solid skyblue;border-radius:3px;"> <button style="margin:10px"> Button </button> </div> </div> </div>
И немного видоизмененный JS:
function clickFunc(event) { console.log(event.currentTarget) }
Мы прикрепили слушатель к внешнему div. Куда бы мы ни кликнули, будь то кнопка или один из внутренних div, в консоли мы всегда получим разметку внешнего div. Это позволяет заключить, что event.currentTarget — это элемент, к которому прикреплен прослушиватель событий.
В чем разница между операторами “==” и “===”?
Разница между оператором “==” (абстрактное или нестрогое равенство) и оператором “===” (строгое равенство) состоит в том, что первый сравнивает значения после их преобразования или приведения к одному типу (Coersion), а второй — без такого преобразования.
Давайте копнем глубже. И сначала поговорим о преобразовании.
Преобразование представляет собой процесс приведения значения к другому типу или, точнее, процесс приведения сравниваемых значений к одному типу. При сравнении оператор “==” производит так называемое неявное сравнение. Оператор “==” выполняет некоторые операции перед сравнением двух значений.
Допустим, мы сравниваем x и y.
Алгоритм следующий:
Если x и y имеют одинаковый тип, сравнение выполняется с помощью оператора “===”.
Если x = null и y = undefined возвращается true.
Если x = undefined и y = null возвращается true.
Если x = число, а y = строка, возвращается x == toNumber(y) (значение y преобразуется в число).
Если x = строка, а y = число, возвращается toNumber(x) == y (значение x преобразуется в число).
Если x = логическое значение, возвращается toNumber(x) == y.
Если y = логическое значение, возвращается x == toNumber(y).
Если x = строка, символ или число, а y = объект, возвращается x == toPrimitive(y) (значение y преобразуется в примитив).
Если x = объект, а y = строка, символ или число, возвращается toPrimitive(x) == y.
Возвращается false.
Почему результатом сравнения двух похожих объектов является 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» — на разные объекты с одинаковыми свойствами и значениями.
Для чего используется оператор “!!”?
Оператор “!!” (двойное отрицание) приводит значение справа от него к логическому значению.
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
Как записать несколько выражений в одну строку?
Для этого мы можем использовать оператор “,” (запятая). Этот оператор «двигается» слева направо и возвращает значение последнего выражения или операнда.
let x = 5 x = (x++, x = addFive(x), x *= 2, x -= 5, x += 10) function addFive(num) { return num + 5 }
Если мы выведем значение x в консоль, то получим 27. Сначала мы увеличиваем значение x на единицу (x = 6). Затем вызываем функцию addFive() с параметром 6, к которому прибавляем 5 (x = 11). После этого мы умножаем значение x на 2 (x = 22). Затем вычитаем 5 (x = 17). И, наконец, прибавляем 10 (x = 27).
Что такое поднятие (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')) */
После завершения фазы компиляции начинается фаза выполнения, когда переменным присваиваются значения и вызываются функции.
Что такое область видимости (Scope)?
https://learn.javascript.ru/closure
https://developer.mozilla.org/ru/docs/Web/JavaScript/Closures
Область видимости — это место, где (или откуда) мы имеем доступ к переменным или функциям. 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' являются ближайшими // к внутренней области видимости переменными
Что такое замыкание (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».
Какие значения в JS являются ложными?
const falsyValues = [’’, 0, null, undefined, NaN, false]
Ложными являются значения, результатом преобразования которых в логическое значение является false.
Как проверить, является ли значение ложным?
Следует использовать функцию Boolean или оператор “!!” (двойное отрицание).