//Замыкания
//makeProfileTimer
let makeProfileTimer = function() {
    let profileTimerNowStart = performance.now();

    return function() {
        let profileTimerNowEnd = performance.now();

        return `Алерт работал ${profileTimerNowEnd - profileTimerNowStart} микросекунд`;
    }
}

var timer = makeProfileTimer()
alert('Замеряем время работы этого alert');  //некий код, время выполнения которого мы хотим измерить с высокой точностью
alert(timer());                              //alert должен вывести время в микросекундах от выполнения makeProfileTimer до момента вызова timer(), 
                                             // т. е. измерить время выполнения alert


//makeSaver
let makeSaver = function(f) {
    let count = 0; //Переменная которая будет проверять запускали ли ранее функцию, если запускали то будет возвращено старое значение
    let functionStorage; //Тут я буду хранить результат работы переданной функции

    let saverFunction = function() {
        if(count == 0) { //Проверка была ли функция запущена ранее
            functionStorage = f(); //Если count == 0 то функцию ранее не запускали, тут я сохраняю результат переданной функции
        } else {
            return functionStorage; //Усли функция ранее запускалась то я возвращаю старое значение
        }

        count++; //После первого запуска функции счетчик будет увеличиваться

        return functionStorage;
    }

    return saverFunction;
}

var saver = makeSaver(Math.random)  //создает функцию-хранилище результата переданной в качестве параметра функции (Math.random 
                                    // в примере). На этом этапе Math.random НЕ вызывается
var value1 = saver()                //saver вызывает переданную в makeSaver функцию, запоминает результат и возвращает его
var value2 = saver()                //saver в дальнейшем просто хранит результат функции, и более НЕ вызывает переданную 
                                    //в makeSaver функцию;
console.log(value1 === value2);                // всегда true

var saver2 = makeSaver(() => console.log('saved function called') || [null, undefined, false, '', 0, Math.random()][Math.ceil(Math.random()*6)])
var value3 = saver2()
var value4 = saver2()

console.log(value3 === value4); // тоже должно быть true


let namePrompt = prompt.bind(window, 'Как тебя зовут?')
let nameSaver = makeSaver(namePrompt)
alert(`Привет! Prompt еще не было!`)
alert(`Привет ${nameSaver()}. Только что запустился prompt, первый и последний раз`)
alert(`Слушай, ${nameSaver()}, го пить пиво. Ведь prompt был только один раз`)


//Final Countdown
let count = 1;

for(let i = 5; i > 0; i--) {

    let countdown = function() {
        console.log(i);
        if(i == 1) {
            console.log("Поехали");
        }
    }

    setTimeout(countdown, function(){
        return 1000 * count++;
    }());
}


//myBind
let myBind = function(bindFunction, thisForFunction, arr) { //Не нашел применения второму параметру )
    let newArr = [];                                        // В этом массиве я буду хранить значения которые должны передаваться при вызове и значения по умолчанию
    let index = 0;                                          // Каунер который я буду в дальнейшем использовать что бы корректно заменять undefined на данные которые передаются при вызове

    let myBindFunction = function(...customArr) {

        for(let item of arr) {                   //Цикл который проходит массив данных по умолчанию
            if(item == undefined) {              //В массиве данных по умолчанию проверяю элемент на undefined, если true то меняю его на элемент который был передан при вызове
                newArr.push(customArr[index]);
                index++;
            } else {
                newArr.push(item);
            }
        }

        return bindFunction(...newArr);
    }

    return myBindFunction;
}

var pow5 = myBind(Math.pow, Math, [undefined, 5]) // первый параметр - функция для биндинга значений по умолчанию, 
                                                  // второй - this для этой функции, третий - массив, в котором undefined означает
                                                  // параметры, которые должны передаваться при вызове,
                                                  // а другие значения являются значениями по умолчанию:
var cube = myBind(Math.pow, Math, [undefined, 3]) // cube возводит число в куб

pow5(2) // => 32, вызывает Math.pow(2,5), соотнесите с [undefined, 5]
cube(3) // => 27


var chessMin = myBind(Math.min, Math, [undefined, 4, undefined, 5,undefined, 8,undefined, 9])
chessMin(-1,-5,3,15) // вызывает Math.min(-1, 4, -5, 5, 3, 8, 15, 9), результат -5



var zeroPrompt = myBind(prompt, window, [undefined, "0"]) // аналогично, только теперь задается "0" как текст по умолчанию в prompt, 
                                                          // а текст приглашения пользователя задается при вызове zeroPrompt
var someNumber = zeroPrompt("Введите число")              // вызывает prompt("Введите число","0")

myBind((...params) => params.join(''), null, [undefined, 'b', undefined, undefined, 'e', 'f'])('a','c','d') === 'abcdef'