NodeJS:功能编程 - 无法访问切换功能中的原型

时间:2017-02-20 16:43:32

标签: javascript node.js promise

我尝试编写一个函数,它返回一些API(AgileCRMManager)的promise版本。 api的设计与请求非常相似。

但我在功能切换方面遇到了一些问题。函数无法访问自己的原型。我得到了以下日志输出:

[Function: getContactByEmail]
[Function: getContactByEmail]
TypeError: this.getOptions is not a function
    at getContactByEmail (/Users/Tilman/Documents/Programme/NodeJS/async_test/node_modules/agile_crm/agilecrm.js:116:24)
    at /Users/Tilman/Documents/Programme/NodeJS/async_test/routes/portal.js:30:5
    at restPromise (/Users/Tilman/Documents/Programme/NodeJS/async_test/routes/portal.js:29:10)
    at Object.<anonymous> (/Users/Tilman/Documents/Programme/NodeJS/async_test/routes/portal.js:22:1)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:456:32)
    at tryModuleLoad (module.js:415:12)
    at Function.Module._load (module.js:407:3)
    at Function.Module.runMain (module.js:575:10)

这是来自agile_crm的getOptions的部分:

ContactAPI.prototype.getOptions = function getOptions() {
    this._options = {
        host: this.domain,
        headers: {
            'Authorization': 'Basic ' + new Buffer(this.email + ':' + this.key).toString('base64'),
            'Accept': 'application/json'
        }
    };

    return this._options;
};

这是我的代码(如果我用a.contactAPI.getContactByEmail更改restFunction,它可以工作。但是我想让它用于更多功能):

var AgileCRMManager = require("agile_crm")
var a = new AgileCRMManager("user-domain",
        "api-key",
        "user-mail")

restPromise('ds@umzuege-selisch.de',a.contactAPI.getContactByEmail)
  .then(console.log)
  .catch(console.error)

function restPromise(data, restFunction) {
  console.log(restFunction);                    // => [Function: getContactByEmail]
  console.log(a.contactAPI.getContactByEmail);  // => [Function: getContactByEmail]
  return new Promise(function(fulfill, reject){
    //a.contactAPI.getContactByEmail(
    restFunction(
      data,
      function(data){
        fulfill(data)
      },
      function(error){
        reject(new Error(error))
      }
    )
  })
}

任何想法如何交出功能和api仍然可以工作?

1 个答案:

答案 0 :(得分:1)

你误解了this在javascript中的运作方式。通常情况下,我会投票支持这样的问题,但这似乎是一个需要进一步解释的特定用例。有关this如何运作的完整说明,我建议您阅读此问题的答案:How does the "this" keyword in Javascript act within an object literal?

理论

现在,问题是在js中this的值取决于你如何调用函数。如果函数是方法,this将解析为该方法所属的对象:

a.foo = function () { console.log(this) };
a.foo() // should output `a`

但是如果函数是一个不属于对象的简单函数,那么this可以是全局对象(浏览器中的窗口),也可以是未定义的,具体取决于严格模式。请注意,js解释天气或不是函数是调用函数时的方法。不是在定义函数时。这有一个有趣的后果:

a.foo = function () { console.log(this) };
b = a.foo;
b(); // here b is a simple function because it's not something.b
     // so the output is either the global object or undefined

The Bug

这是在这行代码中发生的事情:

restPromise('ds@umzuege-selisch.de',a.contactAPI.getContactByEmail)

您将a.contactAPI.getContactByEmail作为函数参考传递。因此它失去了它是a.contactAPI的一部分而变成一个简单函数的事实。通过如何调用函数可以清楚地看到这一事实:

restFunction( //...

请注意,您正在调用restFunction,这是a.contactAPI.getContactByEmail的函数引用。由于它被简单地称为restFunction()而不是a.contactAPI.restFunction(),因此函数中的this指向全局对象(或取决于严格模式而未定义),而不是a.contactAPI

修复

有几种方法可以解决这个问题。

  1. 在匿名函数中包装a.contactAPI.getContactByEmail,以便您可以使用正确的this调用它:

    restPromise(
      'ds@umzuege-selisch.de',
      function(data,ok,err){
        a.contactAPI.getContactByEmail(data,ok,err);
      }
    )
    
  2. 使用.bind()

    restPromise(
      'ds@umzuege-selisch.de',
      a.contactAPI.getContactByEmail.bind(a.contactAPI)
    )
    
  3. 修改restPromise()以接受对象而不是函数。