手写js bind

124 阅读1分钟

bind的第一个参数为新绑定的上下文,而其余参数将作为新函数的参数,供调用时使用。 fn = fn.bind(this, args);

// 用法
const obj = {name: 'xiaoming'};
function sayName(...arg) {
    console.log(arg[0] || '---------');
    console.log(this.name);
}

const objSayName = sayName.bind(obj, '-----practice-bind-----');
objSayName();
// es6 实现bind
Function.prototype._bind1 = function(context, ...args) {
    // return (...args2) => this.call(context, ...args, ...args2);
    return (...args2)  => this.apply(context, [...args, ...args2]);
}
const objSayName1 = sayName._bind1(obj, '--------es6-bind--------');
objSayName1();
// 该方法简单但es6兼容性有问题
// es5 基本实现bind
Function.prototype._bind2 = function() {
    var args = Array.prototype.slice.call(arguments, 0);
    var context = args.splice(0, 1)[0];
    var fn = this;
    return function () {
        var args2 = Array.prototype.slice.call(arguments, 0);
        var newArgs = args.concat(args2);
        return fn.apply(context, newArgs);
    };
}
const objSayName2 = sayName._bind2(obj, '--------es5-bind-base--------');
objSayName2();

如果使用了bind之后的fn指向了 指定的上下文,然后将返回的函数作为构造函数,那么这时候 bind指定的上下文 与 new创建的上下文(新对象) 到底 指向谁?

var testObj = {a: 0};
function setA (val) {
    console.log('-----------------')
    this.a = val;
}
// 先测试一段 原生bind 与new的结果
var testBind = setA.bind(testObj);
var testNewObj = new testBind(1);

console.log('testObj.a: ' + testObj.a); // testObj.a: 0
console.log('testNewObj.a: ' + testNewObj.a); // testNewObj.a: 1
// 执行new 改变this指向 要强过bind

如果用上面手写的bind来测试

var testHandBind = setA._bind2(testObj);
var testHandObj = new testHandBind(2);
console.log('testObj.a: ' + testObj.a); // testObj.a: 2
console.log('testHandObj.a: ' + testHandObj.a); // testNewObj.a: undefined
console.log(testHandObj) // {}
//如上结果与 原生bind出现不一致性 那么就需要兼容这种情况
Function.prototype._bind3 = function() {
    var args = Array.prototype.slice.call(arguments, 0);
    var context = args.splice(0, 1)[0];
    var fn = this;
    if (typeof fn !== "function") {
      throw new Error("Must accept function");
    }
    var resFn = function() {
        var args2 = Array.prototype.slice.call(arguments, 0);
        var newArgs = args.concat(args2);

        // resFn.prototype.isPrototypeOf(this)
        // 这里的判断很多地方都可以看到,可以用来判断构造函数是不是通过new关键字实例化
        // 从而写出对应的兼容代码
        if (this instanceof resFn) {
            fn.apply(this, newArgs);
        } else {
            return fn.apply(context, newArgs);
        }
    }
    return resFn;
}

var testHandBind1 = setA._bind3(testObj);
var testHandObj1 = new testHandBind1(3);
console.log('testObj.a: ' + testObj.a); // testObj.a: 2
console.log('testHandObj1.a: ' + testHandObj1.a); // testNewObj.a: undefined
console.log(testHandObj1) // {}

testHandBind1(4);
console.log('testObj.a: ' + testObj.a);