代码之家  ›  专栏  ›  技术社区  ›  mike g

javascript中“this”关键字的行为原理是什么?

  •  8
  • mike g  · 技术社区  · 16 年前

    我是从语言设计的角度来问这个问题的。所以我想知道

    1. 其行为的基本原理是什么? this ?
    2. 在多大程度上 是一个错误,还是可以改进?

    为了澄清我为什么不安 ,考虑这个例子:

    var a = {};
    a.f = function(){ return this; }
    var f = a.f;
    // f() != a.f()
    

    注意对象是多么容易 f() 归属丢失:与 a , 成为全局对象( window 对于浏览器来说。

    现在考虑:

    var newA = function(){
        var self = {};
        self.f = function(){ return self; }
        return self;
    }
    
    var a = newA();
    var f = a.f;
    // f() == a.f() !
    

    不使用 不管方法在何处或如何使用,我们都能够建立和维护对象上下文。我忍不住想,有了闭门器的动力, 变得多余,甚至有点危险…

    我不是在报复 或者想开始争论;我只是想更好地理解它。我很感激“这个”是有用的,但要认识到 it can be confusing 也。。。当然,对于初学者来说是令人困惑的,对于专家来说也是如此。

    然而,在语言的其他核心方面似乎是回避的公平游戏(例如,克罗克福德和 with new )那么我错过了什么呢,那就意味着 不可或缺?

    7 回复  |  直到 16 年前
        1
  •  11
  •   Shog9    16 年前

    您似乎希望它在某些OO语言中的行为与此相同,在这些语言中,它总是引用方法所属的对象。

    但在javascript中, 功能 可以附加到多个对象,或者根本没有对象。在您的示例中,您编写了一个用于 一个特定对象 …但没有什么能阻止我接受这个函数并将它附加到任何其他对象上。这就是语言的本质——函数是一流的,对象成员是可选的。

    因此, 引用调用函数的上下文。现在,这要么是一个任意对象(通过 . , .apply .call() )或全局对象。在语言的未来版本中,它将引用定义函数的上下文:全局函数的全局对象,外部 this 对于内部函数,您可以将其视为对设计缺陷的更正,因为实际上,使用它引用全局对象并不是特别有用。

        2
  •  8
  •   Community CDub    8 年前

    我不认为把“这个”解开是个错误。一开始它有时会令人困惑,但有充分的理由说明它是这样的。首先要想到的是,由于JavaScript不是基于类的语言,因此函数与任何特定的类都没有关联,因此没有一致的方法自动将“this”绑定到正确的对象实例。例如,

    function Person(first, last, age) {
        this.firstName = first;
        this.lastName = last;
        this.age = age;
    }
    
    Person.prototype.getFullName = function() {
        return this.firstName + " " + this.lastName;
    };
    

    “this”需要引用一个person对象,但是分配给person.prototype.getname的函数没有任何方法知道如何使用它,因此“this”需要绑定到它调用的任何对象。

    当您有嵌套函数时,这会引起问题。

    // This is a really contrived example, but I can't think of anything better
    Person.prototype.getInfo = function() {
        // get name as "Last, First"
        function getNameLastFirst() {
            // oops. "this" is the global object, *not* the Person
            return this.lastName + ", " + this.firstName;
        }
    
        // expect something like "Crumley, Matthew: Age 25",
        // but you get "undefined, undefined: Age 25"
        return getNameLastFirst() + ": Age " + this.age;
    };
    

    句法 artificialidiot 建议是很方便的,但是使用apply将“this”绑定到特定对象是非常容易的:

    function bind(func, obj) {
        return function() {
            return func.apply(obj, arguments);
        };
    }
    
    Person.prototype.getInfo = function() {
        // get name as "Last, First"
        var getNameLastFirst = bind(function () {
            return this.lastName + ", " + this.firstName;
        }, this);
    
        return getNameLastFirst() + ": Age " + this.age;
    };
    

    或者使用闭包的更“传统”的方法:

    Person.prototype.getInfo = function() {
        var self = this;
    
        // get name as "Last, First"
        function getNameLastFirst() {
            return self.lastName + ", " + self.firstName;
        }
    
        return getNameLastFirst() + ": Age " + this.age;
    };
    
        3
  •  3
  •   Svante Svenson    16 年前
    • 它不是
    • 很多OO的东西
    • 这样很好
        4
  •  3
  •   artificialidiot    16 年前

    我认为解开“这个”是个错误。否则就很方便了。未绑定的“this”打开了错误解释浏览器事件处理中最明显的上下文的可能性。另外,对于事件处理和许多回调构造(如map、filter),JavaScript库对“this”应该指什么也有不同的看法。

    移除未绑定的“this”可能不会使事情变得更加困难。

    编辑:我想另一个语法示例会使我的立场更加清晰。

    function Foo()
    {
        //both this refer to the Foo instance
        this.blah=this.function(){this.bar;};
    
        //second this refers to baz
        this.blah=baz.function(){this.bar;};
    
        //second this refers to anonymous function itself
        this.blah=function(){this.bar;};
    }
    
        5
  •  1
  •   rix0rrr    16 年前

    认为 因为javascript是基于原型的语言,所以必须使用未绑定的“this”关键字。有更好的消息人士可以在这里填写详细信息。

    但事实是,这可能是无益的。特别是如果你想把一个对象的方法传递给一个高阶函数,事情就会变得很难看(下面的例子在mootools的帮助下):

    myArray.each(myObject.foo);
    

    将不起作用,因为myObject.foo中的“this”将引用myArray而不是myObject。而是:

    myArray.each(myObject.foo.bind(myObject))
    

    我觉得这很难看。这就是为什么我通常不以面向对象的方式在JavaScript中编程,但我主要依赖闭包。

        6
  •  1
  •   Ates Goral    16 年前

    想想这个成语 a.f() 简写为:

    a.f.call(a);
    

    根据定义,它是对函数的调用 f 使用范围 a .

    var f = a.f;
    f(); // f.call(this);
    a.f(); // f.call(a);
    

    如果 this 不是同一个对象, f() F.() 将使用不同的范围,因此行为可能不同。考虑在其他语言中静态方法和类方法之间的区别:

    class Foo {
    public:
        static void a(Foo *scope) {
            // do something with given scope
        };
    
        void b() {
            a(this); // do something with the scope of this object
        };
    };
    
    Foo foo;
    Foo bar;
    
    foo.a(&bar) != foo.b(); // just like f() != a.f()
    foo.a(&foo) == foo.b(); // just like f.call(a) == a.f()
    
        7
  •  1
  •   Abgan    16 年前
    • 它应该被称为“自我”而不是
    • 指当前对象状态的任何内容。
    • 通过选择“self”作为“this”的名称,并将其显式(作为第一个参数)传递给所有方法。这样,您可以很容易地将实例方法与静态方法或函数区分开来。

    对不起,我真的很喜欢蟒蛇;-)

    推荐文章