JS bind()

200 阅读2分钟

Function.prototype.bind()

仅供本人学习使用

方法

bind()方法主要就是将函数绑定到某个对象,
bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,
例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的 this 自然指向的是 obj
var a = {
    b: function(){
        var func = function(){
            console.log(this.c);
        }
        func();
    },
    c:'hello'
}

a.b(); // undefined 这里的this指向的是全局作用域
console.log(a.c); // hello

// 对比
var a = {
    b: function(){
        var _this = this;
        var func = function(){
            console.log(_this.c);
        }
    },
    c: 'hello'
}

a.b(); // hello
console.log(a.c) // hello

var a = {
    b: function(){
        var func = function(){
            console.log(this.c);
        }
        func.bind(this)();
    },
    c:'Hello!'
}
a.b(); // Hello!

这里有个比较容易混淆的,我一开始以为 bind 是把某个函数的this绑定到某个对象,并且这个函数已经被改变了,但是,bind 并没有改变原函数,它只是返回了一个这个函数的 this 绑定了传入进去的对象的函数,也就是说它并没有改变原函数。

函数柯里化

function f(y, z){
    return this.x + y + z;
}

let m = f.bind({x: 1}, 2);
console.log(m(3));  // 6

从第二个参数起,会依次传递给原始函数。这里的第二个参数,对应的是f函数的y参数,最后调用 m(3) 的时候,3对应的便是最后一个参数 z, 所以执行结果为:1 + 2 + 3 = 6 。

用 bind 方式预定义参数

function list(){
    // console.log(arguments);
    // console.log(Array.prototype.slice.call(arguments));
    return Array.prototype.slice.call(arguments);
}

// 预定义参数
let x = list.bind(undefined, 10);

let list1 = x(); // [10]
let list2 = x(1, 2, 3); // [10, 1, 2, 3]

其中 Array.prototype.slice.call(arguments) 是用来将参数由类数组转换为真正的数组,undefined 表示 this 的指向,10 是 x 中真正的第一个参数

实现 bind 方法

方法一,仅可以绑定,不可传参

Function.prototype.my_bind = function(context){
    let self = this;
    return function(){
        return self.apply(context, arguments);
    }
}

function RName(){
    return this.name;
}
RName();  // ""

let person = {name: 'kong'};
RName.bind(person)();  // kong
RName.my_bind(person)();  // kong

方法二

Function.prototype.my_bind = function(context){
    let args = Array.prototype.slice.call(arguments, 1);  // [2022, 4]
    let self = this;
    return function(){
        let innerArgs = Array.prototype.slice.call(arguments);  // [30]
        let finalArgs = args.concat(innerArgs);
        return self.apply(context, finalArgs);
    }
}

function RName(year, month, day){
    return this.name + ' ' + year + '/' + month + '/' + day;
}

let person = {name: 'kong'};
RName.my_bind(person, 2022, 4)(30)  // 'kong 2022/4/30'

args 表示的是在bind时传入的预定义参数,即为 2022 和 4 ,分别表示year 和 month

innerArgs 表示的时调用返回的参数的时候传入的参数,这里为 30

方法三

bind返回的函数如果作为构造函数,搭配new关键字出现的话,我们绑定的this就需要被忽略

// 处理构造函数场景下的兼容
Function.prototype.bind = Function.prototype.bind || function(context){
    // 确保调用bind方法的一定要是一个函数
    if(typeof this !== "function"){
        throw new TypeError("Function.prototype.bind-what is trying to be bound is not callable");
    }
    
    let args = Array.prototype.slice.call(arguments, 1);
    let self = this;
    let F = function(){};
    let bound = function(){
        let innerArgs = Array.prototype.slice.call(arguments);
        let finalArgs = args.concat(innerArgs);
        return self.apply(this instanceof F ? this : context || this, finalArgs);
    }
    bound.prototype = new F();
    return bound;
}

参考文章: JS中的bind()方法