Ben Aston
Ben Aston

Reputation: 55769

Is it possible to make a callable object non-callable?

In JavaScript, functions are callable.

Can I remove this attribute from a function, leaving only a normal object?

var foo = function () {};
foo.[[callable]] = false; // pseudocode
foo(); // "foo is not a function"

Upvotes: 4

Views: 1667

Answers (4)

Bergi
Bergi

Reputation: 665121

No. Functions (the evaluation behaviour, not their object properties) are pretty much immutable.

The callability of objects is determined by whether they have a [[Call]] slot. The spec says about those:

Internal slots are allocated as part of the process of creating an object and may not be dynamically added to an object.

(and it is implied that they cannot dynamically be removed either). Furthermore, “internal methods are not part of the ECMAScript language” and “are not object properties”, which means they cannot be directly accessed, set, deleted or manipulated in any imaginable way by the means of the language.

[[Call]] slots are part of various objects, where they always contain an internal method, but they are never mutated except for their initialisation (once). They come in various kinds:

As you can see, there is no way to alter or even remove the [[Call]] slot of an object, not even on proxies. Your best bet is

  • make the function throw when called in an inopportune state

  • create a new, uncallable object from the function by means of

    var obj = Object.assign(Object.create(Object.getPrototypeOf(fn)), fn);
    

Upvotes: 12

Peter Seliger
Peter Seliger

Reputation: 13432

I like providing an additional point of view to what Bergi already did mention, also encouraged by this answer within the OP's question.

For what purpose? – user3749178 Apr 16 at 16:18

.

@user3749178 Curiosity. – Ben Aston Apr 16 at 16:19

besides var obj = Object.assign(Object.create(Object.getPrototypeOf(fn)), fn);, that depends on ES6, there is an ES3 compatible approach that does wrap function objects into callable objects. Thus those objects are not callable anymore by the call operator (), but such objects do still expose two call methods call and apply. Since this approach prevents on one hand function objects being invoked by () (methods/functions) or by the new operator (instantiation), on the other hand it still supports delegation (function based mixins/traits).

the gist hosted code of Function.toApplicator

Upvotes: 0

Jose Ricardo Bustos M.
Jose Ricardo Bustos M.

Reputation: 8174

this isn't an elegant solution, the idea is to create the "enabled" attribute as a flag to know if the function is enabled:

var foo = function () { 
  if(!arguments.callee.enabled){
    throw "function disabled";
  }
  console.log("Hello");
}

foo.enabled = true;
foo();
foo.enabled = false;
foo();

Upvotes: -2

Jordan Cortes
Jordan Cortes

Reputation: 281

As far as I know you can't.

The functions have a property called call() which calls the body of the function (sorry for the redundancy).

You can set this property to any expression like for example

foo.call = console.log('foo not a function');

but that will not prevent the function's body to be called.

And since you cannot set it to a function, you can't use the event.preventDefault() function.

Upvotes: 0

Related Questions