“this”关键字如何在函数中工作?

How does "this" keyword work within a function?

提问人:rmeador 提问时间:9/25/2008 最后编辑:Willem van der Veenrmeador 更新时间:12/6/2022 访问量:101451

问:

我刚刚在 JavaScript 中遇到了一个有趣的情况。我有一个类,其方法使用对象文字表示法定义多个对象。在这些对象中,指针正在使用。从程序的行为中,我推断出指针指的是调用该方法的类,而不是由文字创建的对象。thisthis

这似乎是武断的,尽管这是我期望它的工作方式。这是定义的行为吗?跨浏览器安全吗?是否有任何理由可以解释为什么它超出了“规范所说的”(例如,它是某些更广泛的设计决策/理念的结果)?精简代码示例:

// inside class definition, itself an object literal, we have this function:
onRender: function() {

    this.menuItems = this.menuItems.concat([
        {
            text: 'Group by Module',
            rptletdiv: this
        },
        {
            text: 'Group by Status',
            rptletdiv: this
        }]);
    // etc
}
javascript 这种 -设计 语言-特性

评论

0赞 Deeptechtons 10/18/2011
当我这样做时,它确实会发生var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};
0赞 Bergi 7/30/2015
参见 Javascript 中的“this”关键字如何在对象文字中起作用?
0赞 Love Hasija 8/31/2017
看看这篇文章。对这个关键词的各种用法和行为有一些很好的解释。
0赞 AL-zami 9/11/2017
查看此链接 scotch.io/@alZami/understanding-this-in-javascript

答:

9赞 Rakesh Pai 9/25/2008 #1

这是定义的行为吗?是吗 跨浏览器安全?

是的。是的。

有什么理由可以解释为什么 事情就是这样......

的含义很简单:this

  1. 如果在构造函数中使用,并且该函数是使用 关键字调用的,则指将要创建的对象。 即使在公共方法中,也将继续表示对象。thisnewthisthis
  2. 如果在其他任何地方使用,包括嵌套的受保护函数,则它指的是全局范围(在浏览器的情况下是窗口对象)。this

第二种情况显然是设计缺陷,但通过使用闭包可以很容易地解决它。

4赞 Santiago Cepas 9/25/2008 #2

在这种情况下,内部函数绑定到全局对象,而不是外部函数的变量。 这就是语言的设计方式。thisthis

参见 Douglas Crockford 的“JavaScript: The Good Parts”一文。

569赞 Alana Storm 9/25/2008 #3

从我的另一篇帖子中蚕食,这里比你想知道要多。

在我开始之前,这里有一件关于 Javascript 的最重要的事情要记住,当它没有意义时,要对自己重复一遍。Javascript 没有类(ES6 是语法糖)。如果某样东西看起来像一个类,这是一个聪明的伎俩。Javascript 有对象函数。(这不是 100% 准确的,函数只是对象,但有时将它们视为单独的事物会有所帮助)class

this 变量附加到函数。每当调用函数时,都会为其指定一个值,具体取决于调用函数的方式。这通常称为调用模式。

在 javascript 中调用函数有四种方法。您可以将函数作为方法、函数、构造函数apply 调用

作为一种方法

方法是附加到对象的函数

var foo = {};
foo.someMethod = function(){
    alert(this);
}

当作为方法调用时,这将绑定到函数/方法所属的对象。在此示例中,这将绑定到 foo。

作为函数

如果你有一个独立的函数,这个变量将绑定到“全局”对象,几乎总是浏览器上下文中的窗口对象。

 var foo = function(){
    alert(this);
 }
 foo();
 

这可能是让你绊倒的原因,但不要感到难过。许多人认为这是一个糟糕的设计决策。由于回调是作为函数而不是方法调用的,因此您会看到看似不一致的行为。

许多人通过做这样的事情来解决这个问题,嗯,这个

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

您定义了一个指向的变量。闭包(一个独立的主题)保留了,所以如果你调用 bar 作为回调,它仍然有一个引用。

注意:In 模式如果用作函数,则不绑定到全局。(它是)。use strictthisundefined

作为构造函数

还可以调用函数作为构造函数。根据你使用的命名约定 (TestObject),这也可能是你正在做的事情,也是让你绊倒的原因

使用 new 关键字将函数作为构造函数调用。

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

当作为构造函数调用时,将创建一个新对象,并将其绑定到该对象。同样,如果你有内部函数,并且它们被用作回调,你将把它们作为函数调用,这将绑定到全局对象。使用那个 var that = this trick/pattern。

有些人认为 constructor/new 关键字是扔给 Java/传统 OOP 程序员的骨头,作为创建类似于类的东西的一种方式。

使用 Apply 方法

最后,每个函数都有一个名为“apply”的方法(是的,函数是 Javascript 中的对象)。Apply 允许您确定值,还允许您传入参数数组。这是一个无用的例子。

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

 

评论

8赞 Miscreant 7/9/2015
注意:在严格模式下,将用于函数调用。thisundefined
1赞 richard 7/23/2015
函数声明,例如。函数 myFunction () {},是“作为方法”的特例,其中“this”是全局范围(窗口)。
1赞 T.J. Crowder 7/18/2016
@richard:严格模式除外,与范围无关。你指的是全局对象this
0赞 wunth 9/20/2017
@alan风暴.在“作为构造函数”的情况下,与 ?所以两者都允许?如果不是这样,你就可以使用它来为内部工作创建属性和其他变量,那就太好了。this.confusing = 'hell yeah';var confusing = 'hell yeah';myObject.confusingthis
0赞 wunth 9/21/2017
但话又说回来,我想工作的东西可以在函数之外完成,并将值传递给构造函数:然后function Foo(thought){ this.confusing = thought; }var myObject = new Foo("hell yeah");
36赞 Jonny Buchanan 9/26/2008 #4

函数调用

函数只是对象的一种类型。

所有 Function 对象都具有 callapply 方法,这些方法执行调用它们的 Function 对象。

调用时,这些方法的第一个参数指定在执行函数期间关键字将引用的对象 - 如果它是 或 ,则全局对象 , 用于 。thisnullundefinedwindowthis

因此,调用函数...

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

...带括号的 - - 等价于 或 ,这实际上与 或 相同。foo()foo.call(undefined)foo.apply(undefined)foo.call(window)foo.apply(window)

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

其他参数 to 作为参数传递给函数调用,而单个附加参数 to 可以将函数调用的参数指定为类似 Array 的对象。callapply

因此,等价于 或 。foo(1, 2, 3)foo.call(null, 1, 2, 3)foo.apply(null, [1, 2, 3])

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

如果函数是对象的属性...

var obj =
{
    whereAmI: "obj",
    foo: foo
};

...通过对象访问对 Function 的引用并用括号 - - 调用它等效于 或 。obj.foo()foo.call(obj)foo.apply(obj)

但是,作为对象属性保存的函数不会“绑定”到这些对象。正如您在上面的定义中看到的,由于 Functions 只是 Object 的一种类型,因此可以引用它们(因此可以通过引用传递给 Function 调用或通过引用从 Function 调用返回)。传递对函数的引用时,不会携带有关其传递位置的其他信息,这就是发生以下情况的原因:obj

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

对函数引用 的调用 ,不会为调用提供任何上下文,因此它实际上与 相同,因此最终引用了 。如果我们想知道它属于 ,我们需要在调用时以某种方式提供该信息,这是 or 和闭包的第一个参数发挥作用的地方。bazbaz.call(undefined)thiswindowbazobjbazcallapply

示波器链

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

执行函数时,它会创建一个新作用域,并具有对任何封闭作用域的引用。在上面的示例中创建匿名函数时,它引用了创建它的作用域,即 的作用域。这称为“闭包”。bind

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

当您尝试访问变量时,将遍历此“作用域链”以查找具有给定名称的变量 - 如果当前作用域不包含该变量,则查看链中的下一个作用域,依此类推,直到到达全局作用域。当匿名函数返回并完成执行时,匿名函数仍然具有对 的引用,因此 的作用域不会“消失”。bindbindbind

鉴于上述所有内容,您现在应该能够理解以下示例中作用域的工作原理,以及为什么在调用函数时,将具有特定值的函数传递到“pre-bound”周围的技术会起作用:this

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"

评论

0赞 Alex Marandon 3/18/2012
“当传递对函数的引用时,不会携带有关其传递位置的其他信息”,谢谢@insin。
4赞 Damodaran 3/13/2014 #5

我找到了一个关于 ECMAScript 的好教程

答:此值是与执行相关的特殊对象 上下文。因此,它可以被命名为上下文对象(即 对象,在哪个上下文中激活执行上下文)。

任何对象都可以用作上下文的此值。

a 此值是执行上下文的属性,但不是 变量对象的属性。

此功能非常重要,因为与变量相反,此值从不参与标识符解析过程。也就是说,当在代码中访问它时,它的值直接从执行上下文中获取,无需任何范围链查找。在进入上下文时,此值仅确定一次。

在全局上下文中,this 值是全局对象本身(这意味着,此处的此值等于变量 object)

对于函数上下文,每个函数调用中的此值可能不同

参考 Javascript-the-coreChapter-3-this

评论

0赞 RobG 4/13/2015
"在全局上下文中,this 值是全局对象本身(这意味着,此处的此值等于变量 object)”。全局对象是全局执行上下文的一部分,(es4) “变量对象” 和 ES5 环境记录也是如此。但它们是全局对象的不同实体(例如,环境记录不能被直接引用,它被规范禁止,但全局对象可以)。
0赞 Qiulang 9/29/2020 #6

这里的所有答案都非常有帮助,但我仍然很难弄清楚在我的情况下该说什么,这涉及到对象解构。因此,我想使用我的代码的简化版本再添加一个答案,this

let testThis = {
  x: 12,
  y: 20,
  add({ a, b, c }) {
    let d = a + b + c()
    console.log(d)
  },
  test() {
    //the result is NaN
    this.add({
      a: this.x,
      b: this.y,
      c: () => {
        //this here is testThis, NOT the object literal here
        return this.a + this.b 
      },
    })
  },
  test2() {
    //64 as expected
    this.add({
      a: this.x,
      b: this.y,
      c: () => {
        return this.x + this.y
      },
    })
  },
  test3() {
    //NaN
    this.add({
      a: this.x,
      b: this.y,
      c: function () {
        //this here is the global object
        return this.x + this.y 
      },
    })
  },
}

正如这里解释的那样,Javascript - 解构对象 - “this”设置为全局或未定义,而不是对象,它实际上与对象解构无关,而是与c()的调用方式无关,但在这里不容易看穿它。

MDN 说“箭头函数表达式最适合非方法函数”,但箭头函数在这里有效。

0赞 Willem van der Veen 6/21/2022 #7

this在 JS 中:

有 3 种类型的函数具有不同的含义。最好通过示例来解释它们:this

  1. 构造 函数

// In a constructor function this refers to newly created object
// Every function can be a constructor function in JavaScript e.g.
function Dog(color){
  this.color = color;
}

// constructor functions are invoked by putting new in front of the function call 
const myDog = new Dog('red');

// logs Dog has color red
console.log('Dog has color ' + myDog.color);

  1. 正常功能或方法

// Browswer example:

console.log(this === window) // true

function myFn(){
  console.log(this === window)
}

myFn(); // logs true
// The value of this depends on the context object.
// In this case the context from where the function is called is global.
// For the global context in the browser the context object is window.

const myObj = {fn: myFn}

myObj.fn() // logs false
// In this case the context from where the function is called is myObj.
// Therefore, false is logged.

myObj.fn2 = function myFn(){
  console.log(this === myObj)
}

myObj.fn2() // logs true
// In this case the context from where the function is called is myObj.
// Therefore, true is logged.

  1. 事件侦听器

在事件处理程序的函数内部,将引用检测到事件的 DOM 元素。请参阅此问题:在事件处理程序中使用它this