关于this

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

一:this的定义

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

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

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

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

例如:

js 复制代码
   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

二:绑定规则

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

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

1. 默认绑定

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

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

    foo();

2. 隐式绑定

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

js 复制代码
    var obj = {
      foo: function () { console.log(this) },
      bar: 1
    };

    var foo = obj.foo;

    obj.foo() // 1

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

js 复制代码
    var obj = {
      a: {
        foo: function () { console.log(this) },
      },
      bar: 1
    };


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

    obj.a.foo() 

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

再例如:

js 复制代码
    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 值。

js 复制代码
    var obj = {
      foo: function () { console.log(this.bar) },
      bar: 1
    };

    var obj2 = {
      bar: 100
    }

    obj.foo.call(obj2)

4. new 绑定

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

js 复制代码
    function fn ()
    {
      this.bar = 1
    }

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

当函数返回一个对象

js 复制代码
    function fn ()
    {
      this.bar = 1
      return {
        bar: 10
      }
    }

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

当返回一些简单类型时候

js 复制代码
    function fn ()
    {
      this.bar = 1
      return true
    }

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

返回null

js 复制代码
    function fn ()
    {
      this.bar = 1
      return null
    }

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

三:箭头函数

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

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

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

在全局上下文中

js 复制代码
    var a = 1
    const fn = () =>
    {
      console.log(this.a);
    }
    fn()
  • fn箭头函数会自动捕获最近的最近的普通函数上下文,通常是全局对象window

在对象方法中

js 复制代码
    var a = 10
    const obj = {
      a: 1,
      fn: () =>
      {
        console.log(this.a);
      }
    }
    obj.fn()
  • fn箭头函数的this值不取决于被调用时动态绑定,而是在静态创建时候,与最近最近的普通函数上下文this值一致
  • fn箭头函数最近最近的普通函数上下文是window全局
  • 因此this指向window

作为事件回调

html 复制代码
  <button id="btn">点击</button>
js 复制代码
    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 显示绑定

js 复制代码
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 显示绑定

js 复制代码
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 关键字创建对象实例时,它会覆盖之前的显式绑定
相关推荐
腾讯TNTWeb前端团队3 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰6 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy7 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom8 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom8 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom8 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom8 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom8 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试