更新'这'当返回用.apply()调用的函数时,从回调

Update 'this' from callback when returning function called with .apply()

本文关键字:函数 回调 调用 apply 返回 更新      更新时间:2023-09-26

我有一个ProjectClient类,它包含一个用于进行HTTP调用的方法(.GET())。它以与节点请求相同的方式支持一些参数和回调,但也有一些复杂的url和头构建功能,这些功能也发生在幕后:

client.GET(idOrDataObject, function(err, customResponse) {
    // url is built based on the id or dataObject passed as the first param
})

成功响应后,返回的对象类型之一是DataObject:

client.GET(idOrDataObject, function(err, customResponse) {
    console.log(customResponse.dataObject instanceof DataObject) // true
})

我正在为DataObject类添加一个名为reload()的方便方法,该方法调用回client.GET()方法并重新加载自己,但我希望能够使用从服务器返回的DataObject的新版本更新this

DataObject.prototype.reload = function() {
    var args = Array.prototype.slice.call(arguments) // extracts all arguments
    var client = helpers.client.validate(args) // ensures 'client' was passed in the args array before continuing
    args.unshift(this) // prepends 'this' (the DataObject instance)
    // How can I update 'this' with the response contained in the 
    // callback passed (the last element in 'args')?
    return client.GET.apply(client, args)
}

用法如下:

client.GET(idOrDataObject, function(err, customResponse) {
    var o = customResponse.dataObject
    // assume something changed on the server
    o.reload(function(err, done) {
         // o has now been updated with the latest copy from the server
    })
})

更新:

我开始认为,唯一可行的方法是,如果我劫持回调,例如在client.GET:内部

var DataObject = require('../lib/dataObject')
var request = require('request')
var _ = require('lodash')
var GET = function(client, args) {
    var options = {
        client: client
    }
    // if a preformatted options argument passed, assign it to options
    if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) {
        _.assign(options, args[0])
    }
    options.callback = helpers.cb(_.last(args.filter(_.isFunction)))
    options.type = _.first([options.type, args[0]._type, args[0]].filter(_.isString))
    options.dataObject = _.first([options.dataObject, args[0]].filter(function(property) {
        return (property instanceof DataObject)
    }))
    request('http://...', {
        body: options.body,
        json: true,
        method: 'GET'
    }, function(error, response) {
        var customResponse = new CustomResponse(response)
        if (options.dataObject instanceof DataObject) {
            options.dataObject = customResponse.dataObject
            // here I see both 'options.dataObject' and 'customResponse.dataObject' have the correct value for reloadTest
            console.log('updated', options.dataObject.reloadTest, customResponse.dataObject.reloadTest)
        }
        options.callback(error, customResponse, customResponse.dataObject)
    })
}

但即使这样做,dataObject的原始副本也没有被更新——就好像它被克隆或复制了,不是指向原始副本的指针吗?

这是一个证明失败的测试。如何确保传递指向dataObject的正确指针?

var now = Date.now()
client.GET('test', function(err, getResponse) {
    var dataObject = new DataObject(getResponse.dataObject)
    getResponse.dataObject.reloadTest = now // 1452109996140
    console.log('now', now, 'getResponse.dataObject.reloadTest', getResponse.dataObject.reloadTest)
    // now 1452109996140 getResponse.dataObject.reloadTest 1452109996140
    client.PUT(getResponse.dataObject, function(err, putResponse) {
        // updated 1452109996140 1452109996140
        console.log('putResponse.dataObject.reloadTest', putResponse.dataObject.reloadTest)
        // putResponse.dataObject.reloadTest 1452109996140
        dataObject.reload(function(err, response) {
            // updated 1452109996140 1452109996140
            console.log('done', dataObject.reloadTest, 'response.dataObject.reloadTest', response.dataObject.reloadTest)
            // done 1452109916111 response.dataobject.reloadTest 1452109996140
        })
    })
})

简单的答案是,你不能。您实际上无法将该对象交换为另一个对象。一些选项:

  • 使用一个包装器对象,并给出reload方法:

    client.GET(idOrDataObject, function(err, customResponse) {
        var o = new DataWrapper(customResponse);
        o.dataObject; // old data
        // assume something changed on the server
        o.reload(function(err, done) {
             o.dataObject; // new data
        });
    });
    
  • 更新dataObject中的数据,保持与以前相同的对象:

    DataObject.prototype.reload = function(callback) {
        client.GET(this, function(err, customResponse) {
            // Assign all properties from the response to the existing obj
            // Could also use $.extend, _.extend, etc
            Object.assign(this, customResponse.dataObject);
            callback(this);
        }.bind(this));
    }
    
  • 只需在回调中传递新对象并忽略旧对象:

    DataObject.prototype.reload = function(callback) {
        client.GET(this, function(err, customResponse) {
            callback(customResponse.dataObject);
        });
    }