JS基础篇-this

63 阅读3分钟

JavaScript中this概念

当前执行上下文(global、function 或 eval)的一个属性,

  • 全局上下文,this总是指向window

  • 函数上下文中,this取决于函数的调用方式。

    • 在非严格模式下,如果没有设置this,那么this会指向全局的Window,而严格模式下,会指向undefined
        function fn () {
            return this
        }
        fn() === window  // true 非严格模式, 未设置this时,指向window
    
        function fn2 () {
            'use strict'
            return this
        }
        fn2() === undefined // true 严格模式下 未设置this时,本身为undefined
    
    • 与this相关的函数
      1. 常见的隐式调用, 看调用函数时的主体,xxx.fn(), 一般情况下xxx是函数fn执行时的this
          window.a = 'window-a'
          const obj = {
              a: 'obj-a',
              fn: function () {
                  console.log(this.a)
              }
          }
          obj.fn() // 输出 obj-a  
      
      1. 箭头函数,用箭头函数声明中的this,不受调用方式影响,看声明当时的主体,就是执行中的this
          window.a = 'window-a'
          const obj = {
              a: 'obj-a',
              fn: () => {
                  console.log(this.a)
              }
          }
          obj.fn() // 输出 window-a  
      
      1. call、apply改变this指向

          window.a = 'window-a'
          const obj = {
              a: 'obj-a',
              fn: function () {
                  console.log(this.a)
              }
          }
          const obj1 = { a: 'obj1-a' }
          obj.fn.call(obj1) // 输出 obj1-a, 那么把fn换成箭头函数,再使用call, 会改变this指向吗?可以试下~~
          obj.fn.apply(obj1) // 也同样输出obj1-a, 那么apply与call有何区别呐?
          
          // 改造下函数
          const obj = {
              a: 'obj-a',
              fn: function (...args) {
                  console.log(this.a)
                  console.log('args', args)
              }
          }
          const obj1 = { a: 'obj1-a' }
          
          obj.fn.call(obj1, '1', '2', '3', '4')
          obj.fn.apply(obj1, ['1', '2', '3', '4'])
          // 输出相同结果,不过apply支持数组传参,更便捷些, call参数要一个一个写
      
      1. bind使用
          window.a = 'window-a'
          const obj = {
              a: 'obj-a',
              fn: function () {
                  console.log(this.a)
              }
          }
          const obj1 = { a: 'obj1-a' }
          const fn = obj.fn.bind(obj1) // 需要先绑定,返回可执行函数,返回的函数中this,指向bind第一个参数
          fn() // 输出 obj1-a 
      
      

常见this面试题及解析

    window.a = 'window-a'

    const obj = {
      a: 'obj-a',
      fn1: function () {
        console.log('fn1:',this.a)
      },
      fn2: () => console.log('fn2:', this.a),
      fn3: function () {
        return function () {
          console.log('fn3:', this.a)
        }
      },
      fn4: function () {
        return () => {
          console.log('fn4:', this.a)
        }
      }
    }
    const obj1 = { a: 'obj1-a' }
    obj.fn1();           // 输出 obj-a  隐式调用
    obj.fn1.call(obj1);  // 输出 obj1-a call 改变this指向

    obj.fn2();           // 输出 window-a 箭头函数
    obj.fn2.call(obj1);  // 同样输出 window-a call不可改变箭头函数中的this指向

    obj.fn3()();           // 输出 window-a fn3中返回的函数是,  return function () {console.log('fn3:', this.a) } 再次执行时, 无前置的xxx主体,所以指向window
    obj.fn3.call(obj1)();  // 输出 window-a call改变的是fn3中的this, 并不是执行后,返回函数中的this
    obj.fn3().call(obj1);  // 输出 obj1-a , call,改变了fn3执行后函数中的this

    obj.fn4()();           // 输出 obj-a  fn4中函数this指向obj, 箭头函数this也指向 fn4中的obj
    obj.fn4.call(obj1)();  // 输出 obj1-a  call 改变了fn4中函数this,指向obj1, 同时箭头函数也改变
    obj.fn4().call(obj1);  // 输出 obj-a call不可改变箭头函数中的this指向

手写call、apply、bind

call、apply、bind都是Function原型的函数,要写在Function.prototype上

// 手写bind

Function.prototype.myBind = function (_this, ...args) {
    _this = _this || window
    // this是当前可执行的函数
    const fn = this
    _this.fn = fn // 防止 fn函数名重复, 可以用Symbol生成唯一key, 用后再删
    return function () {
       _this.fn(...args) 
    }
}

// 手写 apply 
Function.prototype.myApply = function (_this, args) {
    _this = _this || window
    // this是当前可执行的函数
    const fn = this
    _this.fn = fn // 防止 fn函数名重复, 可以用Symbol生成唯一key, 用后再删除
    _this.fn(...args)
}

// 手写 call 
Function.prototype.myCall = function (_this, ...args) {
    _this = _this || window
    // this是当前可执行的函数
    const fn = this
    _this.fn = fn // 防止 fn函数名重复, 可以用Symbol生成唯一key, 用后再删除
    _this.fn(...args)
}