首页 > web前端 > js教程 > 掌握 JavaScript 中的 &#this&# 关键字:不再回头

掌握 JavaScript 中的 &#this&# 关键字:不再回头

Susan Sarandon
发布: 2025-01-06 06:59:40
原创
1028 人浏览过

Mastering the

JavaScript 的 this 关键字是一个基本概念,经常让初学者和经验丰富的开发人员感到困惑。如果不彻底理解它的动态性质,可能会导致意想不到的行为。本综合指南旨在揭开这一点的神秘面纱,探索其各种背景、细微差别和最佳实践,并附有说明性示例和具有挑战性的问题,以巩固您的理解。

对此的介绍

在 JavaScript 中,this 是一个关键字,引用当前代码正在执行的对象。与其他一些静态绑定 this 的编程语言不同,JavaScript 的 this 是根据函数的调用方式动态确定的。

1. 全球背景

当不在任何函数内部时,this 指的是全局对象。

  • 在浏览器中:全局对象是window。
  • 在 Node.js 中: 全局对象是全局的。

示例

console.log(this === window); // true (in browser)
console.log(this === global); // true (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制

注意: 在严格模式下('use strict';),全局上下文中的 this 仍然是全局对象。

2. 函数上下文

一、常规功能

在常规函数中,这是由函数的调用方式决定的。

  • 默认绑定:如果在没有任何上下文的情况下调用函数,则 this 引用全局对象(或在严格模式下未定义)。

示例:

function showThis() {
  console.log(this);
}

showThis(); // Window object (in browser) or global (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制
  • 隐式绑定:当函数作为对象的方法被调用时,this 引用该对象。

示例

const person = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm Alice"
登录后复制
登录后复制
登录后复制
登录后复制

我们可以使用 callapplybind 显式设置它。

function greet() {
  console.log(`Hello, I'm ${this.name}`);
}

const person = { name: 'Bob' };
greet.call(person); // "Hello, I'm Bob"
登录后复制
登录后复制
登录后复制
登录后复制

二.箭头功能

箭头函数有一个词法 this,这意味着它们在创建时从周围的作用域继承了 this。

示例

const person = {
  name: 'Charlie',
  greet: () => {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm undefined" (or global name if defined)
登录后复制
登录后复制
登录后复制
登录后复制

说明:由于箭头函数没有自己的this,所以this指的是全局对象,而不是person对象。

箭头函数的正确用法:

const person = {
  name: 'Dana',
  greet: function() {
    const inner = () => {
      console.log(`Hello, I'm ${this.name}`);
    };
    inner();
  }
};

person.greet(); // "Hello, I'm Dana"
登录后复制
登录后复制
登录后复制
登录后复制

具有挑战性的方面:如果将方法分配给变量并调用,则可能会失去其预期的上下文。
示例

const calculator = {
  value: 0,
  add: function(num) {
    this.value += num;
    return this.value;
  }
};

console.log(calculator.add(5)); // 5
console.log(calculator.add(10)); // 15

const addFunction = calculator.add;
console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
登录后复制
登录后复制
登录后复制
登录后复制

3.构造函数和this

当函数使用 new 关键字作为构造函数时,this 指的是新创建的实例。

console.log(this === window); // true (in browser)
console.log(this === global); // true (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制

重要提示:
• 如果不使用new,this 可能引用全局对象或者在严格模式下未定义。
• 构造函数通常将第一个字母大写,以区别于常规函数。

4. 事件处理程序中的 this

在事件处理程序中,这是指接收事件的元素。

示例

function showThis() {
  console.log(this);
}

showThis(); // Window object (in browser) or global (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制

5. 使用call、apply和bind进行显式绑定

JavaScript 提供了显式设置 this 值的方法:

  • call: 调用函数,并将其设置为第一个参数,后跟函数参数。
const person = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm Alice"
登录后复制
登录后复制
登录后复制
登录后复制
  • apply: 与 call 类似,但接受数组形式的参数。
function greet() {
  console.log(`Hello, I'm ${this.name}`);
}

const person = { name: 'Bob' };
greet.call(person); // "Hello, I'm Bob"
登录后复制
登录后复制
登录后复制
登录后复制
  • bind: 返回一个新函数,并将 this 绑定到第一个参数。
const person = {
  name: 'Charlie',
  greet: () => {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm undefined" (or global name if defined)
登录后复制
登录后复制
登录后复制
登录后复制

用例:

  • 从其他对象借用方法。
  • 确保这在回调中保持一致。

6. 课堂上的 this

ES6 引入了类,它为构造函数和方法提供了更清晰的语法。在类方法中,this 指的是实例。
示例:

const person = {
  name: 'Dana',
  greet: function() {
    const inner = () => {
      console.log(`Hello, I'm ${this.name}`);
    };
    inner();
  }
};

person.greet(); // "Hello, I'm Dana"
登录后复制
登录后复制
登录后复制
登录后复制

类中的箭头函数:
箭头函数可用于从类上下文继承 this 的方法,这对于回调很有用。

const calculator = {
  value: 0,
  add: function(num) {
    this.value += num;
    return this.value;
  }
};

console.log(calculator.add(5)); // 5
console.log(calculator.add(10)); // 15

const addFunction = calculator.add;
console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
登录后复制
登录后复制
登录后复制
登录后复制

常见陷阱和最佳实践

I. 失去这个背景

将方法作为回调传递时,原始上下文可能会丢失。
问题

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

const alice = new Person('Alice');
console.log(alice.name); // "Alice"
登录后复制
登录后复制

解决方案
使用绑定来保留上下文。

<button>



<p><strong>Arrow Function Caveat:</strong><br>
Using arrow functions in event handlers can lead to this referring to the surrounding scope instead of the event target.<br>
<strong>Example:</strong><br>
</p>

<pre class="brush:php;toolbar:false">button.addEventListener('click', () => {
  console.log(this); // Global object or enclosing scope
});
登录后复制
登录后复制

二.错误使用箭头函数

箭头函数没有自己的 this,这在用作方法时可能会导致意外的行为。

问题

function greet(greeting) {
  console.log(`${greeting}, I'm ${this.name}`);
}

const person = { name: 'Eve' };
greet.call(person, 'Hello'); // "Hello, I'm Eve"
登录后复制
登录后复制

解决方案
使用常规函数作为对象方法。

greet.apply(person, ['Hi']); // "Hi, I'm Eve"
登录后复制
登录后复制

三.避免全局 this

无意中设置全局对象的属性可能会导致错误。

问题

const boundGreet = greet.bind(person);
boundGreet('Hey'); // "Hey, I'm Eve"
登录后复制
登录后复制

解决方案
使用严格模式或正确的绑定。

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

const dog = new Animal('Dog');
dog.speak(); // "Dog makes a noise."
登录后复制
登录后复制

先进理念

一、嵌套函数中的this

在嵌套函数中,this 可能不引用外部的 this。解决方案包括使用箭头函数或将其存储在变量中。
箭头函数示例:

class Person {
  constructor(name) {
    this.name = name;
  }

  greet = () => {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const john = new Person('John');
john.greet(); // "Hello, I'm John"
登录后复制
登录后复制

变量示例:

const obj = {
  name: 'Object',
  getName: function() {
    return this.name;
  }
};

const getName = obj.getName;
console.log(getName()); // undefined or global name
登录后复制
登录后复制

二.这与原型

使用原型时,这是指实例。

console.log(this === window); // true (in browser)
console.log(this === global); // true (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制

结论

JavaScript 中的 this 关键字是一个多功能且强大的功能,如果正确理解,可以极大地增强您的编码能力。


解决这个问题的 10 个棘手问题

为了真正巩固您对此的理解,请解决以下具有挑战性的问题。每个问题都旨在测试 JavaScript 中 this 关键字的不同方面和边缘情况。解决方案在最后。

问题一:神秘的输出

function showThis() {
  console.log(this);
}

showThis(); // Window object (in browser) or global (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制

问题 2:箭头函数惊喜

const person = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm Alice"
登录后复制
登录后复制
登录后复制
登录后复制

问题 3:在回调中绑定 this

function greet() {
  console.log(`Hello, I'm ${this.name}`);
}

const person = { name: 'Bob' };
greet.call(person); // "Hello, I'm Bob"
登录后复制
登录后复制
登录后复制
登录后复制

问题4:正确使用bind

const person = {
  name: 'Charlie',
  greet: () => {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm undefined" (or global name if defined)
登录后复制
登录后复制
登录后复制
登录后复制

问题5:构造函数中的this

const person = {
  name: 'Dana',
  greet: function() {
    const inner = () => {
      console.log(`Hello, I'm ${this.name}`);
    };
    inner();
  }
};

person.greet(); // "Hello, I'm Dana"
登录后复制
登录后复制
登录后复制
登录后复制

问题 6:事件处理程序上下文

const calculator = {
  value: 0,
  add: function(num) {
    this.value += num;
    return this.value;
  }
};

console.log(calculator.add(5)); // 5
console.log(calculator.add(10)); // 15

const addFunction = calculator.add;
console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
登录后复制
登录后复制
登录后复制
登录后复制

问题 8:Promise 中的 this

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

const alice = new Person('Alice');
console.log(alice.name); // "Alice"
登录后复制
登录后复制

问题 9:用 bind 链接

<button>



<p><strong>Arrow Function Caveat:</strong><br>
Using arrow functions in event handlers can lead to this referring to the surrounding scope instead of the event target.<br>
<strong>Example:</strong><br>
</p>

<pre class="brush:php;toolbar:false">button.addEventListener('click', () => {
  console.log(this); // Global object or enclosing scope
});
登录后复制
登录后复制

问题 10:类和继承

function greet(greeting) {
  console.log(`${greeting}, I'm ${this.name}`);
}

const person = { name: 'Eve' };
greet.call(person, 'Hello'); // "Hello, I'm Eve"
登录后复制
登录后复制

棘手问题的解决方案

问题1的解决方案:

当 getName 被分配给一个变量并在没有任何对象上下文的情况下调用时,这默认为全局对象。在非严格模式下,this.name指的是全局名称,即'Global'。在严格模式下,这将是未定义的,从而导致错误。

greet.apply(person, ['Hi']); // "Hi, I'm Eve"
登录后复制
登录后复制

问题2的解决方案:

箭头函数没有自己的this;他们从周围的范围继承它。在这种情况下,周围的范围是全局上下文,其中 this.name 是 'Global'。

const boundGreet = greet.bind(person);
boundGreet('Hey'); // "Hey, I'm Eve"
登录后复制
登录后复制

问题3的解决方案:

在 setInterval 回调中,this 指的是全局对象(或者在严格模式下未定义)。因此, this.seconds 要么增加 window.seconds 要么在严格模式下抛出错误。 timer.seconds 仍为 0。

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

const dog = new Animal('Dog');
dog.speak(); // "Dog makes a noise."
登录后复制
登录后复制

问题4的解决方案:

将retrieveX绑定到模块后,调用boundGetX()可以正确地将其设置为模块。

class Person {
  constructor(name) {
    this.name = name;
  }

  greet = () => {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const john = new Person('John');
john.greet(); // "Hello, I'm John"
登录后复制
登录后复制

问题5的解决方案:

箭头函数 getModel 从构造函数继承 this,它引用新创建的汽车实例。

const obj = {
  name: 'Object',
  getName: function() {
    return this.name;
  }
};

const getName = obj.getName;
console.log(getName()); // undefined or global name
登录后复制
登录后复制

问题6的解决方案:

在使用常规函数的事件处理程序中,this 指的是接收事件的 DOM 元素,即按钮。由于按钮没有 name 属性,因此 this.name 未定义。

console.log(this === window); // true (in browser)
console.log(this === global); // true (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制

问题7的解决方案:

  • 第一个console.log(this.name);里面的outerFunc引用了obj,所以它打印'Outer'。
  • 第二个console.log(this.name);内部innerFunc引用全局对象,因此它在严格模式下打印'Global'或undefined。
function showThis() {
  console.log(this);
}

showThis(); // Window object (in browser) or global (in Node.js)
登录后复制
登录后复制
登录后复制
登录后复制

问题8的解决方案:

在 Promise 构造函数中,this 指的是全局对象(或者在严格模式下未定义)。因此, this.value 是未定义的(或在严格模式下导致错误)。

const person = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm Alice"
登录后复制
登录后复制
登录后复制
登录后复制

问题9的解决方案:

乘法函数与第一个参数 a 绑定为 2。当调用 double(5) 时,它有效地计算乘法(2, 5)。

function greet() {
  console.log(`Hello, I'm ${this.name}`);
}

const person = { name: 'Bob' };
greet.call(person); // "Hello, I'm Bob"
登录后复制
登录后复制
登录后复制
登录后复制

问题10的解决方案:

在Dog类的speak方法中,setTimeout回调是一个常规函数。因此,回调中的 this 指的是全局对象,而不是狗实例。 this.name 为“未定义”,或者如果未全局定义名称,则会导致错误。

const person = {
  name: 'Charlie',
  greet: () => {
    console.log(`Hello, I'm ${this.name}`);
  }
};

person.greet(); // "Hello, I'm undefined" (or global name if defined)
登录后复制
登录后复制
登录后复制
登录后复制

要解决此问题,请使用箭头函数:

const person = {
  name: 'Dana',
  greet: function() {
    const inner = () => {
      console.log(`Hello, I'm ${this.name}`);
    };
    inner();
  }
};

person.greet(); // "Hello, I'm Dana"
登录后复制
登录后复制
登录后复制
登录后复制

现在,它正确记录了:

const calculator = {
  value: 0,
  add: function(num) {
    this.value += num;
    return this.value;
  }
};

console.log(calculator.add(5)); // 5
console.log(calculator.add(10)); // 15

const addFunction = calculator.add;
console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
登录后复制
登录后复制
登录后复制
登录后复制

以上是掌握 JavaScript 中的 &#this&# 关键字:不再回头的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板