面试题:深入解析 bind 实现原理

·  阅读 486

官方解释:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

翻译过来也就是: 该 bind() 方法创建一个新函数,该新函数在被调用时将其 this 关键字设置为提供的值,并在调用新函数时提供给定的参数序列之前。

我们都知道,bindcall/apply 一样,都是用来改变上下文 this 指向的,不同的是,call/apply 是直接使用在函数上,而 bind 绑定 this 后返回一个函数(闭包)

刚说完 bind 是返回一个函数,它的执行很像函数柯里化的一个过程,如下:

fuction fn1(x){                // 柯里化函数
   return function(y){
     return x + y  
   }
}
fn1(1)(2) // 3

function fn2(a,b){             // bind
   return a + b
}
fn2.bind(null, 1)(2) // 3
复制代码

这里是 bind 的一个特殊用法,预设参数。只要将这些参数(如果有的话)作为bind() 的参数写在this后面。 当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。如下:

function sum (a,b) {
   return a + b
}
let add1 = sum.bind(null, 12)
let result = add1(5)  // result = 17

result = add1(6, 3) // result = 18 注意第二个3被丢弃了


复制代码

下面开始解析,在解析前先看如下一题:

 function f() {
     console.log(this);
   }
   let f1 = f.bind(1).bind(2);
  f1(); // this => 1
复制代码

拆分步骤就等同与下面:

let case = f.bind(1); 
let f1 = case.bind(2); 
f1();
复制代码

casef.bind(1) 的执行结果,这里改变的是 fthis。即:

case = fuction(){
    f.apply(1);
}
复制代码

同理,f1 执行结果,改变的是 casethis 指向

f1 = function () {
    case.apply(2);
}
复制代码

重点: f1() 执行的时候改变的是casethis,而不是 fthis

f1() 的时候是调用 case.apply(2),通过 case.apply(2) 再去调用 f.apply(2)。所以不论中间有多少层 bind,最后那个 f 调用永远都是最开始的 1

如下:

let case = f.bind(1);
let f1 = case.bind(2);
let f2 = f1.bind(3);
let f3 = f2.bind(4);
let f4 = f3.bind(5);
let f5 = f4.bind(6);
f5();

f5() => f4.apply(6) => f3.bind(5) => f2.bind(4) => f1.bind(3) => case.bind(2) => f.bind(1) 
复制代码

bind基本实现原理[传递一个参数]

Function.prototype.bind = function(context){
    let _this = this;
    return function(){
        _this.apply(context, arguments)
    }
}
复制代码

修改一下:bind基本实现原理[传递多个参数]

Function.prototype.bind = function(context){
    let _this = this;
    // 把方法的参数截取出来
    let args = [].slice.call(arguments, 1); 
    return function(){
        // 预设参数一定是 args 在前拼接
        _this.apply(context, args.concat([].slice.call(arguments)))
    }
}
复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改