最终理解 JavaScript 中“This”的高级用法

2025-06-08

最终理解 JavaScript 中“This”的高级用法

在这个简短的教程中,我们将通过 7 个不同的示例学习如何在 Javascript 中使用“this”……


如果函数定义为箭头函数:{: #arrow-functions }

const arrowFunction = () => {
  console.log(this);
};
Enter fullscreen mode Exit fullscreen mode

在这种情况下,的值this始终与父范围中相同:this

const outerThis = this;

const arrowFunction = () => {
  // Always logs `true`:
  console.log(this === outerThis);
};
Enter fullscreen mode Exit fullscreen mode

箭头函数很棒,因为的内部值this无法改变,它始终
与外部的值相同this

其他示例

使用箭头函数时,的值this 不能通过来改变bind

// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
Enter fullscreen mode Exit fullscreen mode

使用箭头函数时, 的值this 不能通过callapply来改变

// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});
Enter fullscreen mode Exit fullscreen mode

this 使用箭头函数时,不能通过将函数作为另一个对象的成员来调用来更改的

const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
Enter fullscreen mode Exit fullscreen mode

this 使用箭头函数时,不能通过将函数作为构造函数调用来更改的

// TypeError: arrowFunction is not a constructor
new arrowFunction();
Enter fullscreen mode Exit fullscreen mode

“绑定”实例方法

对于实例方法,如果要确保this始终引用类实例,最好的
方法是使用箭头函数和
字段

class Whatever {
  someMethod = () => {
    // Always the instance of Whatever:
    console.log(this);
  };
}
Enter fullscreen mode Exit fullscreen mode

当在组件(例如
React 组件或 Web 组件)中使用实例方法作为事件监听器时,此模式非常有用。

上述内容可能感觉像是违反了“与父范围内this相同”规则,但如果您将类字段视为 在构造函数中 设置事物的语法糖,它就开始有意义了:this


class Whatever {
  someMethod = (() => {
    const outerThis = this;
    return () => {
      // Always logs `true`:
      console.log(this === outerThis);
    };
  })();
}

// …is roughly equivalent to:

class Whatever {
  constructor() {
    const outerThis = this;
    this.someMethod = () => {
      // Always logs `true`:
      console.log(this === outerThis);
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

替代模式包括在构造函数中绑定现有函数,或
在构造函数中分配函数。如果由于某些原因无法使用类字段,则在
构造函数中分配函数是一种合理的选择:

class Whatever {
  constructor() {
    this.someMethod = () => {
      // …
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

否则,如果使用以下方式调用函数/类new:{: #new }

new Whatever();
Enter fullscreen mode Exit fullscreen mode

上述代码将调用Whatever(如果它是一个类,则调用它的构造函数)并将 结果this设置为
Object.create(Whatever.prototype)

class MyClass {
  constructor() {
    console.log(
      this.constructor === Object.create(MyClass.prototype).constructor,
    );
  }
}

// Logs `true`:
new MyClass();
Enter fullscreen mode Exit fullscreen mode

对于旧式的构造函数也是如此:

function MyClass() {
  console.log(
    this.constructor === Object.create(MyClass.prototype).constructor,
  );
}

// Logs `true`:
new MyClass();
Enter fullscreen mode Exit fullscreen mode

其他示例

当使用 调用时new, 的值this 不能用 更改bind

const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
Enter fullscreen mode Exit fullscreen mode

当使用 调用时new, 的值this 不能通过将函数作为
另一个对象的成员调用来更改:

const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();
Enter fullscreen mode Exit fullscreen mode

否则,如果函数具有“绑定”this值:{: #bound }

function someFunction() {
  return this;
}

const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);
Enter fullscreen mode Exit fullscreen mode

无论何时boundFunction调用,其值将是传递给 ( )的this对象bind
boundObject

// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
Enter fullscreen mode Exit fullscreen mode

避免使用bind将函数绑定到其外部函数this。相反,使用箭头函数,因为它们this从函数声明中就明确了,而不是
在代码后面发生的事情。

不要使用bindset 来设置this与父对象无关的值;这通常是意料之外的,也是它this名声不好的原因。考虑将值作为参数传递;它更明确,并且适​​用于箭头函数。

其他示例

this 调用绑定函数时,不能使用call
apply
更改的值

// Logs `true` - called `this` value is ignored:
console.log(boundFunction.call({foo: 'bar'}) === boundObject);
// Logs `true` - applied `this` value is ignored:
console.log(boundFunction.apply({foo: 'bar'}) === boundObject);
Enter fullscreen mode Exit fullscreen mode

this 当调用绑定函数时,不能通过将该函数作为
另一个对象的成员调用来更改的值:

const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
Enter fullscreen mode Exit fullscreen mode

否则,如果this在调用时设置:{: #call-apply }

function someFunction() {
  return this;
}

const someObject = {hello: 'world'};

// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);
Enter fullscreen mode Exit fullscreen mode

的值是传递给/ 的this对象callapply

不要使用call/apply来设置this与父对象无关的值;这通常是意料之外的,也是它this名声不好的原因。考虑将值作为参数传递;这样更明确,并且适​​用于箭头函数。

不幸的是this,它被 DOM 事件监听器之类的东西设置为其他值,使用它可能会
导致难以理解的代码:

element.addEventListener('click', function (event) {
  // Logs `element`, since the DOM spec sets `this` to
  // the element the handler is attached to.
  console.log(this);
});
Enter fullscreen mode Exit fullscreen mode

我避免this在上述情况下使用,而是:

element.addEventListener('click', (event) => {
  // Ideally, grab it from a parent scope:
  console.log(element);
  // But if you can't do that, get it from the event object:
  console.log(event.currentTarget);
});
Enter fullscreen mode Exit fullscreen mode

否则,如果通过父对象调用该函数(parent.func()):{: #object-member }

const obj = {
  someMethod() {
    return this;
  },
};

// Logs `true`:
console.log(obj.someMethod() === obj);
Enter fullscreen mode Exit fullscreen mode

在这种情况下,该函数作为 的成员函数被调用obj,因此this将是obj。这发生在调用时,因此,如果在没有其父对象的情况下调用该函数,或者使用 不同的父对象,
则链接会断开:

const {someMethod} = obj;
// Logs `false`:
console.log(someMethod() === obj);

const anotherObj = {someMethod};
// Logs `false`:
console.log(anotherObj.someMethod() === obj);
// Logs `true`:
console.log(anotherObj.someMethod() === anotherObj);
Enter fullscreen mode Exit fullscreen mode

someMethod() === obj为 false,因为someMethod 不是作为 的成员调用的obj。你可能
在尝试如下操作时遇到过这个问题:

const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
Enter fullscreen mode Exit fullscreen mode

这会中断,因为 的实现querySelector会查看其自身的this值,并期望
它是一个 DOM 节点,而上面的代码破坏了这种联系。正确实现上述代码的方法如下:

const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
Enter fullscreen mode Exit fullscreen mode

有趣的事实:并非所有 API 都this在内部使用。控制台方法(例如)console.log已更改为
避免this引用,因此log无需绑定到console

不要仅仅为了设置this与父对象无关的值而将函数移植到对象上;这通常是意料之外的,也是它this名声不佳的原因。考虑将值作为参数传递;它更明确,并且适​​用于箭头函数。

否则,如果函数或父范围处于严格模式:{: #strict }

function someFunction() {
  'use strict';
  return this;
}

// Logs `true`:
console.log(someFunction() === undefined);
Enter fullscreen mode Exit fullscreen mode

在这种情况下,的值为this未定义。如果父范围处于严格 模式(并且所有模块都处于严格模式),则'use strict'函数中不需要。

别太依赖这个。我的意思是,有更简单的方法可以获取undefined值😀。

否则:{: #otherwise }

function someFunction() {
  return this;
}

// Logs `true`:
console.log(someFunction() === globalThis);
Enter fullscreen mode Exit fullscreen mode

在这种情况下, 的值this与 相同globalThis

大多数人(包括我)都称之为globalThis全局对象,但这在技术上并非 100% 正确。Mathias Bynens 对此进行了详细说明(https://mathiasbynens.be/notes/globalthis#terminology),包括为什么这样称呼它,globalThis而不是简单地这样称呼global

避免使用this来引用全局对象(是的,我仍然这么称呼它)。相反,使用globalThis
这样更明确。


学习 Javascript 的其他资源:

JavaScript 挑战

获取书籍: 《JavaScript 挑战》


JavaScript:理解奇怪的部分

Monster JavaScript 课程 - 50 多个项目和应用程序

参考网站:https://web.dev/javascript-this/

鏂囩珷鏉yu簮锛�https://dev.to/bricourse/finally-understanding-the-advanced-uses-of-this-in-javascript-40g8
PREV
不要成为模型开发人员
NEXT
最终像专业人士一样理解 JavaScript 闭包