【问题标题】:Javascript Class + Properties not sticking to their valuesJavascript 类 + 属性不遵守其值
【发布时间】:2011-04-30 05:29:37
【问题描述】:

我正在尝试在 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 课程的新手,所以我不确定需要做什么,所以我希望有人可以帮助我。 :)

感谢您的宝贵时间!

【问题讨论】:

    标签: javascript oop properties


    【解决方案1】:

    嗯,这里有两个问题。 第一个问题是不了解this 在Javascript 中的工作原理。当通过setTimeout( oCallback, ...) 调用Foo.onData 时,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 作为调用对象。

    您可能面临的第二个问题是您使用setTimeout 调用的函数只会在1000 ms = 1s 之后执行,所以如果您只是在@987654337 之外检查alert(Foo.PubId) @ 你会得到一个空字符串(因为回调还没有被调用)。

    为了测试Foo.PubId是否确实改变了:

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

    您可以查看完整的测试用例here

    【讨论】:

    • 感谢您的帮助!其他所有人也是如此。大声笑希望我可以投票给你们所有人,但我想我必须是该网站的用户更长的时间才能做到这一点。无论如何,感谢您提供的解决方案 galambalazs。我不知道 Javascript 传递的是函数而不是方法。瘸!任何人,继续使用代码。 (那个 jsbin 网站也很酷!)
    【解决方案2】:

    当使用点表示法调用属于某个对象的方法时,其上下文(this 指向的位置)就是它所连接的对象。例如:

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

    但是,当您将对该函数的引用存储在不同的变量中时,它会失去上下文关系并且上下文成为全局对象:

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

    当执行一个方法时,你可以使用它的callapply 方法来指定你希望它执行的上下文:

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

    在您的特定示例中,我会考虑使用在较新版本的 JavaScript 中引入的名为 bind 的新方法。 bind 受 IE9、Firefox 4 和更新版本的 Google Chrome 和 Safari 支持,它允许您将功能“锁定”到特定上下文。您可以使用以下代码在不支持它的浏览器中实现其大部分功能(取自 Function.prototype.bind 的 MDC 文档):

    // 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) );
    

    【讨论】:

    • 感谢您的建议!我宁愿不依赖解决方法来让它与旧浏览器一起使用。 ://
    • @dab:这不是一个真正的“解决方法”,它只是一个功能较少的实现。这里不需要缺少的功能。对于您需要做的事情,bind() 将是更好的解决方案,因为您可以从参考点(调用 Init 方法)非常控制上下文。对于较新的浏览器,结果会更快,因为有原生解决方案。
    【解决方案3】:

    这是一个范围问题。当您将 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

    【讨论】:

      【解决方案4】:

      onData 与类分开执行。

      你必须这样打电话

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

      使其按预期工作。

      【讨论】:

      • 这是一个匿名函数,而不是闭包
      猜你喜欢
      • 1970-01-01
      • 2011-09-18
      • 1970-01-01
      • 2020-06-08
      • 2013-07-08
      • 2012-05-23
      • 1970-01-01
      • 1970-01-01
      • 2014-11-20
      相关资源
      最近更新 更多