简体   繁体   中英

NodeJS: Functional Programming - No access to prototype in handover function

I try to write a function, that returns a promise version for some API (AgileCRMManager). The design of the api works pretty similar to request.

But i have some Problems with the handover of the Function. The Function have no access to the prototype of its own. I got following log output:

[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)

This is the part with getOptions from agile_crm:

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;
};

This is my Code (if i change restFunction with a.contactAPI.getContactByEmail, it works. But i want to have it for more functions):

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))
      }
    )
  })
}

Any Idea how i can hand Over the function and api will still work?

You've misunderstood how this works in javascript. Normally I'd vote to close questions like this but this seems to be a specific use case that requires further explanation. For a full explanation of how this works I'd suggest reading the answer from this question: How does the "this" keyword in Javascript act within an object literal? .

The Theory

Now, the problem is that in js the value of this depends on how you call a function. If a function is a method, this resolves to the object the method belongs to:

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

But if the function is a simple function that is not part of an object then this is either the global object (window in browsers) or undefined depending on strict mode. Note that js interprets weather or not a function is a method when you call the function. Not when you define the function. This has interesting consequences:

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

This is what happens in this line of code:

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

You're passing a.contactAPI.getContactByEmail as a function reference. Therefore it loses the fact that it's part of a.contactAPI and instead becomes a simple function. This fact can be clearly seen by how you call the function:

restFunction( //...

Note that you're calling restFunction which is a function reference to a.contactAPI.getContactByEmail . Since it is called simply as restFunction() instead of a.contactAPI.restFunction() the this in the function points to the global object (or undefined depending on strict mode) instead of a.contactAPI .

Fixes

There are several ways you can fix this.

  1. Wrap a.contactAPI.getContactByEmail in an anonymous function so you can call it with the proper this :

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

     restPromise( 'ds@umzuege-selisch.de', a.contactAPI.getContactByEmail.bind(a.contactAPI) ) 
  3. Modify restPromise() to accept an object instead of a function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM