Ссылаясь на «это» в родительском замыкании в JavaScript - PullRequest
9 голосов
/ 26 октября 2010

Я хочу сделать это в Javascript:

function Z( f )
{
  f();
}

function A()
{
  this.b = function()
  {
    Z( function () { this.c() } );
  }

  this.c = function()
  {
    alert('hello world!');
  }
}

var foo = new A();
foo.b();

Это может быть достигнуто следующим образом:

function Z( f )
{
  f();
}

function A()
{
  var self = this;
  this.b = function()
  {
    Z( function () { self.c() } );
  }

  this.c = function()
  {
    alert('hello world!');
  }
}

var foo = new A();
foo.b();

Есть ли лучший способ?

Ответы [ 3 ]

6 голосов
/ 26 октября 2010

Хорошим подходом является сохранение ссылки на родителя (как у вас), однако для вашего конкретного примера нет необходимости в анонимной оболочке, вы можете передать функцию напрямую, например:

var self = this;
this.b = function()
{
  Z(self.c);
}

Вы можете проверить это здесь , и без этой оболочки фактически нет необходимости в переменной self, вы можете просто использовать this напрямую, например:

this.b = function()
{
  Z(this.c);
}

Вы можете проверить эту версию здесь .


Поскольку в комментариях ниже, кажется, есть некоторая путаница, приведенный выше код содержит this для вопроса , если вы хотите сохранить контекст this / внутри обратного вызова, используйте .call() вот так :

this.b = function()
{
  Z.call(this, this.c);
}

И для Z:

function Z( f )
{
  f.call(this);
}

Вы можете проверить это здесь .

1 голос
/ 26 октября 2010

Вы также можете использовать

this.b = function()
{
    Z( (function () { this.c() }).apply(this) );
}
1 голос
/ 26 октября 2010

Существует шаблон, который часто называется «Делегат», который решает эту проблему.

В javascript не слишком причудливая реализация может выглядеть примерно так:

/** class Delegate **/
var Delegate = function(thisRef, funcRef, argsArray) {
    this.thisRef=thisRef;
    this.funcRef=funcRef;
    this.argsArray=argsArray;
}
Delegate.prototype.invoke = function() {
    this.funcRef.apply(this.thisRef, this.argsArray);
}
/** static function Delegate.create - convenience function **/
Delegate.create = function(thisRef, funcRef, argsArray) {
    var d = new Delegate(thisRef, funcRef, argsArray);
    return function() {  d.invoke(); }
}

В вашем примере вы бы использовали его так:

this.b = function() {
  Z( Delegate.create(this, this.c) );
}

Вы также можете написать функции, которые ожидают получения делегата:

function Z( d ) {
    d.invoke();
}

затем, в A, ваше значение b становится:

this.b = function() {
    var d = new Delegate(this, this.c);

    Z( d );
    SomeOtherFunc( d );
}

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

...