this

106 阅读3分钟

学习this指向之前需要先去了解执行上下文、执行栈、作用域、作用域链相关的知识

什么是this?

首先,js执行分为创建阶段、执行阶段,创建阶段包含词法分析、语法分析、作用域规则确定,执行阶段包含创建执行上下文、执行函数确定this、垃圾回收。其次js引擎通过执行栈管理执行上下文,全局执行上下文、函数执行上下文 所以作用域沿着创建该函数的地方向上找、this执行的时候谁最后调用就指向谁。this就是指当前执行函数的上下文。函数体里面的this.x就是指当前运行环境的x。在哪个上下文执行,this就指向该上下文。

如何判断this指向?

优先级:new>call apply bind>方法调用>函数调用

  • 全局环境:this指向window
  • 构造函数:this指向新实例对象
  • 对象的方法中:this指向该方法运行时所在的对象
  • call调用对象必须是函数,首先判断是否为函数
  • 匿名函数的this是指向全局对象的,所以this指向window
  • new时候this指向window或者构造函数

this在哪个方法中,它就指向这个方法所在的对象。由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。

如何改变this指向?

new

  • 创建一个空对象,作为将要返回的对象实例
  • 将空对象的原型指向构造函数的prototype属性
  • 将构造函数的this指向这个空对象,并执行构造函数同时带上new时传入的参数。apply
  • 返回的是对象就直接返回,否则返回这个新对象
function news(constructor, params) {
    let args = Array.from(arguments)
    let cons = args.shift()
    let obj = Object.create(cons.prototype)
    let res = cons.apply(obj, args)
    return typeof res ==='object'&&res!==null?res:obj
}

call

  • 判断调用call的是否为函数,判断传入的新this,若没有设为window
  • 新this上添加symbol属性key,将旧this作为方法放入新this中
  • 执行该方法携带参数拿到返回值
  • 删除symbol属性
  • return 执行的结果
function call(context) {
    if (typeof this !== 'function') {
        console.error();
    }
    context = context || window
    let key = Symbol()
    context[key] = this
    let args = [...arguments].slice(1)
    let res = context[key](...args)
    delete context[key]
    return res
}

apply

和call一样,就是执行的时候,参数取...arguments[1]是个集合

bind

参数和call一样从第一个截取,保留新this 旧this和参数,return函数,判断新this instanceof Fn?this:旧this,拼接参数。

function bind(context) {
    if (typeof this !== 'function') {
    }
    context = context || window
    let fn = this
    let args = [...arguments].slice(1)
    return function Fn() {
        fn.apply(this instanceof Fn?this:context,args.concat(...arguments))
    }
}

call apply bind有什么区别?

  • 参数不同:call apply bind第一个参数都是this,call bind后面参数要列举出来,apply第二个参数是剩余参数的集合[]
  • 函数执行时机不同:call apply会直接执行函数,bind会保留旧this,arguments,return函数进行参数拼接