关于this

在这里插入图片描述

参考阮一峰老师对于this的原理理解

一:this的定义

严格模式下非严格模式下this的在全局中会有差别

来自阮一峰老师对于this的由来的理解

由于函数可以在不同的运行环境中运行,所以需要一种机制,能够在函数的内部获取当前运行环境,因此this就出现来,this的设计目的就是为了在函数的内部,指代函数当前的运行环境

所以在绝大多数的情况下,函数当前的运行环境决定了this的值

例如:

   var obj = {
      foo: function () { console.log(this.bar) },
      bar: 1
    };

    var foo = obj.foo;
    var bar = 2;

    obj.foo() // 1
    foo() // 2
  • 对于obj.foo()来说,是通过obj这个对象来找到foo(),所以foo()的运行环境obj对象,因此this的值就是Obj对象
  • 对于foo()来说,foo()函数的运行环境是window,因此this的值就是window
  • image-20230911205503092

二:绑定规则

根据不同的使用场合,this有不同的值,主要分为下面几种情况:

  • 默认绑定
  • 隐式绑定
  • new绑定
  • 显示绑定

1. 默认绑定

默认绑定的意思就是,当函数独立执行,不作为一个对象的方法调用时,this绑定到全局对象中,但在严格模式下,this会绑定到undefined

    function foo ()
    {
      console.log(this); // 在浏览器中通常指向 window 对象
    }

    foo();

2. 隐式绑定

当函数作为对象的方法调用时,this 绑定到调用该方法的对象

    var obj = {
      foo: function () { console.log(this) },
      bar: 1
    };

    var foo = obj.foo;

    obj.foo() // 1

这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

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


    var foo = obj.foo;
    var bar = 2;

    obj.a.foo() 

foo执行的环境是a对象,所以this指向a对象

image-20230911210131323

再例如:

    var obj = {
      a: {
        bar: 1,
        foo: function ()
        {
          console.log(this.bar);	//2
          console.log(this)			//window
        },
      },

    };

    var bar = 2;

    const fn = obj.a.foo	
    fn()

  • 在该代码中,定义了一个变量fn,被将其赋值为obj.a.foo,这意味着fn现在引用了,obj对象中,a属性的foo方法。

  • 最后调用fn(),执行foo()函数

  • obj.a.foo复制给fn,只是将foo函数的引用复制给了fn,但并没有立即执行。所以fn只是函数的引用,它的上下文还是跟obj.a.foo相关

  • 但是当调用fn()时,这才是真正执行foo函数的时候,但由于fn是在全局上下文中调用的,JS将函数上下文this赋值为window

3. 显示绑定

使用 call()apply()bind() 方法显式地指定函数的 this 值。

    var obj = {
      foo: function () { console.log(this.bar) },
      bar: 1
    };

    var obj2 = {
      bar: 100
    }

    obj.foo.call(obj2)

image-20230911212217170

4. new 绑定

当函数用作构造函数(使用 new 关键字创建对象)时,this 绑定到新创建的对象。

    function fn ()
    {
      this.bar = 1
    }

    var obj = new fn()
    console.log(obj.bar);
  • 通过new关键字改变了this的执行,指向了obj

当函数返回一个对象

    function fn ()
    {
      this.bar = 1
      return {
        bar: 10
      }
    }

    var obj = new fn()
    console.log(obj.bar);
  • 当函数返回一个对象时,通过new关键字将this指向改变指向返回的对象,不指向obj
    • image-20230911212816249

当返回一些简单类型时候

    function fn ()
    {
      this.bar = 1
      return true
    }

    var obj = new fn()
    console.log(obj.bar);
  • this还是指向obj

返回null

    function fn ()
    {
      this.bar = 1
      return null
    }

    var obj = new fn()
    console.log(obj.bar);
  • 虽然null是object类型
  • 但是还是指向obj

三:箭头函数

JS中箭头函数与普通函数在this上有着重要的不同。

箭头函数this的绑定是在箭头函数创建的时候就确定的好的,是静态this绑定,它没有自己的上下文,它会捕获最近的普通函数的this

普通函数this值取决于,函数是如何被调用的,是根据调用方式动态确定的

在全局上下文中

    var a = 1
    const fn = () =>
    {
      console.log(this.a);
    }
    fn()

image-20230911214426329

  • fn箭头函数会自动捕获最近的最近的普通函数上下文,通常是全局对象window

在对象方法中

    var a = 10
    const obj = {
      a: 1,
      fn: () =>
      {
        console.log(this.a);
      }
    }
    obj.fn()

image-20230911214641724

  • fn箭头函数的this值不取决于被调用时动态绑定,而是在静态创建时候,与最近最近的普通函数上下文this值一致
  • fn箭头函数最近最近的普通函数上下文是window全局
  • 因此this指向window

作为事件回调

  <button id="btn">点击</button>

    const btn = document.getElementById('btn')
    var a = 10
    const obj = {
      a: 1,
      fn: () =>
      {
        console.log(this.a);		// 10
      }
    }
    btn.addEventListener('click', obj.fn)
  • 点击按钮输出还是10
  • 箭头函数作为回调函数时,其 this 绑定通常与定义它的上下文相同。

四:优先级

1. 隐式绑定 VS 显示绑定

function foo() {
    console.log( this.a );
}

var obj1 = {
    a: 2,
    foo: foo
};

var obj2 = {
    a: 3,
    foo: foo
};

obj1.foo(); // 2
obj2.foo(); // 3

obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2
  • 显示绑定优先级要高于隐式绑定

2. new绑定 VS 显示绑定

function Person(name) {
  this.name = name;
}

const alice = new Person("Alice");
const person = { name: "Bob" };

const boundGreet = greet.bind(person);
const aliceWithBinding = new boundGreet(); // 使用 new 绑定,this 绑定到新对象 aliceWithBinding
console.log(aliceWithBinding.name); // 输出: undefined,因为 new 绑定覆盖了显式绑定

  • new 绑定的优先级更高。当使用 new 关键字创建对象实例时,它会覆盖之前的显式绑定