// makeProfileTimer
{
    function makeProfileTimer() {
        let t0 = performance.now();
        return function tn() {
            let t1 = performance.now();
            return (t1 - t0) * 1000 + 'microseconds';
        };
    }

    let timer = makeProfileTimer();
    alert("Замеряем время работы этого alert");
    alert(timer());
}

// makeSaver
{
    function makeSaver(fn) {
        let saver = fn();
        return function () {
            return saver;
        }
    }

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

    value3 === value4 // тоже должно быть true

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

}

// Final Countdown

{
    timer(+prompt("Введите количество секунд для отсчета", "5"));

    function timer(t) {
        let a = t;
        (function tictac() {
            for (a; a >= 0; a--) {
                result = (a > 0) ? a : "поехали!";
                setTimeout(console.log, 1000 * (t - a), result);
            }
        })();
    }

}

// myBind  
{

    function myBind(fn, thisArg, bindedParams) {
        return function (...params) {
            let combinedParams = [];
            for (let key in bindedParams) {
                if (bindedParams[key] === undefined) {
                    combinedParams.push(params.shift());
                } else {
                    combinedParams.push(bindedParams[key]);
                }
            }
            if (params.length > 0) {
                for (let value of params) {
                    combinedParams.push(value);
                }
            }
            return fn.call(thisArg, ...combinedParams);
        }
    }

    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'
}