4

Function are first class objects in JavaScript - but how to use that in combination with new?

I get how to make an object that acts both like a function and like an object

fetch.proxy = '127.0.0.1'
fetch(url)

But I am trying to figure out how I can combine this with creating a new intance so I can do something along the lines of:

a = new Fetch(host, username) 

a(path1) // fetches username@host/path1

a(path2) // fetches username@host/path2

(not so useful example with only 1 instance)

The following approach does not work as applying new returns an object (so get TypeError: object is not a function).

var Fetch = function(a, b){

    if ( (this instanceof arguments.callee) ){
        //called as constructor
        this.host = a
        this.username = b;
        this.host = 'localhost'

    } else {

   //called as function
   console.log("Fetching "+this.username+'@'+this.host+a);
    }
};

a = new Fetch('host', 'username') 

a('/path1') // TypeError: object is not a function

a('/path2') // TypeError: object is not a function

I have been playing with Fetch.prototype.constructor based on http://tobyho.com/2010/11/22/javascript-constructors-and/ and What it the significance of the Javascript constructor property? but must admit I have started to question if its possible at all.

Do you have any good ideas?

Community
  • 1
  • 1
mathiasrw
  • 610
  • 4
  • 10
  • Would you accept an `Factory` way instead of `New`? – fuyushimoya Jul 08 '15 at 10:58
  • 1
    i think you have some very good replies info here : http://stackoverflow.com/questions/367768/how-to-detect-if-a-function-is-called-as-constructor – fadomire Jul 08 '15 at 11:00
  • you may need to pass both arguments **(a,b)** else you can pass it by making object's key value so the no. of argument's will not make difference to you r code – Himesh Aadeshara Jul 08 '15 at 11:06

2 Answers2

2

how to use that in combination with new?

It doesn't make sense to do that. All that new Foo does is create a new object that inherits from Foo.prototype. However it is currently not possible to have some functions inherit from a prototype other than Function.prototype, so there is no advantage to use new Something to create a function.


Since you are not using prototype in your example, it doesn't matter though. Simply return a function from Fetch:

var Fetch = function(host, username){
  function fetch(path) {
     console.log("Fetching " + fetch.username + '@' + fetch.host + path);
  }
  fetch.username = username;
  fetch.host = host;
  return fetch;
};

Now you can call Fetch with or without new **, it doesn't make a difference:

var a = Fetch(host, username);
// same as
var a = new Fetch(host, username);

Note that I changed this.username to fetch.username in the above example. Every function has its own this value which does not refer to the function instance itself by default, so this.username would not work.

This would allow changing the host or username after the creation via a.username = 'something else';, etc. If you don't want that, simply change fetch.username to username.


**: If the function explicitly returns an object, then new will return that value instead of the newly created object inheriting from Fetch.prototype.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
0

If you want some kind of inheritance, you can ask your constructor to create and return a new function you'll prototype thanks to setProtototypeOf:

const Fetch = function (host, user)
{
  var fetch // new instance of function, relay to model
  
  fetch = function(path) {
    // note we use the closure on instance 'fetch'
    return Fetch.model.apply(fetch, arguments)
  }
  
  // 'setPrototypeOf' is not as borderline as '__proto__',
  // but it has some caveats anyway. Check for example MDN
  // about this.
  Object.setPrototypeOf(fetch, Fetch.model)

  if (host) fetch.host     = host
  if (user) fetch.username = user
  
  return fetch
}

// we name 'model' here; it also works if we 
// say 'prototype' instead.
// It would have been both more meaningful and more confusing at the
// same time, as the prototype would not get used the standard way.
Fetch.model = function (path)
{
  console.log("Fetching " + this.username + '@' + this.host + path)
}
Fetch.model.host     = 'localhost'
Fetch.model.username = 'peter'


var fetch = new Fetch // 'new' is not needed: can say '= Fetch()'
fetch.host = 'paradise'

fetch('/register')
See console output below
Champignac
  • 695
  • 7
  • 7