学习JS原理之this指向

137 阅读3分钟

什么是this

this是JS关键字,代表的是你当前函数的上下文对象,因此this实际上指向的是其被调用的位置,而不是其词法作用域;是包含它的函数作为方法被调用时所属的对象。所以说,调用方式的不同,实际上最后呈现的结果也是不一样的。

如何判断this指向

  • 普通函数调用/直接调用,this 指向的是全局 window 对象。
var name = 'window'
function fun() {
    var name = 'funcion'
    console.log(this.name)
}
fun() // window 
  • 对象方法调用,this 指向该对象(前边谁调用 this 就指向谁)。
var name = 'window'
var obj = {
    name: 'obj',
    fun() {
        console.log(this.name)
    },
    handle: {
        name: 'handle',
        handlefun() {
            console.log(this.name)
        }
    }
}
obj.fun()  // obj
obj.handle.handlefun() // handle
  • 作为构造函数调用,this 永远被绑定在新创建的对象实例上,任何方式都改变不了 this 的指向。
function newObj(name){
    this.name = name
    console.log(this) // {name: '林'}
    // return this
}
var result = new newObj('林')

调用new操作符,会执行的一系列操作,请查看(还未完成)----我理解的new操作符创建实例过程

  • 箭头函数的this
    由于嵌套函数中的 this 不会从外层函数中继承,那么有两种解决办法:
    第一种是把 this 保存为一个 self 变量,再利用变量的作用域机制传递给嵌套函数。
    第二种是继续使用 this,但是要把嵌套函数改为箭头函数,因为箭头函数没有自己的执行上下文,所以它会继承调用函数中的 this。

我们直接对比箭头函数和普通函数的区别:

  1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
  2. 箭头函数不能绑定arguments,取而代之用rest参数...解决
    function test(arg){
      console.log(arguments)
    }
    test(1,2,3)  // [1,2,3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    let fun = (...arg) => {
      console.log(arg);
    }
    fun(4,5,6) // [4,5,6]
  1. 箭头函数没有原型属性
    var test = ()=>{
      return 1
    }
    function fun(){
      return 2
    }
    console.log(test.prototype)  // undefined
    console.log(fun.prototype)   // {constructor: ƒ}
  1. 箭头函数中没有this绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this,否则this的值则被设置为全局对象。并且无法改变。
    var name = 'window';
    var test = {
        name: 'inner',
        fun: function(){
            var innerFun = () => {
                console.log(this.name)
            }
            innerFun()
        },
        fun2: () => {
            console.log(this.name);
        }
    }
    test.fun() // 'inner'
    test.fun2() // 'window'
  1. 箭头函数通过 call()或apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
    const obj = {
        a: 10
    }
    var test = (...arg)=>{
        console.log(arg)  
    }
    test.call(obj, 10) // [10]
  • setTimeout的this
    setTimeout(handleFun, time);定时器第一个参数传入一个处理函数,该函数的this会被改变指向window
var name = 'window'
const obj = {
    name: 'inner',
    fun1() {
        console.log('fun1===>', this.name)
    },
    fun2() {
        setTimeout(function() {
            console.log('fun2===>', this.name)
        }, 500)
    },
    fun3() {
        setTimeout(() => {
            console.log('fun3===>', this.name)
        })
    }
}
obj.fun1() // inner
obj.fun2() // window
obj.fun3() // inner

如何修改this指向

  • call 方法
    fun.call(thisArg, arg1, arg2, ...)

thisArg为要指向的对象,后面为要传入的参数值;改变this后立即执行

    var name = 'window'
    const obj = {
        name: 'obj'
    }
    function test(...args) {
        console.log(args)
        console.log(this.name)    
    }
    test(1,2,3) // [1,2,3] window
    test.call(obj, 1, 2, 3) // [1,2,3] obj
  • apply 方法
    fun.apply(thisArg[, arg1[, arg2[, ...]]])

thisArg为要指向的对象,后面为要传入的参数数组;改变this后立即执行

    var name = 'window'
    const obj = {
        name: 'obj'
    }
    function test(...args) {
        console.log(args)
        console.log(this.name)    
    }
    test(1,2,3) // [1,2,3] window
    test.apply(obj, [1,2,3]) // [1,2,3] obj
  • bind 方法
fun.bind(thisArg[, arg1[, arg2[, ...]]])

thisArg为要指向的对象,后面为要传入的参数值;改变this后生成新函数;调用函数时this指向thisArg

    var name = 'window'
    const obj = {
        name: 'obj'
    }
    function test() {
        console.log(arguments)
        console.log(this.name)    
    }
    test(1,2,3) // [1,2,3] window
    const newFun1 = test.bind(obj, [1,2,3])
    const newFun2 = test.bind(obj, 4,5,6)
    newFun1()
    newFun2()

  • call、apply、bind 三者的区别是什么
    共同点:

    1. 都能改变 this 指向,第一个传递的参数都是 this 指向的对象。
    2. 三者都采用的后续传参的形式。

    不同点:

    1. call的传参是单个传递的,apply后续传递的参数是数组形式,而bind没有规定,传递值和数组都可以。
    2. call和apply函数是立即执行,而bind函数会返回一个函数,随时调用。

图文解读(引用他人图片)