JS Object this.method () ломается через jQuery - PullRequest
13 голосов
/ 09 октября 2009

Я уверен, что есть простой ответ на этот вопрос, но сегодня пятница, и я устал. (

Не знаю, как это объяснить, поэтому я просто отправлю пример кода ...


Вот простой объект:

var Bob =

{ Stuff : ''

, init : function()
    {
        this.Stuff = arguments[0]
    }

, doSomething : function()
    {
        console.log( this.Stuff );
    }

}

И здесь это используется:

$j = jQuery.noConflict();
$j(document).ready( init );


function init()
{
    Bob.init('hello');

    Bob.doSomething();

    $j('#MyButton').click( Bob.doSomething );
}

Все работает, кроме последней строки. Когда jQuery вызывает метод doSomething, он переопределяет this и прекращает его работу.

Попытка использовать просто Stuff тоже не работает.

Итак, как я могу ссылаться на собственные свойства объекта таким образом, чтобы jQuery мог вызывать его, а также позволял объекту работать с вызывающим объектом jQuery?

т.е. Я хотел бы иметь возможность делать такие вещи:

doSomething : function()
    {
        console.log( <CurrentObject>.Stuff + $j(<CallerElement>).attr('id') );
    }

(где <CurrentObject> и <CallerElement> заменены соответствующими именами.)

Ответы [ 4 ]

24 голосов
/ 09 октября 2009

Это не ошибка jQuery, это неотъемлемая часть способа, которым JavaScript обрабатывает объекты.

В отличие от большинства других объектно-ориентированных языков, «this» является , а не ограниченным уровнем для метода в JavaScript; вместо этого он определяется исключительно тем, как вызывается функция:

Bob= {
    toString: function() { return 'Bob!'; },

    foo: function() { alert(this); }
};
Brian= {
    toString: function() { return 'Brian!'; },
};

Bob.foo(); // Bob!
Bob['foo'](); // Bob!

Brian.foo= Bob.foo;
Brian.foo(); // Brian! NOT Bob

var fn= Bob.foo;
fn(); // window NOT Bob

Что вы делаете в случае:

$j('#MyButton').click( Bob.doSomething );

аналогичен последнему примеру с fn: вы извлекаете функцию doSomething из Боба и передаете ее в установщик обработчика событий jQuery как чистую функцию: она больше не имеет никакого отношения к Бобу или любому другому объекту, поэтому Вместо этого JavaScript передает глобальный объект window. (Это одна из худших конструктивных особенностей JavaScript, поскольку вы, возможно, не сразу заметите, что window не Bob, и начнете обращаться к его свойствам, вызывая странные и запутанные взаимодействия и ошибки.)

Чтобы вспомнить Боба, вы обычно делаете функцию, как в ответе Никита, чтобы сохранить копию «Боба» в закрытии, чтобы ее можно было запомнить во время обратного вызова и использовать для вызова реального метода. Однако теперь есть стандартизированный способ сделать это в ECMAScript Fifth Edition:

$j('#MyButton').click( Bob.doSomething.bind(Bob) );

(Вы можете также добавить дополнительные аргументы в вызове bind для вызова doSomething обратно с ними, что удобно.)

Для браузеров, которые еще не поддерживают метод bind () пятого издания (который на данный момент является большинством из них), вы можете взломать собственную реализацию bind (библиотека Prototype также делает это) что-то вроде:

if (!Object.bind) {
    Function.prototype.bind= function(owner) {
        var that= this;
        var args= Array.prototype.slice.call(arguments, 1);
        return function() {
            return that.apply(owner,
                args.length===0? arguments : arguments.length===0? args :
                args.concat(Array.prototype.slice.call(arguments, 0))
            );
        };
    };
}
8 голосов
/ 09 октября 2009

Изменение

$('#MyButton').click( Bob.doSomething );

до

$('#MyButton').click( function() { Bob.doSomething() } );

Вы также можете добавить к своему объекту Bob личное поле var that = this и использовать его везде в членах вместо this, если действительно хотите избежать анонимной функции.

, например

var Bob = new function() {
    // Private Fields
    var that = this;

    // Public Members   
    this.Stuff = '';

    this.init = function() {
                that.Stuff = arguments[0];
        }

    this.doSomething = function() {
                console.log( that.Stuff );
        }
}
5 голосов
/ 09 октября 2009

Идентификация this является распространенной проблемой в JavaScript. Это также сломалось бы, если бы вы попытались создать ярлык для doSomething:

var do = Bob.doSomething;
do(); // this is no longer pointing to Bob!

Хорошей практикой является не полагаться на личность this. Вы можете сделать это различными способами, но проще всего явно указать Bob вместо this внутри doSomething. Другой - использовать функцию конструктора (но тогда вы потеряете классный объектно-литеральный синтаксис):

var createBob = function() {
    var that = {};

    that.Stuff = '';
    that.init = function() {
        that.Stuff = arguments[0];
    };

   that.doSomething = function() {
       console.log( that.Stuff );
   };

   return that;   
}

var bob = createBob();
0 голосов
/ 22 апреля 2010

Вы всегда можете попробовать сделать что-то вроде этого:

$j('#MyButton').click(function(event) {
  Bob.doSomething(event, this);
});

Теперь doSomething будет по-прежнему иметь Bob для своего this, и, используя аргументы функции, у него будет объект event и элемент, на котором он был запущен.

...