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