Skip to content

Latest commit

 

History

History
456 lines (393 loc) · 30 KB

2.cold_closures_cove.md

File metadata and controls

456 lines (393 loc) · 30 KB

2.1 Раздел 1 - Концепция замыкания

2.2 Следим за замыканиями I

Задание. После изучения кода ниже, вычислить финальное значение переменной result и вывести это значение на экран в качестве числа, используя одну линию кода.

var hidden = mystery();
var result = hidden(3);

function mystery() {
    var secret = 6;

    function mystery2(multiplier) {
        multiplier *= 3;
        return secret * multiplier;
    }

    return mystery2;
}

Решение. В задании просится вывести числовое значение. Вычисляем его, получаем 54:

alert(54);

Пояснение. Функция mystery возвращает mystery2 в том виде, как она есть, так как в return mystery2 не стоят круглые скобки. Переменная hidden принимает значение mystery() и оставляет его равным самой функции mystery2. Чтобы получить конкретное значение, переменной result присваивается hidden с конкретным аргументом. Круглые скобки в этом случае позволят получить не саму функцию mystery2, а её значение, где в качестве аргумента multiplier будет выступать число 3. В итоге проводим необходимые математические операции, чтобы вычислить нужное значение.

2.3 Следим за замыканиями II

Задание. На этот раз код стал немного сложнее. Опять же, нужно вывести значение на экран в качестве числа, используя одну линию кода.

var hidden = mystery(4);
var result = hidden(2);

function mystery(input) {
    var secret = 5;

    function mystery2(multiplier) {
        multiplier *= input;
        return secret * multiplier;
    }

    return mystery2;
}

Решение. Снова вычисляем значение, получаем 40:

alert(40);

Пояснение. В этот раз используется 2 аргумента. Теперь появился аргумент у функции mystery, который в переменной hidden обозначен как 4. Значение же аргумента mystery2 теперь равно 2.

2.4 Следим за замыканиями III

Задание. Ещё сложнее! Снова рассчитать конечное значение переменной result и вывести его в виде числа с помощью одной строки кода.

var hidden = mystery(3);
var jumble = mystery3(hidden);
var result = jumble(2);

function mystery(input) {
    var secret = 4;
    input += 2;
    function mystery2(multiplier) {
        multiplier *= input;
        return secret * multiplier;
    }

    return mystery2;
}

function mystery3(param) {
    function mystery4(bonus) {
        return param(6) + bonus;
    }

    return mystery4;
}

Решение. Добавляется ещё две функции и ещё одна переменная. В итоге получаем 122:

alert(122);

Пояснение. Легко работать с двумя функциями и двумя переменными. Когда же их становиться больше, получившуюся паутинку становится распутать труднее. В данном случае переменные идут по порядку, одна содержит в значении другую. Следовательно решение нужно начать с первой - hidden. Она принимает значение функции mystery с аргументом 3 (input), которая выводит функцию mystery2 целиком. С этим всё понятно.
Следующая переменная - jumble. Она принимает значение mystery3 с аргументом hidden, следовательно возвращает функцию mystery4 целиком. В функции mystery4 param принимает аргумент 6, что можно сопоставить с hidden(6), что в свою очередь задаёт аргумент функции mystery2. В итоге результат переменной jumble составляет 122.
Последним шагом нужно найти значение переменной result. Изначально оно равно jumble с аргументом 2. Данный аргумент присваивается функции mystery4. Получаем конечный результат 122.

2.5 Построение замыкания I

Задание. Девушкам-разработчикам в the Cold Closures Cove иногда нужно сообщать путешественникам о различных препятствиях, которые время от времени приплывают в бухточку. Однако, они нуждаются в помощи. Они приготовили очень эффективное предупреждение, которое позволит им предупреждать только когда они должны, и только когда им это нужно. Замыкание в помощь!
Ниже, они начали функцию для вызова warningMaker с параметром obstacle. В этой функции построить и вернуть другую функцию, которая выводит конкретное сообщение, основанное на специфике тревожащего объекта столкновения. Формат сообщения должен быть следующим:

"Beware! There have been <obstacle> sightings in the Cove today!"

Решение. В уже готовой функции warningMaker возвращаем другую анонимную функцию с необходимым сообщением:

function warningMaker(obstacle) {
    return function () {
        alert("Beware! There have been " + obstacle +
        		" sightings in the Cove today!");
    };
}

Пояснение. Возвращение функции из функции, в комплекте с переменными из внешней области видимости, называется замыканием. За пределами крайней функции будет доступно всё содержимое помещённой в неё функции. Замыкание охватывает целую среду, связывающую необходимые переменные из других областей. Внутренняя функция может обращаться к переменным внешней функции, потому что они "чувствуют себя" как глобальные переменные. Им не нужно храниться где-либо во внутренней функции, даже в аргументах. Замыкание может сделать создание очень похожих функций сверх эффективными.

2.6 Использование замыкания I

Задание. The Cove’s Dev Girls только что получили сообщение об айсберге в области! Используя новый производитель предупреждений, построить предупреждающую функцию для препятствующего айсберга и сохранить её в переменной icebergAlert. Затем вызвать эту функцию.

Решение. Присваиваем переменной icebergAlert существующую функцию со значением "iceberg", а затем вызываем анонимную функцию:

function warningMaker(obstacle) {
    return function () {
        alert("Beware! There have been " + obstacle + 
						"sightings in the Cove today!");
    };
}

var icebergAlert = warningMaker("iceberg");
icebergAlert();
}

Пояснение. В прошлом уровне мы узнали, что функция может содержать в себе другую функцию. Если написать имя функции-матери с одними круглыми скобками, то мы получим функцию-дочь целиком; если же поставить две пары скобок, то к нам попадёт дочерний результат. Данный пример мы могли бы решить в одну строку, но в задании требуется именно этот вариант.

2.7 Построение замыкания II

Задание. The Dev Girls восхищаются моим явным мастерством). Чтобы проверить мощь, они попросили изменить warningMaker в объявленном порядке:

  1. количество помех
  2. конкретное расположение найденного препятствия
    Другими словами, они хотели бы передавать конкретное количество и расположение любой предупреждающей функцией, которую они уже создали и использовать эти значения в качестве сообщения. Наша старая функция уже в редакторе. Формат нового сообщения представлен ниже. Имена новых аргументов находятся в скобках:
"Beware! There have been <obstacle> sightings in the Cove today!
<number> <obstacle>(s) spotted at the <location>!"

Решение. В анонимную функцию помещаем 2 аргумента: количество и положение помех; добавляем их в сообщение вместе с необходимым текстом:

function warningMaker(obstacle) {
    return function (number, location) {
        alert("Beware! There have been " + obstacle + 
						" sightings in the Cove today!\n" +
            number + " " + obstacle + "(s) spotted at the " + 
						location + "!"
        );
    };
}

Пояснение. Внутренняя функция может быть как без аргументов, так и с любым их количеством. Если мы поместим все аргументы в warningMaker, то результат не изменится (правда тогда потеряется гибкость), однако, если перенести аргументы из внешней функции, они будут доступны только во внутренней.

2.8 Использование замыкания II

Задание. Улучшенная функциональность предупреждающих сообщений произвела впечатление на the Dev Girls. Они построили несколько генераторов предупреждения и готовы к любой опасности. Вдруг радио стало потрескивать.
Помехи Dev Girls, сигнал бедствия. Dev Girls, сигнал бедствия. У меня 6 убийц пингвинов на свободе возле "Ледяной пещеры", и 1 снежный йети буйствует на той стороне "Метель-Бич". Повторяю, 6 убийц пингвинов, "Ледяные пещеры", 1 снежный йети, "Метель-Бич". Конец связи! Помехи
Просмотреть уже построенное предупреждающее сообщение и вызвать соответствующие функции для этой ситуации, убедившись, что они переданны в соответствующих параметрах. Всё это займёт только 2 строки кода.

Решение. В конце готового кода пишем 2 строки с именем подходящих переменных и необходимыми аргументами:

function warningMaker(obstacle) {
    return function (number, location) {
        alert("Beware! There have been " + obstacle + 
						" sightings in the Cove today!\n" +
            number + " " + obstacle + "(s) spotted at the " + 
						location + "!"
        );
    };
}
var killerPenguinAlert = warningMaker("killer penguin");
var polarBearAlert = warningMaker("polar bear");
var icebergAlert = warningMaker("iceberg");
var flashBlizzardAlert = warningMaker("flash blizzard");
var snowYetiAlert = warningMaker("snow yeti");

killerPenguinAlert(6, "Ice Caves");
snowYetiAlert(1, "Blizzard Beach");

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

2.9 Раздел 2 - Изменение связанных значений после замыкания

2.10 Изменение связанного значения I

Задание. Разработчики the Cold Closures Cove обеспокоены количеством предупреждений, которые были возвращенны из the Cove Rangers в последнее время. Некоторые препятствия были особенно хлопотны, а другие - нет, как обычные угрозы. Они хотели бы изменить предупреждающую функцию, чтобы внутри неё подсчитывалось количество возникновения препятствий, а затем добавить полученное число в предупреждающее сообщение. Опять же, не нужно передавать в вычисления само количество, но каждое сообщение функции должно выводить сохранённый подсчёт.
Формат нового сообщения должен соответствовать нижеследующему, можно использовать count в качестве трекера в новой конструкции:

"Beware! There have been <obstacle> sightings in the Cove today!
<number> <obstacle>(s) spotted at the <location>!
This is Alert #<count> today for <obstacle> danger."

Решение. В готовую функцию добавляем переменную count со значением 0 во внешней переменной, а во внутренней используем инкрементный синтаксис:

function warningMaker(obstacle) {
    var count = 0;
    return function (number, location) {
        count++;
        alert("Beware! There have been " + obstacle +
            " sightings in the Cove today!\n" +
            number + " " + obstacle + "(s) spotted at the " + location + "!\n" +
						"This is Alert #" + count + " today for <obstacle> danger."
        );
    };
}

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

Пояснение. Замыкающие функции могут даже модифицировать связанные переменные в фоновом режиме. Начинают такой трекер с 0, а затем добавляют по одному номеру. В нашем случае это так, но значения переменной могут варьироваться. После каждого запроса будет выводиться новое значение переменной.

2.11 Шоссе к Опасной зоне

Задание. Тревога: Трудный вызов!
Теперь, когда оповещения эффективно учитываются для конкретного препятствия, the Dev Girls должны хранить известные места препятствий, так что список опасных зон для каждого препятствия может быть представлен с каждым новым предупреждением (они работают над лазероуправляемыми акулами и у них мало времени). Используя замыкание в созданном предупреждении, найти способ для хранения каждого нового местоположения в массиве zones, а затем сообщить весь список текущих опасных зон в каждом предупреждении, которое выходит для этого конкретного препятствия. Новое сообщение оповещения должно быть следующим:

"Beware! There have been <obstacle> sightings in the Cove today!
<number> <obstacle>(s) spotted at the <location>!
This is Alert #<count> today for <obstacle> danger.
Current danger zones are:
<zone1>
<zone2>
<zone3>
..."

Конкретный пример этого нового предупреждения был бы:

Beware! There have been giant ice bat sightings in the Cove today!
20 giant ice bat(s) spotted at the Frozen Falls!
This is Alert #3 today for giant ice bat danger.
Current danger zones are:
Blizzard Beach
Ice Caves
Frozen Falls

Можно открыть консоль JavaScript браузера и проверить свой ​​код, чтобы увидеть всплывающие окна.

Решение. Во внешней функции создаём пустой массив zones, в который затем, с помощью метода .push() добавляем местоположения. Переменной list задаём пустую строку; в последствии эту переменную преобразуем в цикле и выведем в сообщении:

function warningMaker(obstacle) {
    var count = 0;
    var zones = [];
    return function (number, location) {
        count++;
        zones.push(location);
        var list = "";
        for (var i = 0; i < zones.length; i++) {
            list = list + "\n" + zones[i];
        }
        alert("Beware! There have been " + obstacle +
            " sightings in the Cove today!\n" +
            number + " " + obstacle + "(s) spotted at the " + location + "!\n" +
            "This is Alert #" + count + " today for " + obstacle + " danger.\n" +
            "Current danger zones are:" + list
        );
    };
}

Пояснение. Хорошая практика - часто проверять свои шаги, выводя их в консоль. Это поможет глубже понять происходящее и вовремя скорректировать дальнейший код.

2.12 Просто следите за всем! Я всё скажу вам!

Задание. Тревога: Трудный вызов!
Для новых путешественников приятно узнать, где опасные зоны, но что, если некоторые из них ищут новые ощущения? Некоторые хотят знать как много препятствий в каждой опасной зоне: достаточно страшно, чтобы только получить адреналин, а не мгновенную смерть.
The Dev Girls на the Cove попросили теперь найти способ для хранения как расположения, так и количества препятствий, выводя оба в сообщении в следующем формате:

"Beware! There have been <obstacle> sightings in the Cove today!
<number> <obstacle>(s) spotted at the <location>!
This is Alert #<count> today for <obstacle> danger. 
Current danger zones are:
<zone1> (<number1>)
<zone2> (<number2>)
<zone3> (<number3>)
..."

Конкретный пример этого нового предупреждения был бы:

"Beware! There have been giant ice bat sightings in the Cove today!
20 giant ice bat(s) spotted at the Frozen Falls!
This is Alert #3 today for giant ice bat danger.  
Current danger zones are:
Blizzard Beach (3)
Ice Caves (12)
Frozen Falls (20)"

Продолжаем использовать массив zones и не стесняемся применять консоль.

Решение. Тут всё очень просто, вместо локации помещаем в массив другой массив с локацией, требуемым синтаксисом и количеством:

function warningMaker(obstacle) {
    var count = 0;
    var zones = [];
    return function (number, location) {
        count++;
        zones.push([location + " (" + number + ")"]);
        var list = "";
        for (var i = 0; i < zones.length; i++) {
            list = list + "\n" + zones[i];
        }
        alert("Beware! There have been " + obstacle +
            " sightings in the Cove today!\n" +
            number + " " + obstacle + "(s) spotted at the " + location + "!\n" +
            "This is Alert #" + count + " today for " + obstacle + " danger.\n" +
            "Current danger zones are:" + list
        );
    };
}

Пояснение. Работа с массивами в js занимает далеко не последнее место в любой части кода, особенно когда дело касается функции. Поэтому важно научиться использовать полученные знания в комплексе.

2.13 Раздел 3 - Опасные моменты в замыканиях

2.14 Финал замыкающих значений I

Задание. Теперь, когда опасные зоны и препятствия в порядке и с ними можно иметь дело, the Dev Girls могли бы использовать нашу поддержку с направляющими акул the Cold Closures Cove, их специально оборудованными лазерами. Это верно. Лазеры.
У них есть проблемы в существующем коде, который пытается использовать замыкания. Нужно его проверить:

function assignLaser( shark, sharkList ){
  var stationAssignment;
  for(var i = 0; i<sharkList.length; i++){
    if(shark == sharkList[i]){
      stationAssignment = function(){
        alert("Yo, " +
              shark +
              "!\n" +
              "Visit underwater strapping station " +
              i +
              " for your sweet laser.\n" +
              "'Bout to get real up in here."
             );
      };
    }
  }
  return stationAssignment;
}

Акулы направлены на лазерную станцию, которой соответствует индекс, который они вызвали в пределах массива sharkList, который выглядит следующим образом:

["Sea Pain", "Great Wheezy", "DJ Chewie", "Lil' Bitey", 
 "Finmaster Flex", "Swim Khalifa", "Ice Teeth", "The Notorious J.A.W."]

Можно подумать, что ничего не может пойти не так с пультом акульего лазера... Но что-то не так, потому что теперь переместитель заклинило на станции the Notorious J.A.W., и все эти акулы поместились здесь.
Нужно выяснить, что произошло и применить исправление из раздела перемещения, которое:

  1. Не изменяет позицию построения функции внутри цикла
  2. Устраняет в последствии ненужные элементы
  3. Обеспечивает, что все акулы будут делать это в нужной части залива

Решение. Удаляем переменную stationAssignment и всё, что с ней связанно и, в теле цикла, возвращаем непосредственно нашу анонимную функцию, выводящую сообщение:

function assignLaser(shark, sharkList) {
    for (var i = 0; i < sharkList.length; i++) {
        if (shark == sharkList[i]) {
            return function () {
                alert("Yo, " + shark + "!\n" +
                    "Visit underwater strapping station " +
                    i + " for your sweet laser.\n" +
                    "'Bout to get real up in here."
                );
            };
        }
    }
}

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

Пояснение. Мы должны обратить пристальное внимание на возвращение и окончательные состояние переменных. Данная функция на самом деле возвращала "момент замыкания", когда окружающая среда и все необходимые переменные упакованы. Существует несколько вариантов для правильного построения замыкания. Один из них представлен в решении. Немедленное возвращение имеет ожидаемый эффект, потому что i не допускает прогрессирования!

2.15 Финал замыкающих значений II

Задание. The Dev Girls сейчас нуждаются в целевом задании для каждой акулы. Для справки, списки акул и задач выглядит следующим образом:

var listOfSharks = ["Sea Pain", "Great Wheezy",
                    "DJ Chewie", "Lil' Bitey",
                    "Finmaster Flex", "Swim Khalifa",
                    "Ice Teeth", "The Notorious J.A.W."];
var listOfTargets = ["icicle bat", "snow yeti",
                     "killer penguin", "frost tiger",
                     "polar bear", "iceberg",
                     "blue witch", "wooly mammoth"];

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

var getTargetFor = makeTargetAssigner(listOfSharks, listOfTargets);
getTargetFor("Ice Teeth");

Вот пример всплывающего сообщения, которое разработчики хотели бы видеть, вызывая getTargetFor:

What up, Ice Teeth!
There've been blue witch sightings in our 'hood!
Time for a swim-by lasering, homie!

Примечание: индекс списка акул соответствует индексу предпологаемой к устранению цели.
Цель заключается в построении функции makeTargetAssigner с использованием закрытия так, что бы она возвращала функцию, которая может быть использована в порядке, как попросили разработчики. Чтобы помочь CodeScool проверить работу более эффективно, использовать shark в качестве параметра для имени акулы в функции замыкания. Можно использовать консоль браузера, чтобы проверить несколько выводов!

Решение. В функции makeTargetAssigner возвращаем анонимную функцию с аргументом shark. Затем строим цикл длинной в массив sharks и ставим в нём условие, что если элемент массива sharks равен shark, то выводится необходимое сообщение:

function makeTargetAssigner(sharks, targets) {
    return function (shark) {
        for (var i = 0; i < sharks.length; i++) {
            if (sharks[i] == shark) {
                alert("What up, " + shark + "!\n" +
                    "There've been " + targets[i] + " sightings in our 'hood!\n" +
                    "Time for a swim-by lasering, homie!");
            }
        };
    };
}

Пояснение. К замыканиям нужно относиться очень внимательно. Некоторые разработчики считают их использование бессмысленным. Но, если научиться с ними правильно работать, можно сделать код более коротким и эффективным.