70 вопросов по JavaScript для подготовки к собеседованию

Original author: Mark A
  • Translation
Доброго времени суток, друзья!

Надеюсь, эта статья будет полезна как начинающим разработчикам, так и опытным.

В вопросах, которые показались мне сложнее прочих, приведены ссылки на дополнительную литературу.

Буду признателен за развернутые комментарии. Все замечания будут учтены при редактировании статьи.

Итак, поехали.

70 вопросов по JavaScript для подготовки к собеседованию


Вопросы:
1. В чем разница между null и undefined?
2. Для чего используется оператор "&&"?
3. Для чего используется оператор "||"?
4. Является ли использование унарного плюса (оператор "+") самым быстрым способом преобразования строки в число?
5. Что такое DOM?
6. Что такое распространение события (Event Propogation)?
7. Что такое всплытие события (Event Bubbling)?
8. Что такое погружение события (Event Capturing)?
9. В чем разница между методами event.preventDefault() и event.stopPropagation()?
10. Как узнать об использовании метода event.preventDefault()?
11. Почему obj.someprop.x приводит к ошибке?
12. Что такое цель события или целевой элемент (event.target)?
13. Что такое текущая цель события (event.currentTarget)?
14. В чем разница между операторами "==" и "==="?
15. Почему результатом сравнения двух похожих объектов является false?
16. Для чего используется оператор "!!"?
17. Как записать несколько выражений в одну строку?
18. Что такое поднятие (Hoisting)?
19. Что такое область видимости (Scope)?
20. Что такое замыкание (Closures)?
21. Какие значения в JS являются ложными?
22. Как проверить, является ли значение ложным?
23. Для чего используется директива «use strict»?
24. Какое значение имеет this?
25. Что такое прототип объекта?
26. Что такое IIFE?
27. Для чего используется метод Function.prototype.apply?
28. Для чего используется метод Function.prototype.call?
29. В чем разница между методами call и apply?
30. Для чего используется метод Function.prototype.bind?
31. Что такое функциональное программирование и какие особенности JS позволяют говорить о нем как о функциональном языке программирования?
32. Что такое функции высшего порядка (Higher Order Functions)?
33. Почему функции в JS называют объектами первого класса (First-class Objects)?
34. Как бы Вы реализовали метод Array.prototype.map?
35. Как бы Вы реализовали метод Array.prototype.filter?
36. Как бы Вы реализовали метод Array.prototype.reduce?
37. Что такое объект arguments?
38. Как создать объект, не имеющий прототипа?
39. Почему в представленном коде переменная b становится глобальной при вызове функции?
40. Что такое ECMAScript?
41. Что нового привнес в JS стандарт ES6 или ECMAScript2015?
42. В чем разница между ключевыми словами «var», «let» и «const»?
43. Что такое стрелочные функции (Arrow Functions)?
44. Что такое классы (Classes)?
45. Что такое шаблонные литералы (Template Literals)?
46. Что такое деструктуризация объекта (Object Destructuring)?
47. Что такое модули (Modules)?
48. Что такое объект Set?
49. Что такое функция обратного вызова (Callback Function)?
50. Что такое промисы (Promises)?
51. Что такое async/await?
52. В чем разница между spread-оператором и rest-оператором?
53. Что такое параметры по умолчанию (Default Parameters)?
54. Что такое объектная обертка (Wrapper Objects)?
55. В чем разница между явным и неявным преобразованием или приведением к типу (Implicit and Explicit Coercion)?
56. Что такое NaN? Как проверить, является ли значение NaN?
57. Как проверить, является ли значение массивом?
58. Как проверить, что число является четным, без использования деления по модулю или деления с остатком (оператора "%")?
59. Как определить наличие свойства в объекте?
60. Что такое AJAX?
61. Как в JS создать объект?
62. В чем разница между методами Object.freeze и Object.seal?
63. В чем разница между оператором «in» и методом hasOwnProperty?
64. Какие приемы работы с асинхронным кодом в JS Вы знаете?
65. В чем разница между обычной функцией и функциональным выражением?
66. Как в JS вызвать функцию?
67. Что такое запоминание или мемоизация (Memoization)?
68. Как бы Вы реализовали вспомогательную функцию запоминания?
69. Почему typeof null возвращает object? Как проверить, является ли значение null?
70. Для чего используется ключевое слово «new»?

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

Ладно, теперь о различиях.

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

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:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document Object Model</title>
</head>

<body>
    <div>
        <p>
            <span></span>
        </p>
        <label></label>
        <input>
    </div>
</body>

</html>

DOM этого HTML выглядит так:



В JS DOM представлен объектом Document. Объект Document имеет большое количество методов для работы с элементами, их созданием, модификацией, удалением и т.д.

6. Что такое распространение события (Event Propagation)?


Когда какое-либо событие происходит в элементе DOM, оно на самом деле происходит не только в нем. Событие «распространяется» от объекта Window до вызвавшего его элемента (event.target). При этом событие последовательно пронизывает (затрагивает) всех предков целевого элемента. Распространение события имеет три стадии или фазы:
  1. Фаза погружения (захвата, перехвата) — событие возникает в объекте Window и опускается до цели события через всех ее предков.
  2. Целевая фаза — это когда событие достигает целевого элемента.
  3. Фаза всплытия — событие поднимается от event.target, последовательно проходит через всех его предков и достигает объекта Window.



Подробнее о распространении событий можно почитать здесь и здесь.

7. Что такое всплытие события?


Когда событие происходит в элементе 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. Вот что такое всплытие события.

8. Что такое погружение события?


Когда событие происходит в элементе 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. Это и есть погружение события.

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 — это элемент, в котором происходит событие, или элемент, вызвавший событие.

Имеем такую разметку:

<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.

13. Что такое текущая цель события (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 — это элемент, к которому прикреплен прослушиватель событий.

14. В чем разница между операторами "==" и "==="?


Разница между оператором "==" (абстрактное или нестрогое равенство) и оператором "===" (строгое равенство) состоит в том, что первый сравнивает значения после их преобразования или приведения к одному типу (Coersion), а второй — без такого преобразования.

Давайте копнем глубже. И сначала поговорим о преобразовании.

Преобразование представляет собой процесс приведения значения к другому типу или, точнее, процесс приведения сравниваемых значений к одному типу. При сравнении оператор "==" производит так называемое неявное сравнение. Оператор "==" выполняет некоторые операции перед сравнением двух значений.

Допустим, мы сравниваем x и y.

Алгоритм следующий:

  1. Если x и y имеют одинаковый тип, сравнение выполняется с помощью оператора "===".
  2. Если x = null и y = undefined возвращается true.
  3. Если x = undefined и y = null возвращается true.
  4. Если x = число, а y = строка, возвращается x == toNumber(y) (значение y преобразуется в число).
  5. Если x = строка, а y = число, возвращается toNumber(x) == y (значение x преобразуется в число).
  6. Если x = логическое значение, возвращается toNumber(x) == y.
  7. Если y = логическое значение, возвращается x == toNumber(y).
  8. Если x = строка, символ или число, а y = объект, возвращается x == toPrimitive(y) (значение y преобразуется в примитив).
  9. Если x = объект, а y = строка, символ или число, возвращается toPrimitive(x) == y.
  10. Возвращается false.

Запомните: для приведения объекта к «примитиву» метод toPrimitive сначала использует метод valueOf, затем метод toString.

Примеры:



Все примеры возвращают true.

Первый пример — первое условие алгоритма.
Второй пример — четвертое условие.
Третий — второе.
Четвертый — седьмое.
Пятый — восьмое.
И последний — десятое.



Если же мы используем оператор "===" все примеры, кроме первого, вернут false, поскольку значения в этих примерах имеют разные типы.

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) {
    return num + 5
}

Если мы выведем значение 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 '#<Window>'
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 — это выполнение функций настройки инициализации и предотвращение конфликтов имен с другими переменными в глобальной области видимости (загрязнение глобального пространства имен). Приведем пример.

<script src="https://cdnurl.com/somelibrary.js"></script>

У нас есть ссылка на библиотеку somelibrary.js, которая предоставляет некоторые глобальные функции, которые мы можем использовать в нашем коде, но в этой библиотеке есть два метода, createGraph и drawGraph, которые мы не используем, потому что они содержат ошибки. И мы хотим реализовать эти функции самостоятельно.

Одним из способов решить данную проблему является изменение структуры наших скриптов:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
    function createGraph() {
        // код
    }

    function drawGraph() {
        // код
    }
</script>

Таким образом, мы переопределяем методы, предоставляемые библиотекой.

Вторым способом является изменение имен наших функций:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
    function myCreateGraph() {
        // код
    }

    function myDrawGraph() {
        // код
    }
</script>

Третий способ — использование IIFE:

<script>
    const graphUtility = (function() {
        function createGraph() {
            // код
        }

        function drawGraph() {
            // код
        }
        return {
            createGraph,
            drawGraph
        }
    })
</script>

В этом примере мы создаем служебную переменную, которая содержит результат 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 `${greetingMessage} ${this.name}`
}

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 `${greetingMessage} ${this.name}`;
}

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) {
    return callback(param)
}

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()

Так происходит, потому что оператор присваивания ("=") имеет правостороннюю ассоциативность, т.е. присваивает значения справа налево. Поэтому код принимает следующий вид:

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 ${name}`
const greet2 = name = > `Hello ${name}`

Мы также можем передавать параметры стрелочным функциям. Если мы передаем один параметр, его можно не оборачивать в круглые скобки:

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 `${this.firstName} ${this.lastName}`
    }
}

Переопределение методов и наследование от другого класса:

// 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 ${this.getFullName()} and I have a position of #{this.jobTitle} and I started at ${this.yearStarted}}`
}

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 ${this.getFullName()} and I have a position of #{this.jobTitle} and I started at ${this.yearStarted}}` 
    }

    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 ${name}!`
}

В ES6 нам не нужно использовать конкатенацию строк для объединения текста с переменной: мы можем использовать выражение ${expr} для получения значения переменной.

46. Что такое деструктуризация объекта (Object Destructuring)?


Деструктуризация — относительно новый способ получения (извлечения) значений объекта или массива.

Допустим, у нас есть такой объект:

const employee = {
    firstName: 'Marko',
    lastName: 'Polo',
    position: 'Software Developer',
    yearHired: 2017
}

Раньше для получения свойств объекта мы создавали переменные для каждого свойства. Это было очень скучно и сильно раздражало:

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

Синтаксис модулей очень простой: мы используем import для импорта функциональности или значений из другого файла или файлов и export для экспорта.

Экспорт функциональности в другой файл (именной экспорт):

// 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) {
    return isNull(val) || isUndefined(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)
})

У промиса есть четыре состояния:

  • Ожидание — начальное состояние промиса. Результата промиса неизвестен, поскольку операция не завершена.
  • Выполнено — асинхронная операция выполнена, имеется результат.
  • Отклонено — асинхронная операция не выполнена, имеется причина.
  • Завершено — выполнено или отклонено.

В качестве параметров конструктор промиса принимает resolve и reject. В resolve записывается результат выполнения операции, в reject — причина невыполнения операции. Результат может быть обработан в методе .then, ошибка — в методе .catch. Метод .then также возвращает промис, поэтому мы можем использовать цепочку, состоящую из нескольких .then.

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({nums:[5,4,3,2,1]}) // [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){
    return value !== 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( {length: 5 })) // false
console.log(Array.isArray([])) // true

Если среда, в которой Вы работаете, не поддерживает данный метод, можете использовать такой полифил:

function isArray(value){
    return Object.prototype.toString.call(value) === '[object Array]'
}

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){
    if(num < 0 || num === 1) return false
    if(num == 0) return true
    return isEven(num - 2)
}

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 ${this.name}`
    }
}

o.greeting // Hi, I'm Mark

Функция-конструктор:

function Person(name){
    this.name = name
}

Person.prototype.greeting = function(){
    return `Hi, I'm ${this.name}`
}

const mark = new Person('Mark')

mark.greeting() // Hi, I'm Mark

Метод Object.create:

const n = {
    greeting(){
        return `Hi, I'm ${this.name}`
    }
}

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(){
    console.log('I will not be hoisted!')
}

Вызов 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) => `${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){
    return value === null
}

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 вещи:

  1. Создает пустой объект.
  2. Привязывает к нему значение this.
  3. Функция наследует от functionName.prototype.
  4. Возвращает значение this, если не указано иное.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 130

    +8
    1. Он использует короткое замыкание во избежание лишних затрат
      Перевести «short-circuiting» как «короткое замыкание» в контексте программирования, когда уже введен термин «closure» (который также переводится как замыкание) может только непрограммист.
    2. Согласно MDN оператор "+" действительно является самым быстрым способом преобразования строки в число
      Нет, не самый быстрый: jsbench.me/6ok6dbgbeo/1
    3. Прототип несуществующего свойства объекта имеет undefined в качестве значения по умолчанию
      У несуществующего свойства нет прототипа.
      +6
      Да, «short-circuiting» не имеет к «closure» ровно никакого отношения. Это «сокращенный способ вычисления» and или or, когда второй аргумент не вычисляется, если результат уже известен:

      true || b — сразу true, b не вычисляется, так как незачем.
      false && b — сразу false, b не вычисляется, так как снова незачем.

      Ну т.е. это просто стоило бы пояснить. Особенно если не знаешь, как это в русском называется.

      >только непрограммист.
      100%.
        –3
        1. Видимо раздел логические операторы на MDN переводил тоже непрограммист:
        Выполнение короткого замыкания
        Так как логические операторы выполняются слева направо, они тестируются на возможные «короткие замыкание», при использовании следующих прав:
        false && (anything) короткое замыкание, дающее false.
        true || (anything) короткое замыкание, дающее true.

        2. Цитата с MDN: унарный плюс — быстрейший и предпочитаемый способ конвертирования чего-либо в число потому, что он не выполняет каких-либо операций с числом.

        3. Из статьи: Remember properties in an object which does not exist in itself and its prototype has a default value of undefined and undefined has no property x. Согласен, что не вполне корректно. Вероятно, автор имел ввиду, что obj.__proto__.__proto__ = null, поэтому возвращается undefined, у которого, естественно, отсутствует свойство x.
          +2
          1. Видимо раздел логические операторы на MDN переводил тоже непрограммист.
            Ну, видимо… Там, в MDN, они хотя бы взяли идиому «короткое замыкание» в кавычки и разжевали что это значит. Ваша фраза: «короткое замыкание во избежание лишних затрат» ничего не объясняет, а только запутывает. Я бы вообще назвал это «ленивые вычисления», хотя это также неточно и ambiguous.
          2. быстрейший и предпочитаемый способ конвертирования чего-либо в число потому, что он не выполняет каких-либо операций с числом
            А какой способ, по-вашему, выполняет операции с числом? Number('123') выполняет? А parseFloat('123')? Чем они хуже? Чем они медленней?
          3. obj.__proto__.__proto__ = null
            Автор имеет ввиду, что свойства someprop нету ни у объекта, ни у его прототипа, по-этому оно undefined. А obj.__proto__.__proto__ === null будет верно для любого простого объекта. Это значит только, что вы находитесь в конце цепочки прототипов. Но это не значит, что obj.__proto__.someprop === undefined
            0
            взяли идиому «короткое замыкание»

            это не идиома, а дословный перевод, бессмысленный и беспощадный.
            в моей стране обычно переводят как «вычисление по короткой схеме».
              +1
              Ну или «по сокращенной». Или наконец просто «сокращенная схема». Что в общем уже одно и тоже почти.
              +3
              отредактировал часть статьи на MDN. перевод там просто трындец. читать нужно на англицком.
                0
                А какой способ, по-вашему, выполняет операции с числом?
                Предположу, имелось в виду то, что если на вход подать уже готовое число, а не строку, то тогда будет быстрее.
                И это не лишено смысла, потому что по моим наблюдениям никто и не использует плюс, если действительно по бизнес-логике нужно парсить строку — там используют parseInt/parseFloat. Плюс же используют для перестраховки, например, аргументов внутри функции — типа, мне тут и так должно приходить число, но на всякий случай фигану-ка я сюда ещё плюсик!
                  0
                  Да, я думаю, ключевое непонимание вышло из-за того, что в MDN написано:
                  быстрейший и предпочитаемый способ конвертирования чего-либо в число

                  а автор пишет:
                  является самым быстрым способом преобразования строки в число

                  т.к.
                  parseFloat(true) = NaN
                  +true = 1

                  И видимо, об этих дополнительных операциях шла речь. Только автор ничего не понял. И написал всё это про число-в-строку, обогатив мир ещё одним авторитетным домыслом.

                  PS: конвертация всего-что-угодно в строку — верный признак плохого кода.
                    0

                    Микробенчмарки бессмысленны в мире современного js, где вещи зависят от движка и версий движка.
                    Ещё б спросили как быстрее всего привести к числу в Chrome 63 (чисто с потолка).
                    Даже вопрос про reduce в Chrome 80 и то веселее :)

              0

              На iPhone SE плюсик действительно самый быстрый оказался.

                0
                А на сколько (просто интересно)? В Mozilla Firefox — «плюсик» 9.34% slower, чем parseFloat.
                  +1
                  unary+: 58 op/s
                  Number: 43 op/s
                  parseFloat: 44 op/s

                  Safari iOS 13.1.2, iPhoneSE. С Power save mode результаты у всех в 2 раза хуже, но тенденция такая же — плюсик быстрее на сравнимый %.
                0

                по пункту 2 — в мобильном сафари — самый быстрый, так что правильный ответ вообще "зависит от компилятора"

                  +1
                  Если точнее — то от engine, в случае с Chrome — это v8. Причем от версии к версии она может меняться.
                +1
                А зачем давать заднюю и скрывать свою вчерашнюю публикацию с глаз долой? habr.com/ru/post/487452
                  +4
                  Потому что не все статьи и переводы получаются удачными. Если профессиональное сообщество реагирует отрицательно, значит, статья получилась плохой. Плохие статьи я либо дорабатываю, либо удаляю. Публицистика для меня — часть самообразования. Я всегда готов прислушаться к мнению более опытных коллег и признать ошибку, если таковая действительно имела место.
                    +4
                    Вся ценность той статьи именно в комментариях к ней, которые будут полезны новичкам или старичкам с ограниченным кругозором.
                      0
                      Интересная точка зрения.
                      +1
                      Если профессиональное сообщество реагирует отрицательно, значит, статья получилась плохой.

                      Или сообщество недостаточно профессионально и (или) компетентно. Оглядываться на мнение толпы, особенно в таких сомнительных местах, как хабр — очень плохой паттерн как для саморазвития в целом, так и самооценки в частности.

                    0
                    Во втором пункте в примере
                    console.log('' && true && 5)

                    в комментарии ответом указана 5, хотя даже новички знают, что приведение пустой строки к логическому значению в Javascript вернёт false.
                      0
                      пропустил пробел между кавычками. спасибо
                      0
                      Отправил коммент по ошибке
                        –5
                        Мой любимый вопрос на собеседованиях это: «чем отличается setTimeout от setInterval, и чем они будут отличаться, если зациклить setTimeout».
                          0
                          Это конечно намного лучше, чем «что будет, если сделать 0 == []», но в абсолютном выражении всё так же бесполезно, тем более что ответ на вторую часть «в зависимости от обстоятельств — либо не слишком предсказуемой разницей в интервалах вызова, либо ничем».
                            –9

                            Не правильный ответ.
                            Они будут принципиально отличаться.
                            Вообще, в зависимости от того, как мне отвечают на этот вопрос я понимаю знает ли человек принципы работы JavaScript или сразу с React начал.

                              +1
                              Расскажите в чем принципиальное отличие?
                                –8
                                Принципиальное отличие в том, что setInterval у нас единственный и неповторимый.
                                Он гарантированно запускает код каждые n миллисекунд. Даже если то, что он запускает ещё не успело выполниться. Ему без разницы.
                                Цикл из setTimeout же, в силу реализации, сначала будет дожидаться выполнения кода (хотя можно конечно же сделать чтобы не дожидался), а затем запускать новую копию кода.

                                Вы делаете слайдер для своего сайта и пишите анимацию слайдов с использованием setInterval. Неверно. Сверните окно браузера, разверните его через 5 минут и посмотрите что получится.
                                Верно: анимировать слайды с помощью зацикленного setTimeout.

                                Скажем, у нас есть код, который выполняется 400 мс. И есть период каждые 1000 мс.
                                В случае с setInterval вторая копия кода будет запущена через 1000 мс, а в случае с зацикленным setTimeout через 1400 мс.

                                И тут можно задать бонусный вопрос: как сделать так, чтобы зацикленный setTimeout был идентичен по повадкам вызову setInterval.
                                  +2
                                  Это интересно, но я не могу воспроизвести. И при свернутом, и при развернутом «цикл» продолжает работать в фоне в обоих случаях:
                                  codepen.io/barkalov/pen/mdJypjB?editors=0010
                                  codepen.io/barkalov/pen/xxGbpaV?editors=0010
                                    +10

                                    Простите конечно, но вы бред городите.
                                    Во-первых, начнем с того, что ни setTimeout, ни setInterval не запускают код. Они лишь регистрируют коллбек. Запускает код уже внутреннее API, которое напрямую связано с такой штукой, как eventloop. Это и есть сердце асинхронности и работы всех асинк-функций. Так вот, что в случае с setTimeout, что в случае с setInterval, код из следующего коллбека будет запущен только и только тогда, когда в callstack'e не будет никаких операций. Поэтому setInterval ничего не запускает и уж тем более ему не все равно, он просто регает коллбек. Почему анимация дергается когда она на setInetrval — так это потому, что регистраций колбеков setInterval делает больше (в тот момент, когда callstack пустует), чем setTimeout. setTimeout более контролируемый, скажем так. А вообще анимации делать неправильно ни через то, ни через другое. Для анимаций есть давно requestAnimationFrame.

                                      +1
                                      А еще лучше css animation/keyframes)
                                  +1
                                  Они будут принципиально отличаться.

                                  И вы, конечно, назовёте эту принципиальную разницу?
                                    –3
                                    Конечно. Выше написал. Но так ведь не интересно, не правда ли?
                                      +3
                                      Конечно не интересно, прежде всего потому что ваше вышенаписанное — не соответствует действительности.

                                      Он гарантированно запускает код каждые n миллисекунд.

                                      setInterval() не только ничего не «запускает» гарантированно, но даже и в стек выполнения не кладёт гарантированно. И то и другое — произойдет по обстоятельствам. Гарантируется здесь лишь то, что от прошлого заталкивания в стек пройдет минимум столько миллисекунд, сколько указано в задержке.

                                      Цикл из setTimeout же, в силу реализации, сначала будет дожидаться выполнения кода (хотя можно конечно же сделать чтобы не дожидался), а затем запускать новую копию кода.

                                      Если вы немедленно после срабатывания прошлого setTimeout() вызовете следующий — то никакого «дожидаться выполнения кода» там, разумеется, не будет.

                                      Еще раз, где тут принципиальная разница, которую вы залихватски наобещали?

                                      Вы делаете слайдер для своего сайта и пишите анимацию слайдов с использованием setInterval.

                                      Того, кто в 2020 году пишет JS-анимацию НЕ через requestAnimationFrame() — следует увольнять за профнепригодность вне зависимости от того, на сколько глупых вопросов по JS он может ответить на собеседовании.
                                        0
                                        Покажите, пожалуйста, мне пример, где вы немедленно после срабатывания прошлого setTimeout() вызываете следующий, да так чтобы это удовлетворяло условиям задачи.
                                          0
                                          А в чём проблема-то?
                                          const timeout = function(fun) {
                                            console.time('timeoutFun');
                                            setTimeout(fun, 1000);
                                          }
                                          
                                          const dowork = async function() {
                                            // "do lots of work"
                                            console.time('work');
                                            let s = '';
                                            for (let i = 0; i < 1000000; i++) {
                                              s += Math.random() > 0.5 ? '+' : '-';
                                            }
                                            console.timeEnd('work');
                                          }
                                          
                                          const run = function() {
                                            const fun = (counter) => async () => {
                                              console.timeEnd('timeoutFun');
                                              if (counter++ < 10) {
                                                timeout(fun(counter));
                                              }
                                              dowork();
                                            }
                                            timeout(fun(0));
                                          }
                                          
                                          run()

                                          Оно выполняется точно так же, как подобный код выполнялся бы с setInterval(). Пока dowork() не больше задержки таймера, он будет выполняться в промежутках между перезапусками setTimeout(). Когда станет больше — будет тормозить и сами перезапуски. То же самое произойдет и с setInterval() в такой ситуации.
                                            –5
                                            У вас нет ни единого await. Зачем тут async?

                                              +2
                                              Тут — конечно не нужен. Это не более чем отсылка к реальным сценариям «выполнения работы». Ну и на синтетических примерах async, из-за накладных расходов, заметно тормозит выполнение, из-за чего за цифрами таймеров интереснее наблюдать.

                                              Вам по существу-то есть что сказать? Я всё еще надеюсь услышать от вас про принципиальную разницу между setInterval() и setTimeout().
                                                0
                                                Ну вот если его убрать, у вас и получится, что реализация через setTimeout не допустит одновременного запуска двух копий кода.
                                                  +5
                                                  реализация через setTimeout не допустит одновременного запуска двух копий кода

                                                  Одновременного запуска кода в JS вы в любом случае не получите, как бы вы не пытались.
                                                    –4
                                                    Я не пойму зачем вы цепляетесь к словам.
                                                    Вы ведь прекрасно поняли о чём я говорю.
                                                      +3
                                                      Я тоже не понимаю, зачем вы продолжаете отстаивать глубоко неверную точку зрения.
                                                      Если браузер занят выполнением кода, новый вызов в стек выполнения упадёт тогда, когда браузер освободится — что в случае setInterval(), что в случае setTimeout(). Заметьте, что для JS нет никакой разницы между «вызов лежит в стеке и ждет» и «вызов еще не упал в стек», и из JS вы не определите, произошло первое или второе. А произойти, в зависимости от обстоятельств, может и то, и другое.
                                                      Если браузер не занят выполнением кода, новый вызов в стек выполнения упадёт через <delay> миллисекунд для setInterval() от прошлого срабатывания, и в момент выполнения повторного setTimeout() плюс <delay> миллисекунд.

                                                      Если код перезапускает setTimeout() немедленно после срабатывания прошлого — никакой наблюдаемой разницы выполнения между этим случаем и использованием setInterval() — не будет.
                                                        –2
                                                        Что не верного в моей точке зрения? Что браузер может накапливать setInterval?

                                                        Скажите, в неактивной вкладке будет выполняться setInterval? А setTimeout?
                                                          0
                                                          Скажите, в неактивной вкладке будет выполняться setInterval? А setTimeout?
                                                          Я вам выше отвечал: оба будут.
                                                            –2
                                                            У вас идеальная ситуация в вакууме. В неактивной вкладке они выполняются, но с низким приоритетом. Это означает, что тяжеловесные операции будут выполняться дольше, и могут не уложиться во время отведённое им в setInterval, и будет ещё один вызов, в то время как предыдущий ещё отрабатывает.
                                                            Зациклив setTimeout такая ситуация невозможна в принципе.
                                                              +3
                                                              и могут не уложиться во время отведённое им в setInterval, и будет ещё один вызов
                                                              Нет, не будет, т.к. «тяжеловесные операции» заблокируют поток.

                                                              Вы же не думаете что setInterval(fn, 1) запущенный ровно на одну секунду сложит в event pool 1000 вызовов и потом их будет долго разгребать и выполнять? Или думаете? Или думаете, что не будет, но только из-за того что 1 мсек слишком маленький квант?
                                                                +3
                                                                Вы же не думаете что setInterval(fn, 1) запущенный ровно на одну секунду сложит в event pool 1000 вызовов и потом их будет долго разгребать и выполнять? Или думаете?

                                                                Вот я только сейчас понял, что наверное заблужение evil_random как раз где-то тут.
                                                                Но разумеется, это именно что заблуждение — постановка следующего вызова функции setInterval() в стек выполнения заведомо не начнётся до того, как уже лежащая в стеке функция того же setInterval() не отправится на выполнение. Это явно вытекает из документации на setInterval(), которая гарантирует соблюдение задержки между двумя соседними вызовами функции.

                                                                Например, согласно MDN:
                                                                This works in a very similar way to setTimeout(), except that the function you pass as the first parameter is executed repeatedly at no less than the number of milliseconds given by the second parameter apart, rather than once.

                                                                Не знаю, как там в хроме, но думаю, что абсолютно всё так же. И в прочих браузерах.

                                                                PS: А, ну разумеется, это в HTML Living Standard именно так.
                                                                  –3
                                                                  Нет никакого заблуждения. Вы не верно поняли условия задачи и выдаёте теперь свои домыслы за мои знания. Вас бы я не взял :)

                                                                  Я прекрасно осознаю как работает JavaScript на низком уровне.

                                                                  И если Вы посмотрите на мой пример ниже, Вы увидите что есть именно принципиальная разница в реализации условной функции ping.

                                                                  Но почему-то мне чаще всего пишут верхнюю с использованием setInterval.
                                                                    +4
                                                                    И если Вы посмотрите на мой пример ниже, Вы увидите что есть именно принципиальная разница в реализации условной функции ping.

                                                                    Да. Ваша «принципиальная» разница в реализации вашего примера называется «функцию wrongPing написал программист, сознательно руками (а не применением setInterval) забивающий страницу бесконечным количеством таймеров».

                                                                    Я вам уже написал — если вы сами сделали код, который забивает страницу растущей вычислительной нагрузкой (через стек вызовов ли, через таймеры ли, через DOM ли, через воркеры ли — неважно), то не надо утверждать, что всё это из-за setInterval(). Всё это из-за того, что вы так написали код.

                                                                    Вас бы я не взял :)

                                                                    Не обольщайтесь, я бы к вам и не пошел. Люди, которые с апломбом делают некорректные заявления, и используют их на собеседованиях — это огромный красный флаг.
                                                                      –3

                                                                      У вас какие-то комплексы что-ли. Где вы апломб увидели? Я написал вопрос, и потом ниже признал, что не совсем корректно поставил задачу. Почитайте внимательно.


                                                                      Это отличный вопрос на самом деле. В зависимости от ответа можно многое сказать о составителе.
                                                                      Вам бы я советовал поработать над Soft Skills. Пригодится и в жизни.

                                                                        +11

                                                                        Первый и наиважнейший софт скилл — никогда, никому, ни при каких обстоятельствах не советовать «поработать над софт скиллз».


                                                                        Ваш К. О.

                                                                          –1

                                                                          А почему нельзя советовать? Не слышал ранее такого мнения. Интересна аргументация.

                                                                            +7

                                                                            Потому что это в чистом виде ad hominem, и лживое слово «советую», особенно — когда совета никто и не спрашивал, — не делает его внезапно дружеским и безобидным.

                                                                        +1
                                                                        Люди, которые с апломбом делают некорректные заявления, и используют их на собеседованиях — это огромный красный флаг.

                                                                        А если я «с апломбом делаю некорректные заявления, но использую их исключительно вне собеседований» — нормально?


                                                                        :)

                                                                          –1

                                                                          Если вы с апломбом делаете некорректные заявления вне собеседований, то я уверен найдется у любителей вешать ярлыки и для вас ярлычок :)

                                                                            0
                                                                            Конечно. Я хоть с вами и много где готов поспорить на хабре, но вы прекрасный автор. В том числе и из-за «апломба». Авторы, которые льют воду килобайтами и скрывают статьи после двух минусов — унылы до боли в зубах.

                                                                            :)
                                                                              +1

                                                                              Ура!


                                                                              много где готов поспорить на хабре

                                                                              Так ведь а это — и самое главное, особенно в деле зачатия истины. Да и просто ведь до зубовного скрежета скучно, когда все со всеми согласны :)

                                                                      –1
                                                                      Конечно же я прекрасно знаю про eventLoop и про то, что конкретно делает setInterval, я там ниже написал пример кода, про который был вопрос.

                                                                      Но мне почему-то на собеседованиях очень редко отвечают про eventLoop. И про то, что пишете Вы. Такому ответу я бы только обрадовался.
                                                                    0
                                                                    неа, зависит от браузера и настроек энергоэффективности операционной системы.
                                                                      0
                                                                      Здесь ключевое слово: оба. Главное setTimeout работает так-же, как и setInterval. А так-то понятно, оно и в активной вкладке лагать может, если браузеру CPU недодать.
                                                                    +1
                                                                    Что браузер может накапливать setInterval?

                                                                    Браузер может (и будет) накапливать любые вызовы в стеке выполнения, если ваш код написан так, что он туда их складывает. Неважно, делаете ли вы setInterval() или что-то еще, набивающее вызовы в стек. Как я написал в коде выше, набить вызовы можно и через setTimeout(), для этого нет абсолютно никаких препятствий.

                                                                    Скажите, в неактивной вкладке будет выполняться setInterval? А setTimeout?

                                                                    Что будет или не будет выполняться в неактивной вкладке — это вообще дело браузера, а не кода.
                                                                    Ну и — https://habr.com/ru/post/486820/#comment_21250490
                                                                      –1
                                                                      Я выше написал как это будет работать.

                                                                      На вопрос вы мне не ответили, кстати.

                                                                      Что будет или не будет выполняться в неактивной вкладке — это вообще дело браузера, а не кода.

                                                                      stackoverflow.com/questions/5927284/how-can-i-make-setinterval-also-work-when-a-tab-is-inactive-in-chrome
                                                                        +1
                                                                        Если вы сами читали свою ссылку на SO — то вы должны были прочитать там как раз то, что я вам ответил — это дело браузера, а не кода. Браузер вполне может остановить на неактивной вкладке всё, что считает нужным, и единственный способ гарантированно этого избегать — пользоваться инструментами, на которые подобные оптимизации не распространяются (то есть вебворкеры).
                                                                          –1
                                                                          Нет. Я кинул ссылку которую не читал.
                                                                          0
                                                                          Что и требовалось доказать. Сам спрашивающий пишет:
                                                                          This can be confirmed by counting the amount of times the setInterval function is called each second — this will not be 30 but just 1 or 2 if the tab is inactive.

                                                                          Т.е. setInterval отработает столько, сколько ему позволят. И там ничего не сказанно о том, что после активизации setInterval выстрелит всеми невыпонеными итерациями. Точно так же, как и setTimeout в цикле.
                                                          +1
                                                          const run = function() {
                                                            const fun = (counter) => async () => {
                                                              console.timeEnd('timeoutFun');
                                                             
                                                              dowork();
                                                              if (counter++ < 10) {
                                                                timeout(fun(counter));
                                                              }
                                                            }
                                                            timeout(fun(0));
                                                          }
                                                          

                                                          Изменим функцию run.
                                                          Если я правильно понимаю, между тем как dowork закончит выполняться первый раз и начнет выполняться во второй раз гарантированно пройдет не менее 1 секунды.

                                                          Если же распланировать вызовы dowork с использованием setInterval, то это будет гарантировать только то, что между вызовами dowork пройдет не менее 1 секунды.
                                                          Т.е. если dowork выполняется 2 секунды, тогда между тем как dowork закончит выполняться первый раз и начнет выполняться во второй раз может пройти менее 1 секунды.
                                                          Возможно, evil_random такой кейс хотел описать.
                                                            +1
                                                            Ну вы же понимаете, что это явно следует из документации setTimeout() и setInterval(), и никак не является принципиальной разницей? Если вы перезапускаете таймер только в конце всей работы — абсолютно ожидаемо, что он начнет тикать только с этого момента.

                                                            Единственная разница между этими двумя функциями — в пункте 7.3 стандарта про таймеры. Одна перезапустит таймер самостоятельно, вторая — нет.
                                                              0
                                                              Согласен с вами.
                                                              По факту пошло описание не принципиальных отличий setTimeout от setInterval, а описание отличий планирования перезапусков с помощью setInterval и setTimeout в цикле.
                                                                –1
                                                                Да, я писал с телефона, между матчами в Sniper Elite 4 и не совсем верно поставил вопрос. Стоило спросить именно про отличия в реализации. На собеседовании, как правило, по наводящим вопросам кандидата с этого вопроса можно выйти и на разговор про eventLoop, или на разговор про то, как написать условный ping.

                                                                Пару раз мне даже говорили, что не знают для чего нужен setInterval.
                                                              –1
                                                              Вопрос был об этом:
                                                              const wrongPing = () => {
                                                                let counter = 0; 
                                                              
                                                                const slowRequestMock = (seqId) => {
                                                                  const now = new Date();
                                                                  console.log('Ping', seqId);
                                                              
                                                                  setTimeout(
                                                                    () => console.log('Pong', seqId, now - new Date()), 
                                                                    Math.floor(Math.random() * 3000) + 100,
                                                                  );
                                                                };
                                                              
                                                                setInterval(() => slowRequestMock(counter++), 0);
                                                              }
                                                              
                                                              const correctPing = () => {
                                                                let counter = 0; 
                                                              
                                                                const slowRequestMock = (seqId, nextPing) => {
                                                                  const now = new Date();
                                                                  console.log('Ping', seqId);
                                                              
                                                                  setTimeout(
                                                                    () => {
                                                                      console.log('Pong', seqId, new Date() - now);
                                                                      nextPing();
                                                                    },
                                                                    Math.floor(Math.random() * 3000) + 100
                                                                  );
                                                                };
                                                              
                                                                const ping = () => setTimeout(() => slowRequestMock(counter++, ping), 0);
                                                                ping();
                                                              }
                                                              


                                                              Понятное дело, что если кандидат начнёт мне рассказывать про eventLoop, я буду только рад и с удовольствием выслушаю его, но чаще всего мне отвечают «ничем».
                                                                +2
                                                                Нет, вопрос был вот об этом:
                                                                Мой любимый вопрос на собеседованиях это: «чем отличается setTimeout от setInterval, и чем они будут отличаться, если зациклить setTimeout».

                                                                И ответ был вот таким:
                                                                Они будут принципиально отличаться.

                                                                const absolutelyCorrectPingBecauseSetTimeout = () => {
                                                                  let counter = 0; 
                                                                
                                                                  const slowRequestMock = (seqId) => {
                                                                    const now = new Date();
                                                                    console.log('Ping', seqId);
                                                                
                                                                    setTimeout(
                                                                      () => {
                                                                        console.log('Pong', seqId, new Date() - now);
                                                                      },
                                                                      Math.floor(Math.random() * 3000) + 100
                                                                    );
                                                                  };
                                                                
                                                                  const ping = () => setTimeout(() => {ping(); slowRequestMock(counter++)}, 0);
                                                                  ping();
                                                                }

                                                                Ой, но как же так? setTimeout, так принципиально отличающийся от setInterval, всё равно забивает страницу бесконечными таймерами?
                                                                  –3

                                                                  Я пожалуй воздержусь от дальнейших дискуссий с Вами. Всего хорошего.

                                                                  +3
                                                                  Нет, вопрос был совершенно не об этом. Все каменты сохранились, в этом легко убедится.

                                                                  Вы именно что с апломбом (согласен с JustDont ) пришли в пост, высказали неверное утверждение, а сейчас вьётесь как уж на сковородке.
                                                                  Сначала у вас было разное поведение setTimeout/setInterval в неактивной вкладке, потом вдруг блокирующие операции (хотя каментом раньше вы писали об анимациях), сейчас вдруг какой-то сломаный пинг. Что это вообще?
                                                                  setInterval(() => slowRequestMock(counter++), 0);

                                                                  По существу последнего примера (игнорируя ошибку с setInterval(fn, 0)): в вашем примере принципиальное отличие не в setInterval/setTimeout а именно в вашей реализации: вы зачем-то во втором случае реализовали по сути onDoneCallback, а в первом нет. Уберите nextPing() из тела коллбека setTimeout в тело slowRequestMock и разницы не будет.

                                                                  По сути вообще: Вы сказали чушь. Что простительно. При этом, используя фамильярный тон («Вас бы я не взял», «Вам бы я советовал поработать над Soft Skills») Что сомнительно. А теперь вместо того чтобы признать свою неправоту, сливаетесь. Что выглядит просто жалко.

                                                                  Не хотел бы я оказаться с вами на одном интерьвью. Но вы меня и не возьмёте, я понял. :)
                                                                    –3
                                                                    Вопрос был именно об этом. То что я его не совсем корректно поставил — я написал в этом комментарии.
                                                                    То есть признал, что неверно сформулировал вопрос. Как Вы заметили, все комментарии сохранились.
                                                                    Я даже привёл примеры как было бы написать верно, а как не верно. Это не вопрос к Senior Developer, как и многие вопросы из этого поста, но ответ на него даже Senior-разработчиком дал бы примерное понимание того, с кем мы имеем дело.

                                                                    И никуда я не сливаюсь. Я просто не желаю общаться с человеком, который сходу пишет мне в хамско-высокомерном тоне, да к тому же ещё и читает выборочно только то, что ему выгодно. У Вас, к сожалению, тоже есть такая проблема.

                                                                    Вас бы, кстати, я возможно и взял, ведь вы сразу попробовали воспроизвести пример, без хамства и занудства. Просто не верно меня поняли.

                                                                    А по поводу придирок к словам, так можно придраться и к комментариям которые были выше «браузер не занят выполнением кода» и начать нести что-то в стиле «вообще-то браузер никакого кода и не выполняет вовсе, потому что тупо не может, его выполняет JavaScript Engine, V8 в случае Хрома и SpiderMonkey в случае Firefox», но я намеренно упростил разговор заявив «setInterval запускает код». Конечно же код запускает не он, код запускает процессор вообще.

                                                                    Даже если подойти чисто формально, то setTimeout и setInterval имеют принципиальное отличие в том, что один запускает код, простите, кладёт в стек v8, ссылку на то, что вы ему там передали, один раз, а второй делает это каждые N ms.

                                                                    Но я уже понял, что конструктивного диалога у нас не получится по выше озвученным причинам. Цель Джастонта не развеять мои заблуждения (хотя их и нет вовсе), а показать что в интернете кто-то не прав. Это чётко прослеживается по его ранним комментариям к другим постам. Любит человек это дело. Что поделаешь.
                                                                    Это без меня, пожалуйста.
                                                                      +4
                                                                      То что я его не совсем корректно поставил
                                                                      Не «не совсем корректно», а «совсем не корректно». Таким он и продолжает быть после третьей итерации.

                                                                      Я просто не желаю общаться с человеком, который сходу пишет мне в хамско-высокомерном тоне
                                                                      Его тон был вполне справедливым ответом на ваше высокомерие вкупе с высказанным заблуждением ещё здесь:
                                                                      Вообще, в зависимости от того, как мне отвечают на этот вопрос я понимаю знает ли человек принципы работы JavaScript или сразу с React начал.


                                                                      Мне 36 лет и моя основная специализация с 2007 года — javascript. И я до сих пор ещё ни разу не собеседовал джунов, представьте. Именно потому что всё ещё не уверен на 100% в своих силах. И очень не хочу выглядеть так, как вы сейчас. Это большая ответственность. Не перед компанией за то, что провороните ценный кадр, а перед соискателем. Он видит в вас авторитет. А вы ему не соответствуете в должной мере.
                                                                        0
                                                                        Мне 35 лет и я в Javascript с 2006-го примерно. И я тоже до сих пор допускаю, что чего-то не знаю или знаю не верно.
                                                                        Для того чтобы собеседовать джунов не нужно полной уверенности в своих силах. Я, как правило, смотрю есть ли в человеке черты присущие инженеру. Если есть, то он быстро из джуна мидлом станет и пойдёт дальше.

                                                                        Вопрос вполне корректен: «1) в чём отличие setInterval от setTimeout? 2) если вместо setInterval взять и зациклить неблокирущий код в setTimeout, чем будут отличаться реализации?».
                                                                          +5

                                                                          Мне 46, в индустрии с 1992, и я кого только не собеседовал. Но я никогда в жизни не доверил бы разговор с соискателем человеку, способному очно, или заочно, произнести фразу «вас бы я не взял».


                                                                          Я даже кандидатам, которым мы вынуждены отказать, очень вежливо и подробно объясняю, что в этот раз пошло не так, и что сделать, чтобы в следующий раз пошло лучше.


                                                                          На интервью категорически нельзя задавать вопросы в невнятных формулировках, или имеющие неочевидный ответ. Ответ должен быть либо пуленепробиваемым, не допускающим никаких разночтений или ответа не должно существовать вовсе, тогда мы сможем поговорить ни о чем и оценить, как кандидат думает.


                                                                          А вот barkalov как раз лукавит, или — если нет — ему давно пора вести собеседования. С таким подходом даже суперскиллы в предмете не нужны, сразу видно — интервьюер отличный.

                                                                            +1
                                                                            Как там после 40 в профессии? Есть жизнь?

                                                                            А то у нас многие кому за 30 переживают, что окажутся никому не нужны совсем скоро. У меня тоже бывают такие мысли.
                                                                              +1

                                                                              У меня подробно заполнен профиль, если это так интересно.


                                                                              Я получаю примерно три-шесть предложений в месяц в свой почтовый ящик напрямую от CEO (HR-спам я выбрасываю, не читая). Не вижу никаких причин для того, чтобы что-то поменялось в будущем.

                                                                                0
                                                                                Ознакомился. Информация обнадёживающая.

                                                                                Даже тут, на Хабре, недавно где-то была статья, что предпочитают молодых-зелёных.
                                                                                Сеют панику в рядах старожил.

                                                                                Вы у меня только что один страх убрали. Спасибо :)
                                                                                  +1

                                                                                  На галерах — возможно. В стабильном бизнесе — я никогда не сталкивался с претензиями к возрасту. Мы в прошлом месяце взяли джуна под сорок и QA — моего ровесника.


                                                                                  А еще, я выбрасываю, не читая, резюме людей, которые младше 30, но претендуют на звание «señor» (если у них нет гитхаба, сравнимого с моим, или другого кода показать, конечно).

                                                                                    0
                                                                                    Ну галеры я не рассматриваю даже.
                                                                                    Я никогда не работал на галерах и надеюсь что никогда не буду вынужден.

                                                                                    Однажды 20-ти летние юнцы с ЕПАМа реджектнули меня с формулировкой «на собеседовании хвастался обширными знаниями в смежных технологиях». Как же я ржал.

                                                                                    По поводу кода я даже не знаю. Мне вот и показать нечего, последние три компании где я работал имели достаточно жёсткие NDA и весь код был закрытый.
                                                                                      +1

                                                                                      Ну, с кодом, конечно, лучше да, чем нет, но мой пассаж про код относился только к «младше 30, но претендуют на звание „señor“».


                                                                                      Юнцы с ЕПАМ закончатся, а мы останемся.

                                                            –4
                                                            И вы взяли слишком низкий уровень.
                                                            Конечно же javascript не real-time и ничего в нём не происходит гарантированно, но в условиях сферического сайта в браузере можно говорить о примерной «гарантии» того, что код будет запущен с помощью setInterval.

                                                            Вам не нравится вопрос? Почему?
                                                        +3
                                                        Не дай бох попасть к такому собеседующему, который свои домыслы выдает за положение вещей.
                                                        Хотя я разок так попал, меня уверяли что process.nextTick() выполнится на следующем тике, а setImmediate() на текущем, хотя в ноде это широко известный прикол, что все наоборот.
                                                          –4
                                                          Не дай бог получить такого кандидата, который свои домыслы о чужих мыслях будет выдавать за факты.
                                                    +4

                                                    Интересно, почему везде, где поясняют промисы — делают это так поверхностно? Просто мой любимый случай, это когда есть, например, чейн из 5 then, и тебе надо в пятом то, что вычислилось во втором. Многие начинают в такой ситуации вкладывать промисы друг в друга (для общего скоупа) и… тем самым пораждают тот же самый "колбек хелл", только теперь это "промис хелл" с теми же вложенностями.

                                                      0

                                                      Немного распишу, раз уж заплюсовали.


                                                      НЕ надо решать так:


                                                      • общим скоупом или вложенностью
                                                      • передачей всех результатов через все звенья

                                                      Пример плохого решения:


                                                      first().then(resultA => {
                                                          return second().then(resultB => {
                                                              return third(resultA, resultB)
                                                          })
                                                      })

                                                      Решать надо переиспользованием объектов промисов, которые несут в себе значение. Примерно так:


                                                      const a = first()
                                                      const b = second()
                                                      Promise.all([a, b]).then(results => {
                                                          const [resultA, resultB] = results
                                                          return third(resultA, resultB)
                                                      })
                                                        +1
                                                        Только
                                                        const b = a.then(second);
                                                        иначе примеры не эквивалентны.

                                                        А решать всё-же лучше с помощью await.
                                                        const a = await first();
                                                        const b = await second();
                                                        return await third(a, b);
                                                        
                                                          0

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

                                                            0
                                                            Расскажите пожалуйста подробнее про разницу. У меня есть убеждение, что await — просто синтаксический сахар, стартующий промис.
                                                            Или вы имеете ввиду это:

                                                            const a = first();
                                                            const b = second();
                                                            // "едем дальше"
                                                            return await third(await a, await b);

                                                            То это как раз не эквивалентно изначальному «плохому» примеру на промисах.
                                                              0

                                                              Async/await это сахар из сборной солянки из генераторов и промисов. Разница только в том, что пример, где Promise.all — не блокирует последующее выполнение в данном скоупе (просто представьте, что у вас тысяча и одна асинк операция в одном скоупе — лишними эвейтами вы будете блокировать даже делегацию задач на low-level API или регистрацию каких-либо коллбеков. Возможно пример из ноды вам прояснит что я имел ввиду:


                                                              const read = util.promisify(fs.readFile)
                                                              
                                                              // scope-blocking
                                                              (async () => {
                                                                  console.log(await read('./one.txt'))
                                                                  console.log(await read('./two.txt'))
                                                                  console.log(await read('./three.txt'))
                                                              })()
                                                              
                                                              // non-blocking
                                                              Promise.all([
                                                                  read('./one.txt'),
                                                                  read('./two.txt'),
                                                                  read('./three.txt')
                                                              ]).then(results => {
                                                                  console.log(results[0])
                                                                  console.log(results[1])
                                                                  console.log(results[2])
                                                              })
                                                                0
                                                                Кажется я понял о чем вы.
                                                                // scope-blocking
                                                                (async () => {
                                                                    let one = read('./one.txt');
                                                                    let two =  read('./two.txt');
                                                                    let three = read('./three.txt');
                                                                    let results = await Promise.all([one, two, three]); // blocking here
                                                                    console.log(results[0]);
                                                                    console.log(results[1]);
                                                                    console.log(results[2]);
                                                                    // some code here that can't be reached until results resolved
                                                                })()

                                                                Вы об этом, что в вашем случае console.log'и будут выполнены в отдельном коллбэке, а в моем случае в основном теле функции, и, соответсвенно, для этого await в моем случае подождет резолва промисов, и не побежит по коду дальше.
                                                                Тогда согласен с вами, только это не вина и не проблема async, просто к нему нужен чуть другой подход. Саму функцию можно декомпозировать на части так, чтобы «дожидание» не мешало другому коду, которому это не требуется. По сути
                                                                именно по этому у вас console.log в коллбэке — это и есть та же декомпозиция, просто не такая красивая.
                                                                Ну и Promise.all, понятное дело, я выкидывать не призываю, он и с async\await бывает нужен, когда нужен.
                                                                  0

                                                                  Так я изначально ничего против async/await не имел, просто сказал что await не всегда то же самое и что, как вы в последующем и выразились: к нему нужен иногда немного другой подход.

                                                                    0
                                                                    Консенсус! :)
                                                            0
                                                            это же и есть общий скоуп, который автор вопроса советует избегать
                                                              0
                                                              А я понял, что общий скоуп, которого следует избегать это что-то вроде:
                                                              let scopedResultA, scopedResultB;
                                                              first().then(resultA => {
                                                                  scopedResultA = resultA;    
                                                              }).then(()=>{
                                                                  return second().then(resultB => {
                                                                      scopedResultB = resultB;
                                                                  })
                                                              }).then(()=>{
                                                                  return third(scopedResultA, scopedResultB);
                                                              })
                                                                0

                                                                Да, и последовательные await именно так и работают. Каждый await создаёт скрытый коллбек, который получает скоуп предыдущего.
                                                                А идея переиспользовать промисы конечно изящная, но на практике не сказал бы что это улучшает читабельность. Хотя я часто храню данные в промисах, чтобы гарантировано запустить код после, например, инициализации конфига.

                                                                  0
                                                                  это же и есть общий скоуп, который автор вопроса советует избегать
                                                                  Не вижу ничего плохого в общем скоупе на async\await. Просто именно на промисах общий скоуп выглядит уродливо. Как впрочем и вложенные промисы. Потому мы и любим async/await.
                                                                  Я за это:

                                                                  async function doSome() {
                                                                      const a = first();
                                                                      const b = second();
                                                                      const c = third(await a, await b);
                                                                      // if u afraid that await will 'block the scope'
                                                                      // just do not place code here that must run berfore a and b resolves
                                                                      // place it in main() instead - it's a pretty common sense
                                                                      return await c;
                                                                  }
                                                                  
                                                                  async function main() {
                                                                      const c = doSome();
                                                                      doSomeElse(); // run "parrallel" task here
                                                                      return await c;
                                                                  }
                                                        0
                                                        68. Как бы Вы реализовали вспомогательную функцию запоминания?
                                                        Очень плохой пример. В вашей имплементации объект cache, (как и все объекты) может иметь только строковые имена (ключи) свойств. Либо делайте мемоизацию с помощью Map, либо не делайте вовсе. Так как:

                                                        let a = memoizeReduceAdd([1, 2, 3, 4], 5); // a = 15
                                                        let b = memoizeReduceAdd([1, 2, 3, 4], 5); // b = 15
                                                        

                                                        let a = memoizeReduceAdd(['1', '2', '3', 4], 5); // a = "51234"
                                                        let b = memoizeReduceAdd([1, 2, 3, 4], 5); // b = "51234", кэш "испорчен"
                                                        
                                                          +1
                                                          (1) Это перевод.
                                                          (2) Мне не нравится и часть «if (cache[params])», которая отказывается вспоминать логически ложные результаты «мемоизуемой» функции.
                                                          +1
                                                          про bind как-то забыли, что он не только this привязывает, но и остальные аргументы. Дальше не стал читать.
                                                          • UFO just landed and posted this here
                                                              +3
                                                              Для самоконтроля попробуйте найти все ошибки. Я на 42м вопросе бросил.
                                                              • UFO just landed and posted this here
                                                              +4
                                                              11. Почему obj.someprop.x приводит к ошибке?
                                                              Вопрос из разряда «почему индейка перешла через дорогу». Как вам ответ «потому что там прописано кидать ошибку», например?
                                                              let obj = {someprop: {}};
                                                              Object.defineProperty(obj.someprop, 'x', {get: () => {throw "Error!"}});
                                                              Или, например, «потому что API отдаёт в нём значения за пределами допустимого диапазона»?
                                                              14. В чем разница между операторами "==" и "==="?
                                                              Сначала упоминание toPrimitive(), toString() и valueOf(), а потом таблица без единого явного использования этих функций. Почему бы добавить туда такие варианты?
                                                              x = { valueOf: () => 10};
                                                              y = 10;
                                                              
                                                              x = { toString: () => 20};
                                                              y = 20;

                                                              16. Для чего используется оператор "!!"?
                                                              С каких пор !! стал отдельным оператором? Это не ++ и не --, это просто дважды подряд применённый унарный оператор !.
                                                              Нельзя создавать функции с помощью функции eval:
                                                              eval('var x = 1')
                                                              Во-первых, где тут создаётся функция?
                                                              Во-вторых, что-то я не вижу, чтобы с помощью eval нельзя было создавать функции:
                                                              (function () {
                                                              	'use strict'
                                                              	eval('function test(){console.log("Success");};test()');
                                                              })();
                                                              -> Success

                                                              (function() {
                                                              	'use strict'
                                                              	let a = eval('(function (){console.log("Success");})');
                                                              	a();
                                                              })();
                                                              -> Success

                                                              В 30-м воопросе вообще не JS, а JSX. К чему в вопросах по чистому JS внезапно из ниоткуда JSX-код в качестве примера?

                                                              const three = function three({
                                                                  return arguments
                                                              })
                                                              Этот код вообще не распарсится, там же аж подсветка синтаксиса сломалась.

                                                              Переменные, объявленные с помощью ключевого слова «var», являются глобальными. Это означает, что они доступны из любого места в коде:
                                                              Вот прямо-таки из любого, а не только из тела функции, в которой они объявлены?
                                                              function lol() {
                                                                var myX = 10;
                                                              }
                                                              lol();
                                                              myX
                                                              ->ReferenceError: myX is not defined
                                                                0
                                                                Кстати, по поводу eval() функций: Я вот только на днях узнал, что Function(param) / new Function(param) может принимать в качестве param — строку с кодом тела функции. То есть как eval, но создает функцию.
                                                                  0
                                                                  во всех хитрозавернутых задачках типа «выкинуть нужную ошибку без использования throw» всегда добавляют условие «никакого eval или Function». Никогда не сталкивались?
                                                                    0
                                                                    Как-то миновало. :)

                                                                    Так?
                                                                    new Promise((resolve, reject)=> { reject(new RangeError('pizza size is too big'))});
                                                                      0

                                                                      Режект промиса не выкидывает эксепшн, если мы не внутри асинка.
                                                                      А задачка решалась через генераторы. Генератор имеет метод throw

                                                                        +1
                                                                        без использования throw
                                                                        А задачка решалась через генераторы. Генератор имеет метод throw
                                                                        Поясните, какой смысл в этом методе, если по условиям его нельзя использовать?
                                                                          0
                                                                          (function*() {})()['th'+'row']('123')
                                                                            +1
                                                                            Но вы же всё равно его используете ;)
                                                                              0
                                                                              условия — не использовать слова try, throw, Function, eval.
                                                                          0
                                                                          То есть, если завернуть мой вариант в (async () => {})(), то решение верное? Если так, то я собой доволен.

                                                                          А по-поводу никаких «eval или Function», как вам такое:
                                                                          setTimeout('console.log("there is one more way to evaluate string")', 0);
                                                                            +1
                                                                            Так можно ж тогда ещё проще:
                                                                            await Promise.reject(new RangeError('pizza size is too big'));
                                                                  0
                                                                  Про Symbol и генераторы ни слова, а ведь так можно проверить кандидата насколько хорошо ему знаком мир Js
                                                                    –3

                                                                    Генераторы — тупиковая ветвь развития, любую логику можно переписать с них более очевидным способом. А Symbol… почему именно Symbol? Чем он показательнее других новинок языка для проверки знаний?

                                                                    0
                                                                    Ну вот опять 25. В пункте про функциональное программирование пишут про операции над коллекциями, которые сами по себе к функциональному программированию не имеют отношения.

                                                                    Функциональное программирование — это прежде всего ленивость, иммутабельность и хвостовые рекурсии.
                                                                      +1

                                                                      ФП — это про чистые функции и ФВП. А то, что вы называете, это, по-моему, свойства каких-то знакомых вам языков или систем.
                                                                      Ленивость — оптимизация, иммутабельность не требование, хвостовая рекурсия — частный случай рекурсии, который легко поддаётся о оптимизации. Хорошо хоть про систему типов не вспомнили

                                                                        0
                                                                        ФП — это то что пошло из лямбда исчислений. Система типов здесь не причём.
                                                                        Основная идея ФП — что мы не изменяем состояние. Отсюда — иммутабельность.

                                                                        Это значит что традиционные конструкции процедурных языков заменяются функциональными аналогами.

                                                                        Аналогом оператора присваивания становится создание функции по вычислению переменной. Это удобно сделать с помощью ленивости. Тогда можно писать a=b, подразумевая что здесь мы создаём новую функцию.

                                                                        Аналогом же цикла является хвостовая рекурсия.

                                                                        Типичным примером функционального языка является хаскель. Ни Java Script, ни Scala функциональными языками не являются.

                                                                        А по поводу ФВП, то они вообще чуть ли не в каждом языке есть, что ж тогда все языки функциональными называть?..
                                                                        Передача коллбака в функцию на С — обычное тривиальное действие. С — теперь функциональный язык?
                                                                          +1
                                                                          ФП — это то что пошло из лямбда исчислений.

                                                                          LISP принято относить к мультипарадигменным функциональным языкам, вообще-то.

                                                                            0

                                                                            Основная идея ФП в том, что мы оперируем чистыми функциями, есть у них внутренние переменные/состояние, циклы или нет — нюансы реализации самой функции, пока на один и тот же набор параметров она отдаёт один и тот же результат.


                                                                            И почему именно хвостовая? Какая религия мешает делать любую рекурсию для итерирования? Я вот предполагаю, что лишь то, что трансляторы могут эффективно разворачивать хвостовую рекурсию в циклы.


                                                                            Все (или почти все) популярные языки являются мультипарадигменными. И вообще мы вроде о функциональных языках не говорили, а говорили о функциональном программировании.

                                                                              0
                                                                              Какая религия мешает делать любую рекурсию для итерирования?

                                                                              Стек мешает :) TCO зачищает стек за собой, а нехвостовой рекурсивный вызов так не сможет, потому что вызывающая функция еще не закончилась, и ей стек может пригодиться. Попробуйте в любом языке, который поддерживает TCO (не js) факториал чего-нибудь значимого посчитать так и эдак. Ну вот, например:


                                                                              def f!(n, acc = 1)
                                                                                return acc if n <= 1 1
                                                                                f!(n - 1, n * acc)
                                                                              end
                                                                              
                                                                              f!(100_000).to_s.size
                                                                              #⇒ SystemStackError: stack level too deep

                                                                              но:


                                                                              RubyVM::InstructionSequence.compile_option = {
                                                                                tailcall_optimization: true,
                                                                                trace_instruction: false
                                                                              }
                                                                              
                                                                              def f!(n, acc = 1)
                                                                                return acc if n <= 1 1
                                                                                f!(n - 1, n * acc)
                                                                              end
                                                                              
                                                                              f!(100_000).to_s.size
                                                                              #⇒ 456574



                                                                              Интересно, что виртуальная машина эрланга внезапно умеет сделать TCO из кривого кода везде, где это возможно.


                                                                              defmodule F do
                                                                                def f!(1), do: 1
                                                                                def f!(n) when n > 0 do
                                                                                  prev = f!(n - 1)
                                                                                  prev * n
                                                                                end
                                                                              end
                                                                              
                                                                              100_000 |> F.f!() |> to_string() |> String.length()
                                                                              #⇒ 456574
                                                                        +2
                                                                        Вставлю свои пять копеек.

                                                                        Вопрос 31.
                                                                        Объект Array содержит методы map, filter и reduce, которые являются самыми известными функциями в мире функционального программирования из-за их полезности, а также потому, что они не изменяют массив, что делает эти функции «чистыми».


                                                                        Вот здесь вообще есть нюанс про то, что это справедливо для массивов с примитивными типами данных. Если на входе есть массив объектов, то в том же .filter или .map никто не запрещает как угодно модифицировать элементы массивов и никакой иммутабельности в этом случае не будет
                                                                          0

                                                                          На уровне массива иммутабельность будет. Значение object — это ссылка. Ни одна ссылка в классическом использовании .filter или .map не пострадает.

                                                                          +1
                                                                          18. Что такое поднятие (Hoisting)?

                                                                          Компиляция. В этой фазе функциональные выражения и переменные, объявленные с помощью ключевого слова «var», со значением undefined поднимаются в самый верх глобальной (или функциональной) области видимости (как бы перемещаются в начало нашего кода.

                                                                          Запомните: поднимаются только функциональные выражения и переменные, объявленные с помощью ключевого слова «var». Обычные функции и стрелочные функции, а также переменные, объявленные с помощью ключевых слов «let» и «const» не поднимаются.


                                                                          Функциональные выражения не поднимаются. А «обычные функции», то есть объявления функций наоборот поднимаются.
                                                                            +1
                                                                            Добрый день. Отличная статья, спасибо.
                                                                            Откорректируйте, пожалуйста, ответ по 18-му вопросу.
                                                                            «Функциональные выражения в JavaScript не поднимаются (hoisting), в отличие от объявленных функций.»
                                                                            developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/function
                                                                              0
                                                                              Добрый вечер!
                                                                              Статья огонь! Не совсем понял про 56 вопрос. Вы пишите, что isNaN работает странно и не совсем так как надо.
                                                                              Результатом всех console.log является true, несмотря на то, что ни одно из значений не является NaN

                                                                              По мне так isNaN работает верно и все что у Вас в аргументах функции isNaN действительно НЕ ЧИСЛО и поэтому возвращается true. Проверка же идет на то, что это НЕ число — Not a Number, а не наоборот )) А вот если мы напишем console.log(isNaN(7)) то выведет false, что тоже верно, так как 7 это число! )) Главное не путаться и чётко понимать на что мы проверяем.
                                                                              Спасибо.
                                                                                +1
                                                                                isNaN — это не проверка на то, что аргумент не является числом. isNaN — это проверка на то, что аргумент является «нечислом». NaN — это особое зарезервированное значение числа с плавающей точкой, определённое в стандарте (так же как и, например Infinity), а не просто «всё что угодно кроме числа».

                                                                                То есть, как это ни странно NaN — формально как раз число.

                                                                                typeof(NaN);  // "number"

                                                                                Грубо говоря, функция isNaN(value) эквивалентна (value === NaN), если бы не одно но: NaN неравен сам себе, поэтому-то и запилили эту функцию.

                                                                              Only users with full accounts can post comments. Log in, please.