老生常谈但是却有点迷的this指向问题

一. 首先明确,this指向是在函数运行时才确定的

二. 误解

  • this 指向函数自身(错误)
       function foo(num) {
          console.log('foo', num);
          this.count ++
        }
        foo.count = 0 // 初始化函数的count属性值是0
    
        for(let i = 0; i < 5; i ++) {
          foo(i)
        }
    
        console.log(foo.count);  //count最终还是0 
    复制代码

    上面代码中,定义了foo函数,通过this每次执行foo就把this的count加1,但是,循环执行了五次之后,foo.count还是等于0,说明了,this不指向foo函数

  • this指向函数的作用域(错误)
        function foo() {
          var a = 2
          this.bar()
        }
    
        function bar() {
          console.log(this.a);
        }
    
        foo()  // undefined
    复制代码

    上面代码想通过this指向foo函数的作用域,但是a打印出undefined,可见,this不是指向函数的作用域

三. this的指向

再次强调,this指向只有在函数运行时才能够确定下来

1. 默认绑定

函数不加任何修饰,直接调用时,如果非严格模式,默认绑定规则this绑定到全局对象

    var a = 1
    function f() {
        console.log(this.a)
    }
    f() // 在非严格模式下1, 严格模式下undefined
复制代码

2. 隐式绑定

默认绑定是对象中有函数属性,当使用对象调用函数时,函数中的this指向调用的对象

let obj = {
  a: 123, 
  foo(){
    console.log(this.a);
  }
}
obj.foo()
复制代码

当连续调用对象时,this指向直接调用的对象

let obj = {
  a: 123, 
  foo(){
    console.log(this.a);
  }
}
let obj2 = {
  a: 2,
  obj
}
obj2.obj.foo()  // 123
复制代码
绑定丢失

函数名只是函数的一个引用,所以,下面代码中的newF实际上指向了obj.foo的指针,所以newF实际是指向foo的指针,也就是说现在调用newF,就是不带任何修饰的函数调用了,采用默认绑定。

let obj = {
  a: 123, 
  foo(){
    console.log(this.a);
  }
}

let newF = obj.foo
newF()  // undefined
复制代码

3. 显式绑定

显示绑定时借助call,apply,bind来显示的指定this指向,一旦指向之后,就不再改变

let obj = {
  a: 123, 
  foo(){
    console.log(this.a);
  }
}

let o = { 
  a: 22
}

obj.foo.call(o) // this被强制指向o
复制代码

4. new 绑定

new操作会经过以下步骤

  1. 创建一个新的对象,把函数的this指向这个新对象
  2. 将对象的原型指向构造函数的原型
  3. 如果没有显式的返回对象 返回这个新创建的对象,如果是,则返回指定对象

所以通过new调用的函数,其中的this指向new返回的对象

以上所有方法都是介绍普通函数的this绑定规则对于箭头函数,则有所不同

箭头函数中的this指向

箭头函数中this指向箭头函数定义时的作用域(全局作用域或者函数作用域,而不是对象块)中的this

function foo() {
  return () => {
    console.log(this.a);
  }
}
let obj1 = {
  a:1
}
let obj2 = {
  a: 2
}
let bar = foo.call(obj1)
bar.call(obj2)  // 1
// 这里函数中返回的箭头函数借用了它定义时所在的作用域(foo)中的this
// 因为foo中的this被硬绑定到了obj1,所以 返回的箭头函数中的this也指向obj1,不能通过call修改
复制代码

箭头函数可以代替bind保持函数中的this到特定对象,箭头函数使用了类似如下写法

function foo() {
  var self = this
  setTimeout(function() {
    console.log(self); // 暂存变量保存,函数定义时作用域中的this
  }, 1000);
}
复制代码

一道面试题,看this

var msg1 = 1;
const msg2 = 2;
const obj = {
    msg1: 3,
    msg2: 4,
    log1: function() {
        console.log(this.msg1, this.msg2)
    },
    log2: () => {
        console.log(this.msg1, this.msg2)
    }
};
const {
    log1,
    log2
} = obj;

obj.log1();  // 3, 4
//  隐式绑定规则,this指向调用的对象
obj.log2();  // 1, undefined
// 箭头函数中的this指向定义时的作用域,即全局作用域,所以this指向全局,
// 注意,箭头函数定义是的作用域,不是obj,obj不是一个作用域,作用域只有全局作用域和函数作用域
//和块级作用域,obj只是一个引用变量,不是一个作用域,
//所以this指向的是window,const和let定义的变//量不挂载载window上,所以访问是undefined
log1();   // 1, undefined
// 解构赋值,导致隐式绑定丢失,相当于直接调用
log2();   // 1, undefined
// 箭头函数仍然指向全局
复制代码

参考文献:《你不知道的javascript上卷》

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享