我刚刚在JavaScript中遇到了一个有趣的情况。我有一个类,其方法使用object-literal表示法定义多个对象。在这些对象中,正在使用 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
}
有帮助吗?

解决方案

从我的另一篇文章中删除,这里比你想知道这个更多。

在开始之前,这是关于Javascript最重要的事情,并且当它没有意义时重复自己。 Javascript没有类(ES6 class 语法糖)。如果看起来像一个类,这是一个聪明的把戏。 Javascript包含对象功能。 (这不是100%准确,函数只是对象,但将它们视为单独的东西有时会有所帮助)

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

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

作为方法

方法是附加到对象

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

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

作为一种功能

如果您有独立功能,变量将绑定到“全局”变量。对象,几乎总是浏览器上下文中的 window 对象。

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

这可能会让你沮丧,但不要感觉不好。很多人认为这是一个糟糕的设计决定。由于回调是作为函数而不是作为方法调用的,因此您可能会看到看似不一致的行为。

很多人通过这样做来解决这个问题,嗯,这个

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

您定义一个 的变量,指向 this 。 Closure(它自己的主题)保持那个,所以如果你把bar称为回调,它仍然有一个引用。

注意:在中使用严格模式(如果用作函数),不绑定到全局。 (它是 undefined )。

作为构造函数

您还可以将函数作为构造函数调用。根据您正在使用的命名约定(TestObject),这也可能就是您正在做的事情,并且正在绊倒您

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

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

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

有些人认为构造函数/ new关键字是Java /传统OOP程序员抛出的一个骨骼,可以创建类似于类的东西。

使用“应用方法”

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

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

其他提示

函数调用

函数只是一种Object。

所有Function对象都有来电 apply 方法执行它们被调用的Function对象。

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

因此,调用一个函数......

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"

call 的附加参数作为函数调用的参数传递,而 apply 的一个附加参数可以将函数调用的参数指定为Array-喜欢对象。

因此, 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
};

...通过对象访问函数的引用并用括号调用它 - obj.foo() - 相当于 foo.call(obj) foo.apply(obj)

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

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

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

范围链

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

执行Function时,它会创建一个新范围并引用任何封闭范围。在上面的示例中创建匿名函数时,它会引用它所创建的作用域,即 bind 的作用域。这被称为“封闭”。

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

当您尝试访问变量时,此“范围链”步行查找具有给定名称的变量 - 如果当前范围不包含变量,则查看链中的下一个范围,依此类推,直到达到全局范围。当返回匿名函数并且 bind 完成执行时,匿名函数仍然引用 bind 的范围,因此 bind 的范围没有“离开”。

鉴于上述所有内容,您现在应该能够理解范围在以下示例中的工作方式,以及为什么使用“预绑定”函数传递函数的技术。具有特定值的 this ,当它被称为工作时将具有:

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

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

是。是的。

  

有什么理由可以解释为什么   这是它的方式......

this 的含义很容易推断:

  1. 如果在构造函数中使用了此,并且使用 new 关键字调用该函数,指的是将被创造。 这个即使在公共方法中也会继续表示对象。
  2. 如果在其他任何地方使用此,包括嵌套的 protected 函数,则它引用全局范围(在浏览器的情况下是窗口对象)。 / LI>

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

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

请参阅“JavaScript:好的部分”由Douglas Crockford作出了很好的解释。

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

  

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

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

  

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

此功能非常重要,因为与变量相反,此值从不参与标识符解析过程。即在代码中访问它时,它的值直接来自执行上下文,没有任何作用域链查找。输入上下文时,此值仅确定一次。

在全局上下文中,this值是全局对象本身(也就是说,此值等于变量对象)

在函数上下文的情况下,每个函数调用中的此值可能不同

参考 Javascript-the-core 第3章 - 这个

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top