手写bind函数的方法实现

291 阅读2分钟

在写bind函数之前,先来了解一下什么是bind函数。引用MDN的一句话来说:

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

简单来说:bind是一个绑定了this的新函数,它跟apply、call一样都是改变this指向的,但唯一区别是bind函数不会立刻执行,需要进行函数调用才能执行;

简单举个栗子:

let obj1 = {
    num: 1
};
let obj2 = {
    num: 2
}

function fn (x,y) {
    console.log(x+y+this.num);
}
fn.call(obj1,1,2); // 4
fn.apply(obj1,[1,2]); // 4

let _fn = fn.bind(obj1,1);
_fn(2); // 4

总结一下bind的特点:

1、改变this指向;

2、返回了一个绑定了this的新函数;

3、支持函数柯里化:我们在调用_fn之前传入了一部分参数,调用时参入了剩余参数;

函数柯里化:简单来说就是一个函数传入部分参数,返回一个新函数处理剩余的参数;

举个栗子:

function fn (x,y) {
    return function(y) {
        console.log(x+y);
    }
}
let _fn = fn(1);
_fn(2); // 3
fn(1)(2); // 3

下面简单来实现以下bind函数:

Function.prototype._bind = function(obj) {
    let fn = this;
    return function() {
        fn.call(obj);
    }
}
let obj = {
    num: 1
}
function fn() {
    console.log(this.num);
}
let _fn = fn._bind(obj);
_fn();// 4

上面这个bind函数简单实现了改变this, let fn = this; 这一行代码注意一下,如果不保存this,this是指向window; 但是有一个问题: 不支持传入参数,下面来完善一下:

Function.prototype._bind = function (obj) {
    // 从第1位裁剪数据,因为第0位是this;
    let args = Array.prototype.slice.call(arguments,1);
    let fn = this;
    return function() {
        fn.apply(obj,args);
    }
}

let obj = {
    num: 1
}
function fn(x) {
    console.log(x+this.num);
}
let _fn = fn._bind(obj,1);
_fn();// 2

上面代码实现了参数传递,但是还不算完美,因为之前说过bind函数支持函数柯里化,在完善一下:

Function.prototype._bind = function(obj) {
    //  从第1位裁剪数据,因为第0位是this;
    let args = Array.prototype.slice.call(arguments,1);
    let fn = this;
    return function() {
        // 截取剩余参数
        let params = Array.prototype.slice.call(arguments);
        fn.apply(obj,args.concat(params));
    }
}

let obj = {
    num: 1
}
function fn (x,y) {
    console.log(x+y+this.num);
}
let _fn = fn._bind(obj,1);
_fn(2); // 4

函数柯里化的方式实现了bind函数,这里注意的是concat合并参数的时候args在前,params在后,顺序不能搞错这样参数的顺序才对;

完。