面试官:请你手写一个bind

前言

我在之前的文章中经常会提到bind这个函数,那么bind究竟是个什么东西呢?本文将带大家深度剖析bind函数,并且带大家手写一个bind

bind

bind是用来改变this指向的一个函数(在前几篇文章中,我们讨论了this,在这里就不多赘述(this到底是谁的舔狗???😍 - 掘金 (juejin.cn))。

先来看官方文档是如何描述的

Function 实例的 bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。

也就是说,bind()中可以传入n个参数,让this指向第一个参数。

我们先来看一个例子:

js 复制代码
const obj = {
    name: 'Alice',
    greet: function () {
      console.log(`Hello, ${this.name}!`);
    },
  };
  // 调用obj的greet方法,此时this值将指向obj对象
  obj.greet(); // 输出:Hello, Alice!
  const greet = obj.greet;
  // 直接调用greet函数,此时this值将指向全局对象(window)
  greet(); // 输出:Hello, undefined
  // 使用bind函数将greet函数绑定到obj对象上
  const boundGreet = obj.greet.bind(obj);
  // 调用绑定后的函数,此时this值将永久地绑定到obj对象
  boundGreet(); // 输出:Hello, Alice!

在this的文章中,我们提到可以通过对象绑定来绑定this,所以obj.greet()是可以访问到name属性的,

但是我们将obj.greet赋值给greet的时候,发生了隐式丢失,这里输出"Hello, undefined"

const boundGreet = obj.greet.bind(obj);当我们使用bind绑定之后,this绑定在obj上,此时输出为"Hello, Alice!"

由此观之,bind绑定的使用极其重要!

手写一个bind

既然bind如此重要,那么我们就来手写一个bind

  1. 思考

先来思考一下,我们的bind该怎么样完成?

  • 函数可以调用我的bind方法
  • 可以传入多个参数
  • 将this绑定到第一个参数

我们需要完成的功能基本就是这三个了,接下来我们将开始完成这些功能

  1. 动手
  • 函数可以调用我的myBind方法

要让我们的myBind方法可以让任何一个函数调用,我们只需要将其写在函数原型上即可

js 复制代码
Function.prototype.myBind = function(){}
  • 可以传入多个参数

当我们要向里面传入多个参数时,该怎么办呢?我们可以使用...args,注意第一个参数是要绑定到的 那个参数

js 复制代码
Function.prototype.myBind = function(context,...args){}
  • 将this绑定到第一个参数

最简单的两步已经完成,我们的重点在第三步,将this绑定到第一个参数

我们又该怎么做呢?

1)首先我们要先判断,是否是函数调用我们的方法

js 复制代码
if(typeof this!== "function"){
    throw new TypeError("error") 
}

看到这里,你估计会说:what?我们不是写在函数的原型上吗?只有函数才能调用我,那我这里还判断个球? 好兄弟,你怕是忘记了箭头函数这个老六了,箭头函数 sayHello 没有自己的 this 值,所以让箭头函数调用 bind方法是没有效果的。

2)当我们没有传入参数时怎么办

js 复制代码
    context = context || window//闭包之中

当我们没有传入参数时,就让this指向全局的window

3)记住,此刻我们的this指向的是我们调用myBind的那个函数,在下面,我们会调用他,所以在这里需要先将this保留下来

js 复制代码
let that = this;

4)最后,我们应该根据不同的调用来完成this绑定,在这里我们return出一个fn方法

如果新函数 fn 使用 new 关键字调用,即通过构造函数的方式调用,那么它会创建一个新的实例,并将 this 值设置为新实例。在这种情况下,fn 函数会调用原始函数,并传递所有的参数,包括原始参数和新参数。这样,在实例化时就可以使用 new 关键字调用被绑定的函数,而且 this 值将被正确地设置为新实例。

否则,如果新函数 fn 是通过普通函数调用方式调用的,那么它会使用 apply 方法来调用原始函数,并将 this 值手动绑定到指定的 context 上,同时传递所有的参数。

js 复制代码
return function fn(...innerArgs){
//this丢失 
    if(this instanceof fn){ 
        return new that(...args,...innerArgs) 
    } 
return that.apply(context,[...args,...innerArgs])
//原先的函数执行,this 手动指定为context 
}

完整的myBind在这里

js 复制代码
Function.prototype.myBind = function(context,...args){
    //函数 this
    // this()
    if(typeof this!== "function"){
        throw new TypeError("error")
    }
    context = context || window//闭包之中
    let that = this;
    return function fn(...innerArgs){
        //this丢失
        if(this instanceof fn){
            return new that(...args,...innerArgs)
        }
        return that.apply(context,[...args,...innerArgs])
        //原先的函数执行,this 手动指定为context
    }
}

我们来调用一下

js 复制代码
Function.prototype.myBind = function(context,...args){
    //函数 this
    // this()
    if(typeof this!== "function"){
        throw new TypeError("error")
    }
    context = context || window//闭包之中
    let that = this;
    return function fn(...innerArgs){
        //this丢失
        if(this instanceof fn){
            return new that(...args,...innerArgs)
        }
        return that.apply(context,[...args,...innerArgs])
        //原先的函数执行,this 手动指定为context
    }
}
function sayHello(x, y, z) {
    console.log("函数中的",this);
    console.log("你好!",this.name);
    console.log(x + y + z);
}
const arrowFn = ()=>{}
let person = {
    name:"坤坤"
}
// arrowFn.myBind()
let bindFn = sayHello.myBind(person,1,2);
console.log(bindFn(3));

确实,我们将this绑定到了坤坤身上...

结尾

手写源码还是有点废脑子的😭,但是当大家理解了以后,多刻意练习,还是难不倒聪明不绝顶的掘友们的!

相关推荐
我是苏苏6 分钟前
C# Main函数中调用异步方法
前端·javascript·c#
转角羊儿17 分钟前
uni-app文章列表制作⑧
前端·javascript·uni-app
大G哥23 分钟前
python 数据类型----可变数据类型
linux·服务器·开发语言·前端·python
hong_zc1 小时前
初始 html
前端·html
小小吱1 小时前
HTML动画
前端·html
Xiao Fei Xiangζั͡ޓއއ1 小时前
一觉睡醒,全世界计算机水平下降100倍,而我却精通C语言——scanf函数
c语言·开发语言·笔记·程序人生·面试·蓝桥杯·学习方法
Bio Coder1 小时前
学习用 Javascript、HTML、CSS 以及 Node.js 开发一个 uTools 插件,学习计划及其周期
javascript·学习·html·开发·utools
糊涂涂是个小盆友1 小时前
前端 - 使用uniapp+vue搭建前端项目(app端)
前端·vue.js·uni-app
NMBG221 小时前
[JAVAEE] 面试题(四) - 多线程下使用ArrayList涉及到的线程安全问题及解决
java·开发语言·面试·java-ee·intellij-idea
浮华似水2 小时前
Javascirpt时区——脱坑指南
前端