8

我是从语言设计的角度来问这个的。所以我试图找出

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

为了澄清我为什么不安this,请考虑以下示例:

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

注意f()所属的对象是多么容易丢失:从a,分离this成为全局对象(window对于浏览器)。

现在考虑:

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

var a = newA();
var f = a.f;
// f() == a.f() !

根本不使用this,我们能够建立和维护对象上下文,而不管方法在哪里或如何使用。我不禁想到,凭借闭包提供的力量,它this变得多余,甚至可能有点危险......

我不是针对 的仇杀this,也不是要开始争论;我只是想更好地理解它。我确实很欣赏“this”可能很有用,但也认识到它也可能令人困惑......当然会让初学者感到困惑,也许在足够模糊的情况下也会让专家感到困惑。

with然而,在该语言的其他核心方面似乎可以回避(即 Crockford 和or new)的时代,它仍然是该语言中一个被大量使用且似乎备受推崇的部分。那我错过了什么,那是this必不可少的吗?

4

7 回答 7

11

您似乎期望它的行为与某些 OO 语言中的行为一样,它总是指方法所属的对象。

但是在 JavaScript 中,一个函数可以附加到多个对象,或者根本没有对象。在您的示例中,您编写了一个旨在在一个特定对象的上下文中使用的函数......但是没有什么能阻止我采用该函数并将其附加到任何其他对象。这就是语言的本质——函数是一流的,对象成员是可选的。

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

于 2009-02-12T16:11:55.960 回答
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;
};

建议的语法人工白痴会很方便,但使用 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;
};
于 2009-02-12T15:50:47.600 回答
3
  • 不是
  • 很多面向对象的东西
  • 它的方式很好
于 2009-02-12T13:21:26.430 回答
3

我认为未绑定的“this”是一个错误。否则它非常方便。未绑定的“this”打开了误解浏览器事件处理中最明显的上下文的可能性。此外,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;};
}
于 2009-02-12T13:47:58.630 回答
1
  • 它应该被称为“自我”
  • 任何引用当前对象状态的东西。
  • 通过选择“self”作为“this”的名称,并将其显式(作为第一个参数)传递给所有方法。通过这种方式,您可以轻松地将实例方法与静态方法或函数区分开来。

抱歉,但我真的很喜欢 Python ;-)

于 2009-02-12T13:35:21.763 回答
1

认为未绑定的“this”关键字是必要的,因为 JavaScript 是一种基于原型的语言。消息灵通的人可能可以在此处填写详细信息。

事实是,虽然非常无益。尤其是如果您想将对象的方法传递给高阶函数,事情就会变得很糟糕(以下示例在 MooTools 的帮助下):

myArray.each(myObject.foo);

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

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

这对我来说似乎很丑陋。这就是为什么我通常不在 JavaScript 中以面向对象的方式进行编程,而是我非常依赖闭包。

于 2009-02-12T16:07:15.643 回答
1

将成语a.f()视为以下的简写:

a.f.call(a);

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

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

如果thisa不是同一个对象,f()并且a.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()
于 2009-02-12T16:08:24.550 回答