# call

Function.prototype._call = function(obj){
    // 首先要获取调用call的函数,用this可以获取
    var obj = obj || window
    obj.fn = this
    var args = [...arguments].slice(1)
    var result = obj.fn(...args)
    delete obj.fn
    return result
}

# apply

Function.prototype._apply = function(obj){
    var obj = obj || window
    obj.fn = this
    var result
    // 检查是否有入参,arguments[0] 指向的是this,arguments[1]指向的是入参数组
    if(arguments[1]){
        result = obj.fn(...arguments[1])
    }else{
        result = obj.fn()
    }

    delete obj.fn
    return result
}

# bind

Function.prototype._bind = function(obj){
    if(typeof this != "function"){
        throw Error("not a function")
    }

    var fn = this
    var args = [...arguments].slice(1)

    var resFn = function(){
        return fn.apply(this instanceof resFn ? this : obj, args.concat(...arguments))
    }

    function tmp(){}

    tmp.prototype = this.prototype
    resFn.prototype = new tmp()

    return resFn
}

Function.prototype._bind2 = function(context) {
    //返回一个绑定this的函数,我们需要在此保存this
    let self = this
        // 可以支持柯里化传参,保存参数
    let arg = [...arguments].slice(1)
        // 返回一个函数
    return function() {
        //同样因为支持柯里化形式传参我们需要再次获取存储参数
        let newArg = [...arguments]
        console.log(newArg)
            // 返回函数绑定this,传入两次保存的参数
            //考虑返回函数有返回值做了return
        return self.apply(context, arg.concat(newArg))
    }
}

let foo = {
    value: 1
}

function bar(name, age){
    console.log(name)
    console.log(age)
    console.log(this.value)
}

bar._call(foo, "xiaoming", "18")
bar._apply(foo, ["xiaohuang", "28"])
bar._bind(foo, "xiaopeng", "38")()