前言

最近在在搞React的时候有用到bind()的时候,因为他的用法其实我还一直不是特别的清楚,所以今天我把bind()他的用法和我遇到的结合起来这样来写一个博客,这样应该可以加深自己的印象同时可以来跟好的来解析bind的使用方法

1.那么我们先来介绍一下bind()

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(); 
  console.log(a.c); 

那么答案是什么呢?undefined和hello,大家可以打印看下,这个就是因为fun()这个函数执行的时候他的函数上下文为window,而a.b()的这个函数的执行的时候函数上下文this为a对象是什么意思呢?

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

看了上面的例子和console.log()的结果大家应该是知道了这个函数上下文大概是这么一回事了把?什么还是不会好把看这里,这里有关于this指向的问题的解析那么问题来了当我们希望func()他的输出的值就是为hrllo怎么办

方法一:改变this的值

  var a = {
    b: function() {
      var _this = this; // 通过赋值的方式将this赋值给that
      var func = function() {
        console.log(_this.c);
      }
      func();
    },
    c: 'hello'
  }
  a.b(); // hello
  console.log(a.c); // hello

方法二:绑定this的值发生改变

// 使用bind方法一
  var a = {
    b: function() {
      var func = function() {
        console.log(this.c);
      }.bind(this);
      func();
    },
    c: 'hello'
  }
  a.b(); // hello
  console.log(a.c); // hello

// 使用bind方法二
  var a = {
    b: function() {
      var func = function() {
        console.log(this.c);
      }
      func.bind(this)();
    },
    c: 'hello'
  }
  a.b(); // hello
  console.log(a.c); // hello

这里我们以a.b()的形式去执行a对象中的b这个函数,是经过对象a的所以当我们来执行a对象中b函数的时候找this就会先找到a对象所以在a对象中的b这个函数中的this为a对象,所以这个时候bind,绑定的this也就是为a对象了

    c = 10;
    var a = {
      b: function () {
        console.log(this);
        var func = function () {
          console.log(this.c);
        }.bind(this);
        func();
      },
      c: 'hello'
    }
    var d = a.b;
    d();

这里我们以d()的形式去执行a对象中的b这个函数吗,因为d()的执行的时候由于没人应用this默认为window,所以在a对象中的b这个函数中的this为window,所以不这个时候bind,绑定的this也就是为window了

bind()的原生实现分步解析

1:通过call,吧arguments生成一个真正的数组

Array.prototype.slice.call(arguments)是用来将参数由类数组转换为真正的数组;
[].slice.call(arguments)是用来将参数由类数组转换为真正的数组;

上面两中方法的结果一样

解析:其实就是arguments他是一个类数组(对象)他没slice的方法 ,通过上面的Array.prototype.slice写法可以让他添加slice的方法,为什么可以呢?

slice()没参数默认从key0到最后一项,浅拷贝,不影响原数组

因为其实arguments也是一个对象,就相当与是在arguments中执行了slice()函数那么不要参数的情况下就是根据key下标会返回一个完整的数组从而达到了生成了一个新的数组的效果,arguments对象中是有length属性和0.1.2.这样的属性的而slice就是会根据这些0.1.2的这些key(也叫属性值的东西)放回成为一个数组

原生bing实现

    Function.prototype.myBind = function () {
      if (typeof this !== 'function') throw 'caller must be a function'
      let self = this // 这里是关键 用来和new出来的比较原型来判断是否为new出来的  当前函数对象
      let context = arguments[0]
      let args = Array.prototype.slice.call(arguments, 1) // 旧:参数
      let fn = function () {
        let fnArgs = Array.prototype.slice.call(arguments) // 新:参数
        // bind 函数的参数 + 延迟函数的参数
        // 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
        self.apply(this instanceof self ? this : context, args.concat(fnArgs))
      }
      fn.prototype = Object.create(self.prototype) // 维护原型
      return fn
    }

关键点

1.bing是影响不到new构造函数过程中this为新构造出来的那个对象的定义

当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。 ---MDN

代码:this instanceof self

this:对myBing出来的函数所用者的指向

self:使用myBing()的那个函数

instanceof:用来检查构造函数的prototype属性是否出现在某一个实例对象的原型链上

代码: fn.prototype = Object.create(self.prototype) // 维护原型,用来满足instanceof的条件(下面分析)

2.由于返回的是新定义出来的函数,所以原型要记得绑定 

3.Object.create()  专门用来绑定__proto__的

分析

这里帮忙分析下子在用new流程的时候为什么要---(this instanceof self)

var YAxisPoint = Point.MyBind(null, 0/*x*/);

var axisPoint = new YAxisPoint(5);

我们以上面代码为例子,所以可以得出

1. self:Point 函数

我们使用关键词new的时候在执行YAxisPoint函数,那么YAxisPoint 函数中的this就是我们new出来的实例了

2..this = 新出来的实例对象

由于this等于新new出来的实例其实this instanceof self不应该为true的,因为新创建的函数和引用myBind的函数(self函数)完全无瓜葛

但是,fn.prototype = Object.create(self.prototype),导致fn构造函数new出来的实例指向的prototype和引用myBind的函数(self函数)的prototype是一样的,所以this instanceof self === true

意外的痛苦

apply和call实现

    Function.prototype.newApplyOrCall = function () {
      // 预防
      if (typeof this !== 'function') throw '请使用函数调用'
      let self = arguments[0] || window // 虽然是必填项但是在非严格模式,null undefined 会默认转为window
      let args = [...arguments].flat().slice(1)

      let fn = Symbol("_newApplyOrCallFn");
      self[fn] = this // 这里添加属性还是有点问题,并不可以完全删除
      // Reflect.deleteProperty(self, fn) // 删除属性
      const res = self[fn](...args)
      // delete self[fn]
      Reflect.deleteProperty(self, fn) // 删除属性
      return res
    }

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐