js基础篇-this指向全面解析

前言

什么是this

  • 有一个误解是this,是指向自身。学习this的第一步是明白this并不指向自身,也不指向函数的词法作用域
  • this是在运行时进行绑定的,并不是在编写时进行绑定的。
  • this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式

this指向的分析

调用栈和调用位置

js中的函数是一个一个调用的,分析出函数的调用栈并找出调用位置,调用位置则是在调用栈当前位置的前一个函数中。
例:

function fun1(){
    console.log('这是第一个函数')
    //调用栈 fun1
    //调用位置则是在fun1前一个,即为全局环境
    func2()
}
function fun2(){
    console.log('这是第二函数')
    //调用栈 fun1 -> fun2
    //调用位置则是在fun2前一个,即为fun1
    fun3()
}
function fun3(){
    //调用栈 fun1 -> fun2 -> fun3
    //调用位置则是在fun3前一个,即为fun2
    console.log('这是第三个函数')
}
fun1() //调用fun1
复制代码

绑定规则

根据函数的调用位置分析到底应用哪一条绑定规则,多条规则都可用时,规则的优先级。

  1. 默认绑定:独立函数调用(无法应用其他规则时的默认规则)。
    独立函数的调用this指向全局,即使调用位置在另外一个函数中,指向的也是全局
    例:

    function test(){
        const a = 'aaa'
        test2()
    }
    function test2(){
        console.log("this指向的测试",this.a) //this指向的测试,undefined(严格模式下this指向undefined,则this.a会抛出错误)
    }
    test()
    复制代码
  2. 隐式绑定:调用位置是否有上下文。
    如果被对象拥有或者包含,通过引用调用,this则指向该对象。
    例:

    function foo(){
        console.log(this.a)
    }
    const obj = {
        a: 2,
        fun:foo
    }
    obj.fun() //2
    复制代码

    注意:对象属性引用链只有在最后一层调用位置中起作用
    例:

    function foo(){
        console.log(this.a)
    }
    const obj1 = {
        a: 20,
        obj2:obj2
    }
    const obj2 = {
        a: 10,
        fun:foo
    }
    obj1.obj2.fun() //10,指向的是最后一层调用链,obj2
    复制代码

    注意:隐式绑定this丢失,当通过引用赋值将对象函数赋给一个变量,再通过变量调用函数,则相当于是独立函数的调用,走默认规则,this则指向全局(严格模式下指向undefined)
    例:

    function foo(){
        console.log(this.a)
    }
    const obj = {
        a: 10,
        fun:foo
    }
    const test = obj.fun
    test() //undefined
    复制代码
  3. 显示绑定 – call、bind、apply改变this的指向
    call和bind的用法一样,区别是是否立即执行(call立即执行,bind不会立即执行)。

    function foo(){
        console.log(this.a)
    }
    function foo1(){
        console.log(this.a)
    }
    const obj = {
        a:1
    }
    foo.call(obj) //1
    foo1.bind(obj)
    foo1() //1
    复制代码

    注意:一旦硬绑定之后,this的指向就无法再发生改变

  4. new绑定
    可以通过new给函数创建一个指向函数内部的this
    例:

    function foo(){
        console.log(this.a)
    }
    function foo(){
        console.log(this.a)
    }
    const obj = {
        a:1
    }
    复制代码
  5. 箭头函数:根据外层(函数或者全局)作用域来决定this。箭头函数this的指向不能改变
    例:

    function foo(){
        return () => {
            //继承自foo
            console.log(this.a)
        }
    }
    const obj1 = {
        a:1
    }
    const obj2 = {
        a:2
    }
    const fun = foo.call(obj1)
    fun.call(obj2) //1
    复制代码

总结

如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断this的绑定对象。

  1. 由new调用?绑定到新创建的对象。
  2. 由call或者apply(或者bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到undefined,否则绑定到全局对象。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享