首页 文章

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

提问于
浏览
244

我刚刚在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
}

5 回答

  • 553

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

    是 . 是的 .

    有没有任何理由可以解释为什么它是这样的...

    this 的含义很容易推断:

    • 如果在构造函数内部使用了 this ,并且使用 new 关键字调用了该函数, this 将引用将要创建的对象 . 即使在公共方法中, this 仍将继续表示对象 .

    • 如果 this 在其他任何地方使用,包括嵌套的受保护函数,则它引用全局范围(在浏览器的情况下是窗口对象) .

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

  • 9

    函数调用

    函数只是一种Object .

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

    调用时,这些方法的第一个参数指定在执行函数期间 this 关键字将引用的对象 - 如果它是 nullundefined ,则全局对象 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) .

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

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

    对函数引用 baz 的调用与 baz.call(undefined) 实际上没有't provide any context for the call, so it',因此 this 最终引用了 window . 如果我们希望 baz 知道它属于 obj ,我们需要以某种方式在调用 baz 时提供该信息,这是 callapply 和闭包的第一个参数发挥作用的地方 .

    范围链

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

    执行Function时,它会创建一个新范围并引用任何封闭范围 . 当在上面的例子中创建匿名函数时,它引用了它创建的范围,这是 bind 的范围 . 这被称为"closure."

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

    当您尝试访问变量时,将使用"scope chain"查找具有给定名称的变量 - 如果当前范围不包含变量,则查看链中的下一个范围,依此类推,直到达到全局范围为止 . 当返回匿名函数并且 bind 完成执行时,匿名函数仍然具有对 bind 范围的引用,因此 bind 's scope doesn' t "go away" .

    鉴于上述所有内容,您现在应该能够理解范围在以下示例中的工作方式,以及为什么在调用"pre-bound"时使用特定值 this 传递函数的技术在调用时会起作用:

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

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

    请参阅Douglas Crockford撰写的“JavaScript:The Good Parts”作为一个很好的解释 .

  • 4

    从我的另一个帖子中解放出来,这里比你想知道的更多 .

    在我开始之前,这里's the most important thing to keep in mind about Javascript, and to repeat to yourself when it doesn' t有意义 . Javascript没有类(ES6 classsyntactic sugar) . 如果看起来像一个类,这是一个聪明的伎俩 . Javascript有 objectsfunctions . (那不是100%准确,函数只是对象,但有时可以将它们视为单独的东西有帮助)

    此变量附加到函数 . 无论何时调用函数,都会给出一定的值,具体取决于您调用函数的方式 . 这通常称为调用模式 .

    有四种方法可以在javascript中调用函数 . 您可以将函数作为方法,函数,构造函数和apply来调用 .

    作为一种方法

    方法是附加到对象的函数

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

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

    作为一个功能

    如果你有一个独立的函数,这个变量将被绑定到"global"对象,几乎总是在浏览器的上下文中的窗口对象 .

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

    This may be what's tripping you up ,但不要为什么你看到看似不一致的行为 .

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

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

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

    注意:在 use strict 模式中,如果用作函数, this 不会绑定到全局 . (这是 undefined ) .

    作为构造函数

    您还可以将函数作为构造函数调用 . 根据您正在使用的命名约定(TestObject),这也是 may be what you're doing and is what's tripping you up .

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

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

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

    有些人认为构造函数/ 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);
    
  • 34

    我发现了一个很好的关于ECMAScript的教程

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

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

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

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

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

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

    参考Javascript-the-coreChapter-3-this

相关问题