关于this的一切

参考文档:MDN-this

JS中的this是什么?

预计阅读时间: 5 分钟

在JavaScript中,this 是一个 关键字,它指向 当前执行上下文 中的对象。具体来说,this 的值取决于当前代码的执行环境:

  • 全局执行上下文中指向全局对象
  • 函数执行上下文中调用点决定
"调用点的解释

调用点,亦即函数的调用方式,如直接调用,作为对象的方法调用,call/bind/apply调用,构造函数new

JS中this的绑定规则

1. 默认绑定,直接调用

指的是直接调用函数,或者在全局作用域直接使用属性 this.xxx

  • 严格模式下,this默认绑定undefined (类中 默认开启严格模式)
  • 非严格模式下,this默认绑定globalThis ,浏览器是 window对象
WARNING

默认绑定上层作用域的this不是一回事,

默认绑定

是指直接调用函数时,没有显式绑定this的情况下,使用的this

2. 隐式绑定

"隐式绑定

函数(非箭头函数)被当作对象的一个方法来调用

main.js
1// 明显的对象属性
2 const obj = { 
3     name:'obj',
4     foo(){
5          console.log(this.name)
6       }
7 }
8 obj.foo() // this指向obj
9
10//数组也是一个对象 
11 function foo(){ 
12   console.log(this.length)
13 }
14 const arr = [foo,1,2]
15 arr[0]() // 这也会把this绑定到arr 即结果是 3

3.显式绑定

"显式绑定

就是分别使用 bind、apply、call方式,显式绑定this

  • bind 函数只绑定,不执行,参数在绑定的时候可以传入,在调用的时候不能覆盖,会在后面添加新的参数
  • call调用即执行,参数是单独传入
  • apply调用即执行,参数通过数组传入
main.js
1//三者调用
2const obj ={ name:'this'}
3var func = function(arg1, arg2) {};
4// 直接调用了
5func.call(obj, arg1, arg2); // 单独传入
6func.apply(obj, [arg1, arg2]);  //参数是数组
7
8//返回一个函数
9nfn = func.bind(obj,arg1) // 绑定的时候传入
10nfn(arg2) // 调用的时候可以额外传入

4. New 绑定

  1. 通过new 内部创建一个新的对象,绑定到this
"super调用会比较特殊

super是一个关键词,特殊语法当一个函数以 super.method() 的形式被调用时,method 函数内的 thissuper.method() 调用周围的 this 值相同,通常不等于 super 所指向的对象。这是因为 super.method 不是像上面的对象成员访问——它是一种特殊的语法,有不同的绑定规则。有关示例,请参见 super 参考

5.绑定优先级

对于每一种绑定方式,都有自己的优先级,优先级高的会覆盖优先级低的

"绑定优先级

new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

对于显式绑定

"显式绑定优先级

bind > apply == call

main.js
1function Foo(){    
2  console.log('this', this)
3}
4const objA ={ 
5  name:'objA',
6}
7const objB ={ 
8  name:'objB',
9}
10
11const fn = Foo.bind(objA)
12fn.call(objB) // this { name: 'objA' }
13new fn() // this Foo {}
14
15const objC ={ 
16  name:'objC',
17  foo:fn
18}
19
20new objC.foo() // this Foo {}
"显式绑定特殊情况
  1. 非严格模式下:
  • apply/call/bind绑定null/undefied时,这个绑定会被忽略,使用默认绑定(并不是降级,不会使用隐式绑定);
  • 基本类型会被转换成包装类型(对象)
  1. 严格模式下,会直接使用绑定的类型,默认绑定是undefined

6.箭头函数

"箭头函数this

箭头函数里没有绑定自己的this,如果在箭头函数使用了this,调用的时候会到上层作用域中查找this,就像普通变量查找一样的规则

main.js
1const obj = {
2  name:'obj',
3  foo: function() {
4    const bar = ()=>{
5      console.log('this', this)
6    }
7    return bar
8  }
9}
10// 这里bar的this是上层作用域foo的this,如果改变foo的this指向,bar中的this也会跟着改变
11
12const obj = {
13  name:'obj',
14  foo: ()=> {
15    console.log('this foo', this)  // 全局作用域的this
16    const bar = ()=>{
17      console.log('this bar', this)  // 全局作用域的this
18    }
19    return bar
20  }
21}
22//如果把foo也改成了箭头函数,那么foo和bar的this指向都要继续往外查找,是全局作用域的this
23// 注意!! 对象不是代码块,obj没有产生作用域,所以!!
24
25// 函数的调用方式:这里也是直接调用!!! 因为obj.foo()函数执行后,返回值是一个新的函数的地址!!!
26obj.foo()() 
27// 也是通过小括号直接调用了
"对象不是代码块,没有产生作用域!!

7. 手写call、apply、bind实现

"手写思路

基本思路,使用隐式绑定,给传入的thisArg转换成一个对象,给新的对象添加一个属性fn,值为this,

this即为调用者,也就是调用的函数,然后使用传入的thisArg去调用原来的函数

main.js
1Function.prototype.myCall = function (thisArg, ...args) {
2    thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
3    Object.defineProperty(thisArg, 'fn', {
4        configurable: true,
5        enumerable: false,
6        value: this
7      })
8     //
9    thisArg.fn(...args)
10    delete thisArg.fn
11}

总结

this 的设计是为了让函数能够根据调用者灵活地执行操作。

箭头函数:

  • 没有自己的this,会到上层作用域中查找this

普通函数:

  • 默认绑定:如果函数作为普通函数调用,this 指向全局对象(非严格模式)或 undefined(严格模式)。
  • 隐式绑定:如果函数作为对象的方法调用,this 指向该对象。
  • 显式绑定:如果函数通过 callapplybind 调用,this 指向显式传入的对象。
  • new绑定:如果函数通过 new 调用,this 指向新创建的对象。