JS中this指向理不清?!深度解析this,小白也能看懂

171 阅读5分钟

在JavaScript中,this 是一个特殊的关键字,它通常用于指代当前执行上下文中的对象。但 this 的指向是动态的,它取决于代码执行的方式和上下文。这可能导致在实际使用中分不清this实际指向的内容,从而非常困扰我们,不过不用担心,接下来我们将详细剖析这个关键词的指向规则。

this的绑定规则

  1. 默认绑定 --- 函数在哪里调用,this就指向哪里(不全对)->函数在哪个词法作用域里生效,this就指向哪个词法作用域(实际上都会指向全局作用域)
  2. 隐式绑定 ---当函数被一个对象所拥有,再调用时,这种情况下,在调用时,this会指向该对象
    隐式丢失 --- 当函数被多个对象链式调用时,this指向最近引用函数的对象
  3. 显式绑定 --- call ,apply,bind
  4. new 绑定
  5. 箭头函数问题

默认绑定

默认绑定的情况下this关键字指向的是全局作用域

可能别的解释会说"函数在哪里调用,this就指向哪里"实际上具体应该是"函数在哪个词法作用域里生效,this就指向哪个词法作用域",但是最后在默认调用的情况下,this所指向的都是全局作用域。这个结论建议直接记,仔细分析也可以。请分析以下代码的执行结果:
本文中涉及默认绑定的代码需要在浏览器环境下运行,node环境当中没有window对象

//默认绑定
    var a=1
    function foo(){
        var a=2
        function bar(){
            baz()
        }
        function baz(){
            console.log(this.a)
        }
        bar()
       
    }
    foo()

分析

  • 调用函数foo,执行内部逻辑
  • 执行bar内部语句baz(),执行baz内部语句 console.log(this.a)
  • this指向该函数的词法作用域,那么就指向了foo,读取到a的值为2
  • 最终输出2

其实以上是错的

正确分析:

  • 调用函数foo,执行内部逻辑
  • 执行bar内部语句baz(),执行baz内部语句 console.log(this.a)
  • this指向该函数的词法作用域,那么就指向了foo的作用域,但是foo的作用域并不是其词法作用域,故最后指向foo的词法作用域-全局作用域
  • 最终输出全局变量a,值为1
    默认绑定总结: 在默认绑定(除去以下3种情况)时,实际上我们不用去管实际是如何指向全局作用域,只要是默认绑定情况,this指向的就是全局作用域

隐式绑定

能直接看得见的我们称之为显式,不能直接看见的我们称为隐式

function foo() {
    console.log(this.a)
}
var obj={
    a:2,
    foo:foo
}
obj.foo()//输出2

默认调用是通过函数名直接调用,那么通过对象调用函数,该函数中的this会指向该对象。那么要是是多个对象呢?

function foo() {
    console.log(this.a)
}
var obj={
    a:1,
    foo:foo
}
var obj2={
    a:2 ,
    obj:obj
}

obj2.obj.foo()//输出1

隐式丢失

当函数被多个对象链式调用时,this指向最近引用函数的对象

显式绑定

call

foo.call(obj,n1,n2...),调用call函数,第一个参数为需要指向的对象,后面放置该多个函数的传入实参

function foo(n,m){
    console.log(this.a,n,m)
}
var obj={
    a:2
}
//强行把foo函数的this指向改为后面括号中的第一个参数
//后面放置函数需要传入的参数
foo.call(obj,100,1000)//输出2 100 1000

apply

apply函数与call函数类似不同点是call函数传入实参是一个一个的,而apply传入实参,是放在一个数组当中

function foo(n,m){
    console.log(this.a,n,m)
}
var obj={
    a:2
}
//强行把foo函数的this指向改为后面括号中的第一个参数对象
//后面放置函数需要传入的参数的数组
foo.apply(obj,[200,2000])//输出 2 200 2000

bind

function foo(n,m){
    console.log(this.a,n,m,p,q)
}
var obj={
    a:2
}
//强行把强行把foo函数的this指向obj
//并可以传入函数所需的参数,也可以在返回函数里面传入,不够再到返回函数的传入参数里面找
var bar=foo.bind(obj,200)
bar(100,200)//输出2 200 100 200

易错

var a='外部'
function foo() {
    var a='内部'
    function bar (){
        console.log(this)
        console.log(this.a)
    }
   var baz= bar.bind(foo)
   baz()
}

foo() //输出undefined

this被强制指向了foo,函数是函数,函数虽然是一种特殊对象,但函数当中声明的变量并不是函数的属性,访问对象不存在的属性,会在对象中创新创建变量赋值为undefined

var a='外部'
function foo() {
    var a='内部'
    function bar (){
        console.log(this)
        console.log(this.a)
    }
   var baz= bar.bind(foo)
   baz()
}
foo.a=2
foo() //输出2

new绑定

当使用new方法创建新对象时,this所指的就是新生成的对象

new关键词的作用:

  1. 创建this对象
  2. 执行函数里面的逻辑为该对象增加属性
  3. 将隐式对象值赋予为构造函数的显式对象
  4. 返回这个this对象
//new关键词生成对象的作用过程
this={
    height:1400
    towner:user1
    color:red
    [[prototype]]:Car.prototype
}
return this

箭头函数

箭头函数没有this这个概念,写在箭头函数中的this也是它外层的函数的this 构造函数一定要用this关键字,所以箭头函数不能作为构造函数使用

请分析以下代码执行结果:

var obj={
    name:"TOM",
    show:function show (){
        var bar=()=>{
            console.log(this.name);
        }
        bar()
    }
    
}
obj.show()

分析

  • 当调用show方法时,执行内部的bar函数,执行bar内部console.log(this.name)语句
  • 但是bar是一个箭头函数,this属于外部函数的this
  • 于是this实际上是show函数的this回到了情况2隐式绑定,this指向对象obj
  • 打印TOM

总结

一共5种情况

  1. 默认绑定 this指向全局作用域
  2. 隐式绑定规则 ---当函数被一个对象所拥有,再调用时,这种情况下,在调用时,this会指向该对象
    隐式丢失 --- 当函数被多个对象链式调用时,this指向最近引用函数的对象
  3. 显式绑定 --- 3个函数:call ,apply,bind 5. new 绑定,this指向新生成的对象
  4. 写在箭头函数中的this是它外层的函数的this