this指向问题以及手写call,apply,bind函数

97 阅读3分钟

this指向问题

什么是this

this表示当前函数的执行上下文,this一般存在在函数中,当函数执行时指向当前执行函数的对象,若当前函数无调用对象,则指向全局对象window,开启严格模式则为undefined,

  function fn(){
    console.log(this)
  }

  function fn1(){
    "use strict" //严格模式
    console.log(this)
  }
  const obj = {
    fn2 = function(){
      console.log(this)
    }
  }
  fn()  // window
  fn1() // undefined
  obj.fn2() //obj

NEW关键字

New关键字会通过构造函数生成一个空对象并返回this,this指向返回的空对象

箭头函数

箭头函数没有自己的this,他的this指向就是外层第一个对象或普通函数的this,要注意的是箭头函数中的this无法再次修改

事件绑定

事件绑定中的this会指向事件源

定时器绑定

定时器的this指向window,因为定时器方法在window对象中,并且参数使用回调函数作为处理函数,而回调函数的this指向widnow

call,apply,bind函数调用

其中call,apply是临时改变一次this指向 bind则是永久改变

手写call,apply,bind

函数原型的call,apply,bind方法都会改变this指向,call和apply只是类似,但传参不同,bind则可以柯里化形式进行传参,但使用bind会形成闭包

由于es6出来已经相当久了,所以手写时会使用到一些es6语法

  • 剩余参数
  • 扩展运算符
  • 解构
  • Symbol
  • 三目运算符
  • 块级作用域 const,let

其他知识:

  • 闭包
  • 垃圾回收机制
  • 柯里化函数

调用函数原型上提供的call方法:

call方法第一个参数为this指向的上下文,后续1个或多个参数都会以剩余参数的方式输出

  const obj = {a,1}
  function foo (...args){
    console.log(this)
    console.log(...args)
  }
  foo(1,2,3,4,5) //输出 window和1,2,3,4,5
  foo.call(obj,1,2,3,4,5) //输出 obj和1,2,3,4,5

实现call

  Function.prototype._call = function (context,...args) {
    // 将传入的上下文保存,如果传入的上下文为null或者undefined,则将上下文指向window
    const ctx = context ? context : window
    // 通过symbol创建唯一属性
    const key =Symbol()
    // 将唯一属性指向调用_call函数的函数
    ctx[key] = this
    // 由于call方法是函数原型上的方法,所以this的指向一定是函数
    // 立即调用一次函数,并将结果保存
    const result = ctx[key](...args)
    // 将我们将创的key属性删除
    delete ctx[key]
    // 返回调用结果
    return result
  }
  foo(1,2,3,4,5) //输出 window和1,2,3,4,5
  foo._call(obj,1,2,3,4,5) //输出 obj和1,2,3,4,5

实现apply

调用函数原型上提供的apply方法:

apply方法第一个参数为this指向的上下文,第二个参数接收一个数组

  const obj = {a,1}
  function foo (...args){
    console.log(this)
    console.log(...args)
  }
  foo(1,2,3,4,5) //输出 window和1,2,3,4,5
  foo.apply(obj,[1,2,3,4,5]) //输出 obj和1,2,3,4,5

apply函数的实现和call大同小异,只不过是将剩余参数换成了数组

    Function.prototype._apply_ = function (context,arr) {
      const ctx = context ? context : window
      const key = Symbol()
      ctx[key] = this
      const result = ctx[key](arr)
      delete ctx[key] 
      return result
    }

调用函数原型上提供的bind方法:

bind方法会返回一个可以柯里化调用的函数

  const obj = {a,1}
  function foo (...args){
    console.log(this)
    console.log(...args)
  }
  foo(1,2,3,4,5)
  // bind会返回一个可以柯里化调用的函数,但是在没有参数是情况下不会执行,但使用bind会产生闭包
  const fn = foo.bind(obj)
  fn(1) // obj 1
  const fn1 = foo.bind(obj,1)
  fn1(2,3,4) // ojb 1,2,3,4

实现bind

    Function.prototype._bind_ = function (context,...args) {
      // 保存当前指向
      let self = this
      // 保存当前参数
      let arg = [...args]
      // 返回一个函数
      return function (...args){
        let temp = [...args]
        // 函数中调用了参数arg,会形成闭包
        // 由于返回的函数中调用了apply,所以返回的函数被调用时this指向依然是bind传入的上下文,而不是指向调用对象
        // 并且apply会立即执行当前函数,返回结果,所以函数也会正常执行
        self.apply(context,[...arg,...temp])
      }
    }