"this"关键字是如何工作的,以及何时应该使用它?

1489

我希望找到一个清晰的解释,“this”关键字的作用是什么,以及如何正确使用它。

它似乎表现得很奇怪,我不完全明白为什么。

this如何工作,应该在什么情况下使用?


22个回答

9

以下是关于this的最详细和全面的文章:

JavaScript中“this”关键字的简要解释

this的概念在于理解函数调用类型对设置this值的重要性。


当你无法确定this时,不要问自己:

this从哪里获取?

而应该问自己:

这个函数是如何被调用的?

对于箭头函数(上下文透明的特殊情况),请问自己:

在定义箭头函数的地方,this的值是什么?

这种思维方式在处理this时是正确的,可以帮助你避免头痛。


除了链接到您的博客,也许您可以深入探讨一下,如何通过询问那些问题来帮助别人理解“this”关键字? - Magnus Lind Oxlund

7

这是我见过最好的解释:深入理解 JavaScript 的 this

this 引用始终指向一个对象 - 一个单一的对象,通常在函数或方法中使用,但也可以在全局范围外使用。请注意,在使用严格模式时,在全局函数和未绑定到任何对象的匿名函数中,此时 this 将保持 undefined 的值。

以下是四种容易引起混淆的情况:

  1. 当我们将使用 this 的方法作为参数传递给回调函数时。
  2. 当我们使用内部函数(闭包)时。需要注意的是,闭包不能使用 this 关键字访问外部函数的 this 变量,因为 this 变量只能由函数本身访问,不能被内部函数访问。
  3. 当依赖于 this 的方法被分配给跨上下文的变量时,此时 this 引用的对象会与最初预期的对象不同。
  4. 在使用 bind、apply 和 call 方法时使用 this

作者提供了代码示例、解释和解决方案,我认为非常有帮助。


6

在伪经典的术语中,许多讲座教授“this”关键字的方式是将其视为由类或对象构造函数实例化的对象。每次从类构造新对象时,可以想象在幕后创建并返回一个本地实例化的“this”对象。我记得它是这样教的:

function Car(make, model, year) {
var this = {}; // under the hood, so to speak
this.make = make;
this.model = model;
this.year = year;
return this; // under the hood
}

var mycar = new Car('Eagle', 'Talon TSi', 1993);
// ========= under the hood
var this = {};
this.make = 'Eagle';
this.model = 'Talon TSi';
this.year = 1993;
return this;

6

this是JavaScript中一个常被误解的概念,因为它在不同的地方有些微不同的行为。简单来说,this指的是我们当前执行的函数的“拥有者”

this帮助我们获取当前对象(也称为执行上下文),以便我们能够理解当前this所指向的是哪个对象。

var val = "window.val"

var obj = {
    val: "obj.val",
    innerMethod: function () {
        var val = "obj.val.inner",
            func = function () {
                var self = this;
                return self.val;
            };

        return func;
    },
    outerMethod: function(){
        return this.val;
    }
};

//This actually gets executed inside window object 
console.log(obj.innerMethod()()); //returns window.val

//Breakdown in to 2 lines explains this in detail
var _inn = obj.innerMethod();
console.log(_inn()); //returns window.val

console.log(obj.outerMethod()); //returns obj.val

在上面的代码中,我们创建了三个名称为“val”的变量。一个在全局上下文中,一个在obj内部,另一个在obj的innerMethod内部。JavaScript通过从本地到全局的范围链解析特定上下文中的标识符。
一些可以区分this的地方

调用对象的方法

var status = 1;
var helper = {
    status : 2,
    getStatus: function () {
        return this.status;
    }
};

var theStatus1 = helper.getStatus(); //line1
console.log(theStatus1); //2

var theStatus2 = helper.getStatus;
console.log(theStatus2()); //1

当执行line1时,JavaScript为函数调用建立了一个执行上下文(EC),将this设置为点号前面所引用的对象。因此,在最后一行中,您可以理解a()是在全局上下文中执行的,即window

使用构造器

this可用于引用正在创建的对象。
function Person(name){
    this.personName = name;
    this.sayHello = function(){
        return "Hello " + this.personName;
    }
}

var person1 = new Person('Scott');
console.log(person1.sayHello()); //Hello Scott

var person2 = new Person('Hugh');
var sayHelloP2 = person2.sayHello;
console.log(sayHelloP2()); //Hello undefined

当执行新的Person()时,会创建一个全新的对象。调用Person并将其this设置为引用该新对象。

函数调用

function testFunc() {
    this.name = "Name";
    this.myCustomAttribute = "Custom Attribute";
    return this;
}

var whatIsThis = testFunc();
console.log(whatIsThis); //window

var whatIsThis2 = new testFunc();
console.log(whatIsThis2);  //testFunc() / object

console.log(window.myCustomAttribute); //Custom Attribute 

如果我们忘记使用new关键字,whatIsThis会引用它能找到的最全局的上下文(window)

在事件处理程序中

如果事件处理程序是内联的,this指向全局对象。

<script type="application/javascript">
    function click_handler() {
        alert(this); // alerts the window object
    }
</script>

<button id='thebutton' onclick='click_handler()'>Click me!</button>

通过JavaScript添加事件处理程序时,this指的是生成事件的DOM元素。



6

"this"的值取决于函数执行的上下文。上下文可以是任何对象或全局对象,即window。

因此,“this”的语义与传统的面向对象编程语言不同。这会导致问题: 1. 当一个函数被传递给另一个变量(很可能是回调)时;2. 当闭包从类的成员方法中调用时。

在这两种情况下,this被设置为window。


4

这个链接(http://www.quirksmode.org/js/this.html)有帮助吗?(JavaScript中“this”的大多数混淆都来自于它通常不与您的对象关联,而是与当前执行范围相关--虽然可能并不完全是这样,但对我来说总是感觉是这样--请参阅文章以获取完整解释)


2
最好说它与“当前执行上下文”链接。除了ES6(草案)通过箭头函数改变了这一点,其中this解析为外部执行上下文。 - RobG

3

关于this关键字的一些信息

让我们在全局范围内将this关键字记录到控制台,不需要任何其他代码但是

console.log(this)

在客户端/浏览器中,this关键字是一个全局对象,即window
console.log(this === window) // true

服务器/Node/Javascript 运行时 中,this 关键字也是一个全局对象,即 module.exports

console.log(this === module.exports) // true
console.log(this === exports) // true

请记住,exports只是对module.exports的引用。


3
JavaScript中的"this" 这是执行上下文的属性之一。 在此输入图片描述
  • 每次执行函数时才创建此属性,而不是在此之前。
  • 它的值不是静态的,而是取决于如何使用它。
  • 它采用一个值,该值指向使用它的函数的所有者

有不同的方式可以使用"this"关键字,以下是其示例(方法、常规函数、箭头函数、事件监听器、显式函数绑定)。

1. 在方法内部。

this ===(调用该方法的对象)。

在此输入图片描述 在上面的示例中,方法“fullName()”由一个对象“person”调用,因此方法“fullName()”内部的this值将等于“person”对象。

2. 在函数内部。

i) 函数声明/表达式

在松散模式下,this === window (对象) enter image description here

在严格模式下,this === undefined enter image description here

注意:使用函数声明或函数表达式方法定义函数时,此属性的工作方式相同。

ii) 箭头函数:

箭头函数没有自己的 this 属性,它们将 this 的值作为其周围的函数。 如果周围的函数不存在,即它们在全局级别定义,则 this === window (对象)

enter image description here

3. 事件监听器 this === 附加处理程序的对象。 click事件绑定到Document对象。

enter image description here

在上面的例子中,由于点击处理程序附加到“document”对象,因此这将等于“document”对象。
4. 显式函数绑定(call、apply、bind)
call() 和 apply() 方法是预定义的 JavaScript 方法。
它们都可以用于使用另一个对象作为参数调用对象方法。

enter image description here

在上面的例子中,“printFullDetails()”内部通过将其作为调用方法的第一个参数传递来明确设置为personObj1和personObj2。
你可以在这里了解更多关于call、apply和bind方法的信息。

这里的“被接受的答案”是正确的、最新的和完整的。代码示例不应该以截图的形式出现,而应该是可复制的代码块(也适用于您之前的回答)。“每次函数执行时都会创建此属性,而不是在此之前”是不正确的:它忽略了类和全局执行上下文。“取决于它的使用方式”相当模糊。“函数的所有者”在JS中并不是一个语言结构。对象不能“调用”方法。方法可以在对象上被调用(或“关闭”)。“正常模式”比严格模式更不“正常”... - Sebastian Simon
通常应该提到 globalThis 而不是 window。 “箭头函数没有自己的 this 属性” 是含糊的。 this 不是对象(ES 语言值)的属性,而是环境记录(ES 规范值)上的内部插槽。箭头函数不会 绑定 this。 如果它们在全局级别定义,则相同的严格与松散模式规则适用。事件监听器实际上并不特殊;addEventListener 使用 currentTarget 属性将侦听器函数调用为 this 绑定的接收事件。 - Sebastian Simon
有几个API绑定了一些this值,但是你可以创建自己的API来实现这一点。 “在上面的例子中”?该示例在下面。 - Sebastian Simon
@SebastianSimon,我非常尊重您花时间阅读我的回答。 我真的很感激您提供最新的答案。 但是我认为对于初学者来说不太有用,因为它太冗长了,而且如果我想要详细的概念,我会去官方的MDN文档。 我尽可能地简短明了地回答了问题。 所提供的屏幕截图仅供快速查看,在将来我会尝试放上代码。 - Arham Chowdhury

3
我对这个问题的看法与其他答案不同,希望对您有所帮助。
看待JavaScript的一种方式是认为只有一种方法可以调用函数1。它是:
functionObject.call(objectForThis, arg0, arg1, arg2, ...);

“objectForThis”总是提供了某些值。

其他所有内容都是对functionObject.call的语法糖

因此,可以通过它如何转换为functionObject.call来描述其他所有内容。

如果只调用一个函数,则this是“全局对象”,在浏览器中是窗口。

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

foo();  // this is the window object

换句话说,
foo();

被有效地翻译成了
foo.call(window);

请注意,如果您使用严格模式,则this将为undefined

'use strict';

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

foo();  // this is the window object

这意味着
换句话说,
foo();

被有效地翻译为
foo.call(undefined);

在JavaScript中,有像+-*这样的运算符。还有一个点运算符.
当左侧为对象,右侧为函数时,使用.运算符实际上意味着“将对象作为this传递给函数”。
例如:

const bar = {
  name: 'bar',
  foo() { 
    console.log(this); 
  },
};

bar.foo();  // this is bar

换句话说,bar.foo()被翻译为const temp = bar.foo; temp.call(bar);
请注意,函数的创建方式并不重要(大部分情况下……)。所有这些方式都会产生相同的结果。

const bar = {
  name: 'bar',
  fn1() { console.log(this); },
  fn2: function() { console.log(this); },
  fn3: otherFunction,
};

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

bar.fn1();  // this is bar
bar.fn2();  // this is bar
bar.fn3();  // this is bar

再次说,这些只是语法糖。

{ const temp = bar.fn1; temp.call(bar); }
{ const temp = bar.fn2; temp.call(bar); }
{ const temp = bar.fn3; temp.call(bar); }

另一个要注意的问题是原型链。当您使用 a.b 时,JavaScript 首先在直接由 a 引用的对象中查找属性 b。如果在对象上没有找到 b,则 JavaScript 将查找对象的原型以找到 b
有多种方法来定义对象的原型,2019 年最常见的是使用 class 关键字。但就 this 而言,它并不重要。重要的是,在查找对象 a 的属性 b 时,如果在对象或其原型链中找到属性 b,且 b 最终是一个函数,则应用与上述相同的规则。函数 b 引用将使用 call 方法调用,并传递 a 作为 objectForThis,如本答案顶部所示。
现在,让我们想象我们制作了一个显式设置 this 并调用另一个函数的函数,然后使用 .(点)运算符调用它。

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

function bar() {
  const objectForThis = {name: 'moo'}
  foo.call(objectForThis);  // explicitly passing objectForThis
}

const obj = {
  bar,
};

obj.bar();  

使用call进行翻译后,obj.bar()变成了const temp = obj.bar; temp.call(obj);。当我们进入bar函数时,我们调用foo,但是我们明确传递了另一个对象作为objectForThis,因此当我们到达foo时,this是那个内部对象。
这就是bind=>函数的实际效果。它们更像是语法糖。它们有效地构建了一个与上面的bar完全相同的新的不可见函数,它在调用指定的任何函数之前显式设置this。在绑定的情况下,this设置为您传递给bind的内容。

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

const bar = foo.bind({name: 'moo'});

// bind created a new invisible function that calls foo with the bound object.

bar();  

// the objectForThis we are passing to bar here is ignored because
// the invisible function that bind created will call foo with with
// the object we bound above

bar.call({name: 'other'});

请注意,如果functionObject.bind不存在,我们可以像这样自己制作一个。
function bind(fn, objectForThis) {
  return function(...args) {
    return fn.call(objectForthis, ...args);
  };
}

然后我们可以这样调用它。
function foo() {
  console.log(this);
}

const bar = bind(foo, {name:'abc'});

箭头函数和 => 运算符是绑定的语法糖。
const a = () => {console.log(this)};

是同样的意思

const tempFn = function() {console.log(this)}; 
const a = tempFn.bind(this);

bind 类似,一个新的不可见函数被创建,它使用绑定的值来调用给定的函数,但不同于 bind 的是要绑定的对象是隐式的。它是当使用 => 操作符时出现的 this
因此,就像上面的规则一样。
const a = () => { console.log(this); }  // this is the global object

'use strict';
const a = () => { console.log(this); }  // this is undefined

function foo() {
  return () => { console.log(this); }
}

const obj = {
  foo,
};
const b = obj.foo();
b();

"obj.foo()"翻译成"const temp = obj.foo; temp.call(obj);",这意味着foo中的箭头运算符将会将obj绑定到一个新的不可见函数,并返回该新的不可见函数,该函数被赋值给b。"b()"将像以前一样工作,即"b.call(window)"或"b.call(undefined)"调用foo创建的新不可见函数。该不可见函数忽略传递给它的this,并将obj作为objectForThis传递给箭头函数。

以上代码的翻译如下:

function foo() {
  function tempFn() {
    console.log(this);
  }
  return tempFn.bind(this);
}

const obj = {
  foo,
};
const b = obj.foo();
b.call(window or undefined if strict mode);

"apply"是另一个与"call"类似的函数。
functionName.apply(objectForThis, arrayOfArgs);

但是,从ES6的概念上来说,你甚至可以将其翻译成。
functionName.call(objectForThis, ...arrayOfArgs);

你的解释非常有效。完全消除了我的困惑。 - John Winston

1
这是关于编程的内容,保留了HTML格式,翻译为:

这个用法类似于作用域


  <script type="text/javascript" language="javascript">
$('#tbleName tbody tr').each(function{
var txt='';
txt += $(this).find("td").eq(0).text();
\\same as above but synatx different
var txt1='';
 txt1+=$('#tbleName tbody tr').eq(0).text();
alert(txt1)
});
</script>

txt1和txt的值相同 在上面的例子中, $(this) = $('#tbleName tbody tr') 相同


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接