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

这似乎是随意的,尽管这是我所期望的工作方式。这是已定义的行为吗?它跨浏览器安全吗?是否有任何潜在的原因,为什么它会超出“规格说明”(例如,这是一些更广泛的设计决策/哲学的结果)?简化后的代码示例:

// 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
}

当前回答

函数调用

函数只是Object的一种类型。

所有Function对象都有调用和应用方法,用于执行被调用的Function对象。

当调用时,这些方法的第一个参数指定在函数执行期间this关键字将引用的对象——如果它为空或未定义,则使用全局对象window。

因此,调用函数…

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"

要调用的附加实参作为函数调用的实参传递,而要apply的单个附加实参可以将函数调用的实参指定为类似array的对象。

因此,foo(1,2,3)等价于foo。调用(null, 1,2,3)或foo。应用(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
};

...通过对象访问函数的引用并使用圆括号- obj.foo() -等价于foo.call(obj)或foo.apply(obj)。

然而,作为对象属性持有的函数并不“绑定”到这些对象。正如您在上面obj的定义中所看到的,由于函数只是对象的一种类型,所以它们可以被引用(因此可以通过引用传递给函数调用或通过引用从函数调用返回)。当传递一个函数的引用时,不会携带关于它从哪里传递的额外信息,这就是为什么会发生以下情况:

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

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

作用域链

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

当函数被执行时,它会创建一个新的作用域,并具有对任何封闭作用域的引用。在上面的例子中创建匿名函数时,它有一个对创建它的作用域的引用,也就是bind的作用域。这就是所谓的“闭包”。

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

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

以上所述,你现在应该能够理解scope在下面的例子中是如何工作的,以及为什么在“pre-bound”函数被调用时传递一个具有特定this值的函数的技术是有效的:

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

其他回答

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

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

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

此值是执行上下文的属性,但不是 属性。

这个特性非常重要,因为与变量相反,这个值从不参与标识符解析过程。也就是说,当在代码中访问this时,它的值直接从执行上下文中获取,而不需要任何作用域链查找。this的值在进入上下文时只确定一次。

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

对于函数上下文,这个值在每个单独的函数调用中都可能是不同的

参考Javascript-the-core和第3-this章

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

是的。是的。

这背后有什么原因吗 事情就是这样……

这句话的意思很容易推导:

如果在构造函数中使用this,并且函数是用new关键字调用的,则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'设置为全局或未定义,而不是object,它实际上与对象解构无关,而是如何调用c(),但在这里不容易通过它看到。

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

这在JS中:

有三种不同意义的函数类型。最好通过例子来解释:

构造函数

//在构造函数中this指向新创建的对象 //每个函数都可以是JavaScript的构造函数。 函数的狗(颜色){ 这一点。颜色=颜色; } //通过在函数调用前放置new来调用构造函数 const myDog = new Dog('red'); // logs狗有红色 console.log('狗有颜色' + myDog.color);

正常的函数或方法

// 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.

事件监听器

在事件处理程序的函数中,this将引用检测到事件的DOM元素。请看这个问题:在事件处理程序中使用这个

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

请参阅Douglas Crockford所著的“JavaScript: The Good Parts”。