Задачка на JavaScript (ES6)
Привет обитателям DOU!
Я тут недавно по долгу службы перешёл на JavaScript и начал знакомство с ES6. В итоге сочинил небольшую задачку, которую и предлагаю порешать. Конструкции не совсем близкие к реальности, ES6 синтаксис вполне валидный. Это всё, конечно, не ново, но для разминки, думаю, будет интересно. Ниже код, который нужно прочитать и сказать, что будет на выходе. Ещё ниже пояснение. Для тех, кто не сильно знаком с ES6 — я буду давать краткие пояснения. Тут далеко не весь ES6, посему за самим стандартом можно пройти по ссылке es6katas.org )
Со всеми замечаниями и уточнениями милости прошу в комменты, не проходите мимо )
'Dangerous - ES6!'; `What's "console.log" gonna log?`; let misteries = new Array( (number) => number, (number) => (number), (number) => {number}, (number) => ({number}), (number) => {(number)} ); (function (whatIsMistery = misteries[3](3)) { const {number} = whatIsMistery; misteries = [...misteries, number]; }()); let results = {}; [0,1,2,3,4].forEach(mistery => results[`mistery${mistery}`] = misteries[mistery](mistery)); results[`mistery${5}`] = misteries[5] console.log(); // The main question: comment this entire line and tell why!? (function (...somethingElse) { for (let something of somethingElse) console.log(results[`mistery${something}`]); }(0, 1, 2, 3, 4, 5)); // Check yourself: https://repl.it/languages/javascript/
Итак, поехали разбираться. Что мы видим? Мы видим массив misteries, объявленный через ключевое слово new, хотя можно было воспользоваться литералом, и всё было бы также. Есть пустой объект results, пара ифи (Immediately Invoked Function Expression) и консоль логи.
В массиве misteries лежит 5 стрелочных функций.
Стрелочные функции уменьшают количество кода и сохраняют контекст
var func1 = function(a,b) { return a*b }; var func2 = (a,b) => a*b; var func3 = (a,b) => { return a*b }; // все 3 функции одинаковые. Если стрелочную функцию можно записать в одну строчку, то ключевое слово return JS подставляет за вас втихую. // Если он видит фигурные скобки, то ждёт return. var myVar = 'window'; var obj = { myVar: 'objVar', one: function() { console.log(this.myVar) // objVar } }; var obj2 = { myVar: 'objVar', one: () => console.log(this.myVar) // window }; obj.one(); obj2.one();
Вернёмся к функциям позже, т.к. их сейчас всё равно никто не вызывает.
Дальше идёт функция ифи
(function (whatIsMistery = misteries[3](3)) { const {number} = whatIsMistery; misteries = [...misteries, number]; }());Она сразу же вызывается без параметров. В аргументах функции стоит дефолтное значение
whatIsMistery = misteries[3](3)
Такое есть и в других языках. Т.е., если мы при вызове функции передадим что-то отличное от undefined, то whatIsMistery будет этим значением. Если нет, то misteries[3](3). При чём эта штука правильно хендлит ситуации когда мы хотим передать null, 0, пустую строку и всё, кроме undefined.
Итак, whatIsMistery = результат выполнения misteries[3] с параметром 3, т.е. (3) => ({number}).
В функции мы видим литерал объекта.
Это просто синтаксический сахар, который эквивалентен
(3) => ({ number: number })Т.е. если мы передаём в функцию какую-нибудь переменную myVar и хотим создать объект, в котором будет поле с названием «myVar» и переданным в функцию значением, то можно воспользоваться литералом объекта для сокращения количества символов.
Функция вернёт объект {number: 3}.
С дефолтным параметром и его результатом разобрались, теперь вернёмся к нашему ифи.
const {number} = whatIsMistery;Это деструктуризация.
Иными словами мы разбираем конструкцию. Наша конструкция — это объект {number: 3}.
const {number} — создаёт неизменяемую переменную number (там необязательно должен быть const) переменная должна называться так же как и поле нашего объекта. И присваиваться ей должен наш объект. Тогда JS создаст переменную number, пойдёт в whatIsMistery, найдёт поле number и присвоит переменной значение этого поля.
В итоге мы получим переменную number = 3;
Дальше 14 строка misteries = [...misteries, number];
...misteries — Spread operator.
Эта штука упрощает конкатенацию массивов. [1,2,...[3,4],5] = [1,2,3,4,5]
Тут мы переопределяем переменную misteries и складываем в неё всё, что в ней было раньше и добавляем нашу переменную number, которая равна 3.
Строка 19
[0,1,2,3,4].forEach(mistery => results[`mistery${mistery}`] = misteries[mistery](mistery));Тут мы на лету создаём массив [0,1,2,3,4] и применяем к нему forEach
Это метод массива. Он применяется к массиву и в него передаётся функция. Форыч итеративно выполняет функцию со всеми элементами массива по очереди. В отличие от map он ничего не возвращает, соответственно и return не нужен.
В объекте results мы создаём поля и присваиваем им какие-то значения. В квадратных скобках стоят шаблонные строки
Эти ребята упрощают конкатенацию строк c переменными. Записываются они в обратных кавычках (символ слева от «1»), в них как обычные символы записываются и двойные и одинарные кавычки, и переменные вставляются как ${myVar / 2 + 18}. Операции с переменными происходят внутри скобок, а в строку попадает результат. Т.е. `mistery${mistery}` в первой итерации форыча будет `mistery0`
В первой итерации будет results[`mistery0`] = misteries[0](0)
Во второй results[`mistery1`] = misteries[1](1)
И так далее.
Теперь мы можем вернуться к массиву misteries. Благодаря форычу, в объект results он будет идти со следующими значениями:
let misteries = new Array( (0) => number, (1) => (number), (2) => {number}, (3) => ({number}), (4) => {(number)} );(0) => number — вернёт то, что в него передали = 0.
(1) => (number) — тоже самое = 1.
(2) => {number} — эквивалентно записи
(2) => { number }А значит требует ключевого слова return. Т.к. его нет, то возвращается undefined
(3) => ({number}) — с этим мы уже разобрались, вернётся объект {number, 3}
(4) => {(number)} — то же самое, что (2) => {number}, посему undefined
В строке 21
results[`mistery${5}`] = misteries[5]Мы в results добавляем поле `mistery${5}` или просто mistery5 и ассайним ему значение misteries[5], которое, как мы уже решили равно намберу 3.
Итого results — это объект
{ mistery0: 0, mistery1: 1, mistery2: undefined, mistery3: { number: 3 }, mistery4: undefined, mistery5: 3 }В строке 23 стоит console.log(); который выводит на экран пустую стоку и символ переноса строки.
В строке 25 — последний ифи
(function (...somethingElse) { for (let something of somethingElse) console.log(results[`mistery${something}`]); }(0, 1, 2, 3, 4, 5));Функция незамедлительно вызывается с
Это все аргументы, переданные в функцию, представленные в виде массива. Т.е.
...somethingElse = [arguments] = Array(arguments) в то время, как arguments — это массиво подобный объект (как будто созданный через new Array()), который содержит в себе аргументы функции и является итерируемым, но не имеет методов массива, то Rest — это настоящий массив.
Внутри функции есть конструкция for of
Конструкция применяется к итерируемым объектам и позволяет обращаться к ним без итератора.
let iterable = [10, 20, 30]; for (let value of iterable) { console.log(value); } // 10 // 20 // 30
На экран мы выводим results[`mistery${something}`]. Тут тоже шаблонная строка. Something — это наши 0,1,2,3,4,5 => `mistery${something}` = «mistery0» и так дальше, т.е. мы просто выводим всё содержимое объекта results:
0 1 undefined { number: 3 } undefined 3
Основная задача решена, теперь на внимательность. Если убрать консоль лог со строчки 23, то результат получится:
0 1 undefined { number: 3 } undefined undefined TypeError: misteries[5] is not a function at eval:25:1 at evalМы потеряли number 3 последним элементом results и получили TypeError, что misteries[5], который был 3, а теперь undefined — не функция, что нам и так известно )
Это случается из-за доброты джаваскрипта. Он разрешает нам не ставить семиколоны и ставит их за нас, когда мы забываем. Но, дабы разрешить нам писать красивый код с вайтспейсами, он анализирует код и не добавляет семиколоны в логически незаконченных инструкциях.
В данном случае он ругается на строку 25, где у нас начинается ифи. А в 21 строке
results[`mistery${5}`] = misteries[5]Я забыл поставить семиколон. JavaScript увидел, что на 25 строке открывается скобка и понял, что это call функции misteries[5] и ругнулся, что это ж не функция.
Спасибо тем, кто дочитал до конца )
9 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів