2009-02-12 3 views
8

Я спрашиваю об этом с точки зрения языка. Итак, я пытаюсь найтиКаково обоснование поведения ключевого слова 'this' в JavaScript?

  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, или хотите, чтобы начать спор; Я просто пытаюсь лучше понять это. Я действительно ценю, что «это» может быть полезно, но признать, что it can be confusing также ... Конечно, запутанный для новичков, а может быть, и для экспертов, и в достаточно неясных случаях.

И все же, остается в значительной степени используются и, казалось бы, уважаемая часть языка, в то время, когда другие основные аспекты языка, кажется справедливой игрой для гнушаясь (т.е. Крокфорд и with или new). Что мне не хватает тогда, что делает this незаменимым?

+0

Что создаёт впечатление, что «это» является ошибкой? –

+0

@ Gamecat: Вы никогда не пытались создать язык, похожий на javascript, не так ли? – artificialidiot

+0

Я все еще думаю, что это аргументировано. Язык подразумевает проблему, cf «Как удивительно это« это »(если это потрясающе)» – annakata

ответ

11

Вы, кажется, ожидает, что это вести себя, как это происходит в некоторых объектно-ориентированных языков, где оно всегда относится к объекту метод принадлежит.

Но в JavaScript функция может быть прикреплена к нескольким объектам или вообще не иметь объекта. В вашем примере вы написали функцию, предназначенную для использования в контексте одного конкретного объекта ... Но ничто не мешает мне принять эту функцию и привязать ее к любому другому объекту. Это просто характер языка - функции являются первоклассными, членство объекта не является обязательным.

Следовательно, этот относится к контексту, в котором вызывается функция. Прямо сейчас это либо произвольный объект (указанный через ., .apply, или .call()), либо глобальный объект. В будущих версиях языка он будет ссылаться на контекст, в котором была определена функция: глобальный объект для глобальных функций, внешний this для внутренних функций; вы можете рассматривать это как исправление дефекта дизайна, поскольку на практике возможность ссылаться на глобальный объект с использованием этого не особенно полезна.

3
  • Это не
  • Много OO вещи
  • Это хороший способ это
+1

Хорошо. Не могли бы вы привести примеры того, что OO-материал невозможен? –

+0

Это очень бесполезный ответ, поскольку автор высказывает мнение как факт, не опираясь на аргументы или примеры. -1 – rix0rrr

+0

mike g: делать публичные функции rix0rr: довольно сложно вернуться с фактами, так как другая сторона не предоставляет – svinto

1
  • Он должен называться «я» вместо
  • Все, что относится к току объектов.
  • Выбрав «self» как имя для «this» и передав его явно (как первый аргумент) всем методам. Таким образом, вы можете легко указать метод экземпляра из статического метода или из функции.

Извините, но мне очень нравится Python ;-)

+0

Я думаю, что его следует называть Me;) –

+0

У кого-то нет чувства юмора? Перенос «этого»? – Abgan

+0

Вы забыли упомянуть, что вы хотите, чтобы «я» передавался как первый аргумент метода вместо того, чтобы иметь неявный доступ к нему. –

3

Я думаю, несвязанный "это" ошибка. В противном случае это очень удобно. Unbound «this» открывает возможность неверного истолкования контекста, наиболее заметно проявляющегося при обработке событий браузерами. Кроме того, библиотеки javascript имеют разные мнения о том, что «это» должно относиться к обработке событий и к многим конструкциям обратного вызова (например, к карте, фильтру).

Удаление несвязанного «этого», вероятно, не затруднит задачу.

Редактировать: Я предполагаю, что альтернативный пример синтаксиса упростит мою позицию.

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;}; 
} 
8

Я не думаю, что создание этого «несвязанного» было ошибкой. Иногда это может сбивать с толку, но есть веские причины. Первое, что приходит на ум, состоит в том, что, поскольку JavaScript не является языком на основе классов, функции не связаны с каким-либо конкретным классом, поэтому нет последовательного способа автоматически привязать «это» к правильному экземпляру объекта. Например,

function Person(first, last, age) { 
    this.firstName = first; 
    this.lastName = last; 
    this.age = age; 
} 

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

«это» должно относиться к объекту Person, но функция, назначенная Person.prototype.getName не имеет никакого способа узнать, как это будет использоваться, так что «это» должен быть привязан к любому объекту, на который он вызван.

Если это вызывает проблему, это когда у вас есть вложенные функции.

// 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 предложил бы удобно, но это довольно легко связать «это» к конкретному объекту при помощи применяются:

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; 
}; 
+0

Matthew Crumley (я не знаю, почему знак «@» не работает, но, надеюсь, вы увидите этот пост). Я пытаюсь следовать вашему коду в вашем ответе, но, может быть, мне что-то не хватает? Скажите, пожалуйста, где вы определяете функцию 'getNameFirstLast'? Является ли это опечаткой и почему ее возвращаемые значения не определены? Благодарю. – Chris22

+0

@ Chris22 Упс, это была опечатка. это должно быть 'getNameLastFirst()'. Причина, по которой он возвращает 'undefined' в первом примере, состоит в том, что он вызывается без контекста, поэтому по умолчанию этот параметр привязан к глобальной области. Таким образом (при условии, что глобальных переменных не названо 'lastName' или' firstName'), оба свойства будут не определены, а не будут получать значения из объекта Person, как вы могли бы ожидать. –

+0

+1 Мэтью Крамли Хорошо, спасибо.Я понимаю масштаб «этого». Опечатка просто отвлекала :-) – Chris22

1

I необязательное ключевое слово 'this' необходимо, потому что JavaScript - это язык, основанный на прототипе. Кто-то, кто лучше информирован, может, возможно, заполнить подробности здесь.

Тот факт, что это так, сильно бесполезен. Особенно, если вы хотите передать метод объекта в функции высшего порядка, вещи начинают получать уродливые (следующие примеры с небольшой помощью MooTools):

myArray.each(myObject.foo); 

не будет работать, потому что «это» в myObject.foo будет ссылаться на myArray вместо myObject. Вместо этого:

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

Что кажется очень уродливым для меня. Вот почему я обычно не программирую объектно-ориентированным образом в JavaScript, но вместо этого я полагаюсь на закрытие.

1

Рассмотрим идиома a.f() в качестве краткого обозначения:

a.f.call(a); 

Это, по определению, вызов функции f, используя сферу a.

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

Если this и a не тот же объект, 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() 
Смежные вопросы