重写call、apply、bind和new

166 阅读1分钟
function fn(param1, param2) {
    console.log('val:' + this.val)
    console.log('param1:' + param1)
    console.log('param2:' + param2)
}
let obj = {
    val: 'val'
}

fn.call(obj, 'param1', 'param2')
fn.apply(obj, ['param1', 'param2'])
fn.bind(obj, 'param1')('param2')

console.log('---use newCall---')
Function.prototype.newCall = function (context) {
    context = context || window
    context.fn = this
    let args = []
    for (let i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']')
    }
    let result = eval('context.fn(' + args + ')')
    delete context.fn
    return result
}
fn.newCall(obj, 'param1', 'param2')

console.log('---use newApply---')
Function.prototype.newApply = function(context, arr) {
    context = context || window
    context.fn = this
    let result = null
    if (!arr) {
        result = context.fn()
    } else {
        let args = []
        for (let i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']')
        }
        result = eval('context.fn(' + args + ')')
    }
    delete context.fn
    return result
}
fn.newApply(obj, ['param1', 'param2'])

console.log('---use newBind---')
Function.prototype.newBind = function(context) {
    let _this = this
    let args = Array.prototype.slice.call(arguments, 1)
    let processFn = function() {}
    processFn.prototype = this.prototype
    let bindFn = function() {
        let bindArgs = Array.prototype.slice.call(arguments)
        return _this.apply(this instanceof processFn ? this: context, args.concat(bindArgs))
    }
    bindFn.prototype = new processFn()
    return bindFn
}
fn.newBind(obj, 'param1')('param2')

console.log('---use newNew---')
function Fn(param1) {
    this.param1 = param1
    this.param2 = 'param2'
}
Fn.prototype.param3 = 'param3'

let oldFn = new Fn('param1')
console.log(oldFn.param1)
console.log(oldFn.param2)
console.log(oldFn.param3)

function newFn() {
    let obj = new Object()
    let Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype
    let ret = Constructor.apply(obj, arguments)
    return typeof ret === 'object' ? ret || obj : obj;
}

let newObj = newFn(Fn, 'param1')
console.log(newObj.param1)
console.log(newObj.param2)
console.log(newObj.param3)