Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoking a function without parentheses

Tags:

javascript

I was told today that it's possible to invoke a function without parentheses. The only ways I could think of was using functions like apply or call.

f.apply(this); f.call(this); 

But these require parentheses on apply and call leaving us at square one. I also considered the idea of passing the function to some sort of event handler such as setTimeout:

setTimeout(f, 500); 

But then the question becomes "how do you invoke setTimeout without parentheses?"

So what's the solution to this riddle? How can you invoke a function in Javascript without using parentheses?

like image 356
Mike Cluck Avatar asked Mar 11 '16 20:03

Mike Cluck


People also ask

Can I call function without parentheses in JavaScript?

Calling a JavaScript function without the parens passes that function's definition as a reference. It is one of the fundamentals to programming in JavaScript known as callbacks.

Which method does not require the use of parentheses?

Attributes and properties do not require parentheses. Functions and methods require parentheses.

Why do functions have parentheses?

Parentheses have multiple functions relating to functions and structures. They are used to contain a list of parameters passed to functions and control structures and they are used to group expressions to control the order of execution.

What is the difference between calling function with parentheses and without in JavaScript?

With parenthesis the method is invoked because of the parenthesis, the result of that invocation will be stored in before_add. Without the parenthesis you store a reference (or "pointer" if you will) to the function in the variable. edit: Added as answer which should be more appropriate. Does this answer your question?


1 Answers

There are several different ways to call a function without parentheses.

Let's assume you have this function defined:

function greet() {     console.log('hello'); } 

Then here follow some ways to call greet without parentheses:

1. As Constructor

With new you can invoke a function without parentheses:

new greet; // parentheses are optional in this construct. 

From MDN on the new oprator:

Syntax

new constructor[([arguments])] 

2. As toString or valueOf Implementation

toString and valueOf are special methods: they get called implicitly when a conversion is necessary:

var obj = {     toString: function() {          return 'hello';     } }  '' + obj; // concatenation forces cast to string and call to toString. 

You could (ab)use this pattern to call greet without parentheses:

'' + { toString: greet }; 

Or with valueOf:

+{ valueOf: greet }; 

valueOf and toString are in fact called from the @@toPrimitive method (since ES6), and so you can also implement that method:

+{ [Symbol.toPrimitive]: greet } "" + { [Symbol.toPrimitive]: greet } 

2.b Overriding valueOf in Function Prototype

You could take the previous idea to override the valueOf method on the Function prototype:

Function.prototype.valueOf = function() {     this.call(this);     // Optional improvement: avoid `NaN` issues when used in expressions.     return 0;  }; 

Once you have done that, you can write:

+greet; 

And although there are parentheses involved down the line, the actual triggering invocation has no parentheses. See more about this in the blog "Calling methods in JavaScript, without really calling them"

3. As Generator

You could define a generator function (with *), which returns an iterator. You can call it using the spread syntax or with the for...of syntax.

First we need a generator variant of the original greet function:

function* greet_gen() {     console.log('hello'); } 

And then we call it without parentheses by defining the @@iterator method:

[...{ [Symbol.iterator]: greet_gen }]; 

Normally generators would have a yield keyword somewhere, but it is not needed for the function to get called.

The last statement invokes the function, but that could also be done with destructuring:

[,] = { [Symbol.iterator]: greet_gen }; 

or a for ... of construct, but it has parentheses of its own:

for ({} of { [Symbol.iterator]: greet_gen }); 

Note that you can do the above with the original greet function as well, but it will trigger an exception in the process, after greet has been executed (tested on FF and Chrome). You could manage the exception with a try...catch block.

4. As Getter

@jehna1 has a full answer on this, so give him credit. Here is a way to call a function parentheses-less on the global scope, avoiding the deprecated __defineGetter__ method. It uses Object.defineProperty instead.

We need to create a variant of the original greet function for this:

Object.defineProperty(window, 'greet_get', { get: greet }); 

And then:

greet_get; 

Replace window with whatever your global object is.

You could call the original greet function without leaving a trace on the global object like this:

Object.defineProperty({}, 'greet', { get: greet }).greet; 

But one could argue we do have parentheses here (although they are not involved in the actual invocation).

5. As Tag Function

Since ES6 you can call a function passing it a template literal with this syntax:

greet``; 

See "Tagged Template Literals".

6. As Proxy Handler

Since ES6, you can define a proxy:

var proxy = new Proxy({}, { get: greet } ); 

And then reading any property value will invoke greet:

proxy._; // even if property not defined, it still triggers greet 

There are many variations of this. One more example:

var proxy = new Proxy({}, { has: greet } );  1 in proxy; // triggers greet 

7. As instance checker

The instanceof operator executes the @@hasInstance method on the second operand, when defined:

1 instanceof { [Symbol.hasInstance]: greet } // triggers greet 
like image 111
trincot Avatar answered Sep 27 '22 22:09

trincot