1

I would like to add a method to a function expression as simply as possible. In my case, I use a function expression, but a function declaration would also be ok. Since functions are objects, I guess it is possible. But how?


Details:

I want to add the method as simply and as directly as possible, so I neither want to use Constructor functions nor inheritance.

The following code adds a method (setListener) to a function (secondsEvent) without Constructors and inheritance, but not within the anonymous function expression, which I would like to achieve somehow (see comments):

function listener1 (date) {
    console.log("listener1: " + date);
}

function listener2 (date) {
    console.log("listener2: " + date);
}

var secondsEvent = function () {
    //The following causes "listener is not defined" in firefox console                                                      
    //listener: listener1,                                                                                                   

    //The following causes "function statement requires a name" in firefox console,                                          
    //and if I give the function a name, I get "secondsEvent.setListener is not a function".                                  
    //Thus I add the property after this function expression.                                                                   
    //setListener: function (newListener) {                                                                                  
    //    listener = newListener;                                                                                            
    //},                                                                                                                     
    secondsEvent.listener(new Date());
    var timeout = setTimeout(secondsEvent, 1000);
}

// Thus the following 5 lines are needed.                                                                                    
secondsEvent.listener = listener1;
secondsEvent.setListener = function (newListener) {
    secondsEvent.listener = newListener;
    console.log("listener now " + secondsEvent.listener.name);
}

secondsEvent();
secondsEvent.setListener(listener2);

The code yields the following output:

listener1: Fri Dec 28 2018 21:55:05 GMT+0100 (Central European Standard Time) 
listener now listener2 
listener2: Fri Dec 28 2018 21:55:06 GMT+0100 (Central European Standard Time) 
listener2: Fri Dec 28 2018 21:55:07 GMT+0100 (Central European Standard Time) 
.....

Updates:

Originally, I used Douglas Crockford's term "function literal" instead of function expression, but updated the title and question now.

In response to Teemu's suggestion, I tried the following code:

function listener1 (date) {
    console.log("listener1: "+date + "<br>");
}

function listener2 (date) {
    console.log("listener2: "+date + "<br>");
}

var secondsEvent = function () {
    secondsEvent.listener = listener1;
    secondsEvent.setListener = function (newListener) {
        secondsEvent.listener = newListener;
        console.log("listener now " + secondsEvent.listener.name);
    }
    clearTimeout(secondsEvent.timeout);
    secondsEvent.listener(new Date());
    secondsEvent.timeout = setTimeout(secondsEvent, 1000);
};

secondsEvent();
secondsEvent.setListener(listener2);

However, its output is the following:

listener1: Sat Dec 29 2018 10:47:48 GMT+0100 (Central European Standard Time)<br> test.js:2:5
listener now listener2 test.js:27:2
listener1: Sat Dec 29 2018 10:47:49 GMT+0100 (Central European Standard Time)<br> test.js:2:5
listener1: Sat Dec 29 2018 10:47:50 GMT+0100 (Central European Standard Time)<br>

To avoid setting the properties secondsEvent.listener and secondsEvent.setListener each time secondsEvent is called, I use the following:

secondsEvent.listener = secondsEvent.listener || listener1;
secondsEvent.setListener = secondsEvent.setListener || function (newListener) {
    secondsEvent.listener = newListener;
    console.log("listener now " + secondsEvent.listener.name);
}

Is there a nicer solution for setting the properties only once? Maybe some js initializer idiom? (I know that pulling this out of the function would be another solution, but that leads to my first solution which I want to avoid since it would be much nicer to include the property definitions within the function expression (or function declaration, whichever you use), before they are used.

7
  • The name of a declared function is available inside of that function, hence simply in the function body, do: FUNCTION_NAME.METHOD_NAME = function () {...};. Though I've no clue, how the provided code in the post is relevant to the question ..? Commented Dec 28, 2018 at 21:07
  • @Teemu, thanks for the advice, I will try it out. The code I provided is relevant to the question since it shows how I add a method to the function and the comments show how I tried it within the function literal. Commented Dec 28, 2018 at 21:10
  • 2
    "function literal" is a bit confusing term because such thing doesn't exist in standard JS terminology. With the term, do you mean a declared function, or a function expression? Notice also, that document.write(ln) easily leads to reference errors, since it wipes all the previous code out from the document, and creates a new document, if called after the page has been parsed. Use console methods for getting info and debugging. Commented Dec 28, 2018 at 21:12
  • I mean "function literal" as in JS The good parts page 19 (oreilly.com/library/view/javascript-the-good/9780596517748/…): I would like to add the method within the declaration of secondsEvent, as I have commented in my code. Commented Dec 28, 2018 at 21:23
  • 1
    Ah .. Mr Crockford has written that article for more than a decade ago ... Nowadays that's "officially" an "anonymous function expression". Notice, that "the second part" of the "function literal" is missing in the example of the article. Commented Dec 28, 2018 at 21:29

3 Answers 3

2

You speak of a "function literal", but there is no such thing in JavaScript. We do speak of a function expression though.

One of the things you could consider is using Object.assign which will allow an object (provided as a literal) to be merged into a (function) object:

function listener1 (date) {
    console.log("listener1: "+date);
}

function listener2 (date) {
    console.log("listener2: "+date);
}

var secondsEvent = Object.assign(function () {
    clearTimeout(secondsEvent.timeout);
    secondsEvent.listener(new Date());
    secondsEvent.timeout = setTimeout(secondsEvent, 1000);
}, {
    listener: listener1,
    setListener(newListener) {
        this.listener = newListener;
        console.log("listener now " + this.listener.name);
    }
});

secondsEvent();
secondsEvent.setListener(listener2);

The Traditional Alternative

What follows is not what you asked, but I provide it anyway as a comparison.

The more traditional way to have behaviour and data combined is by starting with an object. Here we do not extend a function with extra properties; the function becomes a method of the object that has the properties:

function listener1 (date) {
    console.log("listener1: "+date);
}

function listener2 (date) {
    console.log("listener2: "+date);
}

var secondsEvent = {
    listener: listener1,
    start() {
        clearTimeout(this.timeout);
        this.listener(new Date());
        this.timeout = setTimeout(() => this.start(), 1000);
    },
    setListener(newListener) {
        this.listener = newListener;
        console.log("listener now " + this.listener.name);
    }
};

secondsEvent.start(); // secondsEvent is not a function. We call a method
secondsEvent.setListener(listener2);

Altough here you need to call the function with .start(), it has as advantage that it looks and works like most other objects: the users of this API will not be surprised.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, trincot, that is a good solution (+1). Btw, I have updated my question to avoid the term "function literal".
Works like a charm, trincot. I do not understand though why you use this.listener.name instead of secondsEvent.listener.name in setListener (which also works in my browser). Isn't it better to avoid this where possible?
It is a matter of opinion whether this should be avoided. Choose what you are comfortable with. ;-) But I see I was not consistent in using it. Made a small update. The advantage of using this is that the function setListener could be borrowed to work on another object. But from a class-methods kind of thinking that could sound really weird and undesirable.
As comparison I added how the interface would be made in a more standard, traditional way.
0

The syntax you are using looks like object or more suitably class syntax. Its not very clear from your question whether you are trying to avoid classes. Though you mention wanting to avoid constructors, so i'll assume you mean you dont want to use classes.

You can have methods in functions

function methodFunction () {
   return {
       myMethod (argument) {
           if (argument) console.log(argument)
           return new Date()
       }
   }
}

methodFunction().myMethod('heres something i want to pass my function method')
// will console.log heres something i want to pass my function method
console.log(methodFunction().myMethod())
// will console.log the date as it is returned from the function method

Here the first time we call the function we pass in the string 'heres something i want to pass my function method' which we log back out from within the function method where it comes in as the argument 'argument'

the second time we dont pass a string so nothing is console logged as a side effect of the function and instead the returned new Date() is console logged as the return value from the function

3 Comments

Is myMethod really a method of methodFunction? It looks like a returned function object to me.
Thats generally how you would add a method to a function. Its worth noting that es6 arrow functions don't even have a 'this'. You also said you wanted to avoid constructors. The good parts is a great exploration of the language but if you're just getting started theres plenty to keep yourself busy with before you start exploring the intricacies of abusing types for things they're not best suited for. The way you're calling those methods would ultimately best lend itself to using classes.
You are right, I am only getting started in JS. The situation is: I have a function (secondsEvent) and a bit of state (the current listener), which I do not want to store in a global variable, but as close to secondsEvent as possible. Hence I used a property and a setter. What would be a better pattern in JS?
0

I'm answering only to the very first part of the question and ignoring the "use case". There's an old, and a bit worn phrase: "Everything in JS is object." Yes, everything, but primitives, would be more correct.

But functions, they really are objects, callable objects. That means these function objects can have properties, either in form of values, or methods. The original question was: "[How] to add a method to a function [object]".

The answer is quite simple, get a reference to the function, and add a property (a method is a property as well) to the referred function, just like you'd do when you're adding a property to a regular object, and that's it. Or is it? There are several situations where a property is wanted to be assigned to a function, and that function could've been allocated to the memory in different ways. Now the question can be boiled down to "How to get a reference to a function at the point it is needed?"

There are three different main ways to define a function:

  1. declared function (a declared function always has a name) [fn1]
  2. a named function expression (can be assigned to a variable or used elsewhere) [fn2]
  3. an anonymous function expression (can be assigned to a variable or used elsewhere) [fn3]

#1 is the easiest case, you can refer a declared function anywhere in the current scope by using its name, including the function body itself. Thus you can create a method for this function by doing fn1.method = function () {...} inside the fn1s body, or outside of the function using the same notation.

#2 you can use the the given name only in the function body. If you need to assign a method outside of this function, use the name of the variable, or in the case the function was passed as an argument to another function, refer fn2 with the name of the argument.

#3 doesn't provide any reference to the function itself inside of the body of the function. However, if this function is assigned to a variable, the variable name can be used to refer the function itself inside the function's body, provided the function is not executed as an IIFE. Outside of the function body, the name of the variable can be used to refer the function as soon as the variable has been initilized.

What comes to this value in the function methods, you can't trust it referring the "method owning" function. If you need to refer this in the methods, it's better to use real objects instead of functions.

As a side note, using properties of function objects is not a regular use case in JS. Usually real objects are used instead. A general example of taking the advanage of the properties of a function object is jQuery's $, which actually is a function, but has also some methods and properties.

1 Comment

Thank you very much for the explanation, Teemu. I have added an update at the end of my question to apply your suggestion to my case. But that lead to a question about initialization, which I have stated there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.