// makeProfileTimer

// Напишите функцию makeProfileTimer, которая служит для замера времени выполнения другого кода и работает следующим образом:

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

// Используйте performance.now()

function makeProfileTimer() {
    var t1 = performance.now();
    return function () {
        var t2 = performance.now();
        return (t2 - t1);
    };
};

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

// makeSaver

// Напишите функцию makeSaver, которая:

// 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 был только один раз`)

// Таким образом makeSaver решает две задачи:

// * Навсегда сохраняет результат функции.Это актуально, например, для Math.random.
// * Действует лениво, то есть вызывает Math.random только тогда, когда результат действительно нужен.
// Если же по каким - то причинам значение не понадобится, то Math.random даже не будет вызван

function makeSaver(callback) {
    let result, executed = !1;
    return function () {
        return executed ? result : (executed = !0, res = callback());
    };
};

var saver = makeSaver(Math.random)

let value1 = saver();
let value2 = saver();
value1 === value2;

function fun() {
    return console.log('saved function called') || [null, undefined, false, '', 0, Math.random()][Math.ceil(Math.random() * 6)]
}

var saver2 = makeSaver(fun)

var value3 = saver2()
var value4 = saver2()

value3 === value4

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

// myBind

// Изучите встроенную функцию bind, и сделайте свою версию, которая позволит определить "значение по умолчанию" не только для первых параметров, 
// но для любых других, например для степени в Math.pow:

// 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'
// Массив, который идет третьим параметром определяет, какие поля должны подменяться значением по умолчанию, 
// а какие - задаваться в последствии(undefined).

let myBind = function (fn, context, arr) {
    return function () {
        let index = 0
        var fnArgs = [].slice.call(arguments);
        return fn.apply(context, arr.map(i => {
            if (i === undefined) {
                let newValue = fnArgs[index]
                index++
                return newValue
            }
            else return i
        }));
    };
};

var pow5 = myBind(Math.pow, Math, [undefined, 5]);
var cube = myBind(Math.pow, Math, [undefined, 3]);
alert(pow5(2));
alert(cube(3));

var chessMin = myBind(Math.min, Math, [undefined, 4, undefined, 5, undefined, 8, undefined, 9])
chessMin(-1, -5, 3, 15);

var zeroPrompt = myBind(prompt, window, [undefined, "0"])
var someNumber = zeroPrompt("Введите число")
alert(someNumber);

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