Javascript Class + Свойства не придерживаются своих значений - PullRequest
3 голосов
/ 25 октября 2010

Я пытаюсь сделать класс в JavaScript. Я создаю его с помощью объекта типа JSON.

Делаем это:

Foo = {
   PubId: '',

   Init:function( oCallback )
   {
         this.sendCommand( 'INIT', {}, oCallback );
   },

    sendCommand : function( sCommand, aParams, oCallback )
    {

        setTimeout( oCallback, 1000, '{"response":"INIT","time":1287982024,"pubid":"4cc50bc47c7b3"}' );

        return true;
    },
    onData : function( sData )
    {
        var aRes = JSON.parse( sData );

        this.PubId = aRes.pubid;
        alert( this.PubId );
        return this.PubId;
    },
    umtest:function(){ alert( this.PubId ); }
}

Затем я делаю это после включения скрипта:

Foo.Init( Foo.onData ); 

Проблема в том, что this.PubId обновляется внутри метода onData, но вне него pubid пуст.

Я довольно новичок в классах javascript, поэтому я не уверен, что нужно делать, поэтому я надеялся, что кто-нибудь сможет мне помочь. :)

Спасибо за ваше время!

Ответы [ 4 ]

2 голосов
/ 25 октября 2010

Ну, у вас здесь две проблемы. Проблема first не в понимании того, как this работает в Javascript. Когда Foo.onData вызывается через setTimeout( oCallback, ...), this будет ссылаться на глобальный объект, а не Foo.

Чтобы назвать его с Foo как this, вы должны изменить свой код:

sendCommand: function (sCommand, aParams, oCallback) {
    var that = this;           // save this reference
    setTimeout(function () {
        oCallback.call( that,  // call the function with it
               '{"response":"INIT","time":1287982024,"pubid":"4cc50bc47c7b3"}' );
    }, 1000);
    return true;
},

Чтобы проверить, что изменилось, поместите этот код в onData:

// is `this` really Foo or the global object?
alert(this === Foo);    // should be true
alert(this === window); // should be false

В обновленной версии this будет правильно ссылаться на Foo как объект вызова.

Проблема second , с которой вы можете столкнуться, заключается в том, что ваша функция, вызванная с setTimeout, будет выполняться только после 1000 ms = 1s, поэтому, если вы просто проверяете alert(Foo.PubId) вне Foo вы получите пустую строку (потому что обратный вызов еще не был вызван).

Чтобы проверить, действительно ли Foo.PubId изменено:

// execute the check after 2s so as to
// make sure the callback has been called
setTimeout( function () {
  alert(Foo.PubId);
}, 2000);

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

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

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

// Context: `this` is `myObj`
myObj.myMethod();

Однако, когда вы сохраняете ссылку на эту функцию в другой переменной, она теряет эту контекстную связь, и контекст становится глобальным объектом:

// Context: `this` is the global object - `window` in a web browser
var myMethod = myObj.myMethod;
myMethod();

Когдавыполняя метод, вы можете использовать его метод call или apply, чтобы указать контекст, в котором вы хотели бы его выполнить:в новых версиях JavaScript с именем bind.bind поддерживается IE9, Firefox 4 и более новыми версиями Google Chrome и Safari и позволяет «привязывать» функцию к определенному контексту.Вы можете реализовать большую часть его функций в браузерах, которые не поддерживают его, используя следующий код (взят из документации MDC для Function.prototype.bind ):

// PARTIAL WORKAROUND for Function.prototype.bind  
if (!Function.prototype.bind)  
    Function.prototype.bind = function(context /*, arg1, arg2... */) {  
        'use strict';  
        if (typeof this !== 'function') throw new TypeError();  
        var _slice = Array.prototype.slice,  
            _concat = Array.prototype.concat,  
            _arguments = _slice.call(arguments, 1),  
            _this = this,  
            _function = function() {  
                return _this.apply(this instanceof _dummy ? this : context,  
                    _concat.call(_arguments, _slice.call(arguments, 0)));  
            },  
            _dummy = function() {};  
        _dummy.prototype = _this.prototype;  
        _function.prototype = new _dummy();  
        return _function;  
};  

Использовать его в своем коде очень просто:

Foo.Init( Foo.onData.bind(Foo) );
0 голосов
/ 25 октября 2010

Это проблема объема. Когда вы передаете Foo.onData вашей функции init, она «теряет» связь с классом. Функции являются объектами первого класса, поэтому они могут существовать сами по себе. Вы можете сделать следующее:

Foo = {
   PubId: '',

   Init:function(oCallback, scope)
   {
         this.sendCommand('INIT', {}, oCallback, scope);
   },

    sendCommand : function(sCommand, aParams, oCallback, scope)
    {
        var cb = oCallback;
        if(scope) {
            cb = function(data) {
               oCallback.call(scope, data); 
            };
        }

        setTimeout( cb, 1000, '{"response":"INIT","time":1287982024,"pubid":"4cc50bc47c7b3"}' );

        return true;
    }
    //...
}

, а затем позвоните с

Foo.Init(Foo.onData, Foo);

Первый параметр, который передается в call(), станет this внутри метода.

0 голосов
/ 25 октября 2010

onData выполняется отдельно от класса.

Вы должны позвонить, как это

Foo.Init(function(d) { Foo.onData(d);} ); 

, чтобы это работало как положено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...