140

ES6 allows to extend special objects. So it's possible to inherit from the function. Such object can be called as a function, but how can I implement the logic for such call?

class Smth extends Function {
  constructor (x) {
    // What should be done here
    super();
  }
}

(new Smth(256))() // to get 256 at this call?

Any method of class gets reference to the class instance via this. But when it is called as a function, this refers to window. How can I get the reference to the class instance when it is called as a function?

PS: Same question in Russian.

Alexander O'Mara
  • 58,688
  • 18
  • 163
  • 171
Qwertiy
  • 19,681
  • 15
  • 61
  • 128
  • 34
    Ah, finally someone asked this queston :-) – Bergi Apr 26 '16 at 17:06
  • 1
    Just do `super(x)` (i.e. pass it along to `Function`)? Not sure if `Function` can actually be extended though. – Felix Kling Apr 26 '16 at 17:07
  • Bear in mind that there are still problems with extending built-in classes. The spec suggests that should be possible, but I've run into problems extending `Error`, among others. – ssube Apr 26 '16 at 17:08
  • [Chat discussion](//chat.stackexchange.com/rooms/38918/discussion-between-qwertiy-and-grundy) is currently in russian, but we can switch english there. – Qwertiy Apr 26 '16 at 17:09
  • 1
    Keep in mind that `Function` is simply a function constructor. The implementation of the function has to be passed to the constructor. If you don't want `Smth` to accept an implementation, you have to provide it in the constructor, i.e. `super('function implementation here')`. – Felix Kling Apr 26 '16 at 17:11
  • See [How to access the correct `this` / context inside a callback?](http://stackoverflow.com/q/20279484/218196) regarding your edit. – Felix Kling Apr 26 '16 at 17:22
  • @FelixKling, I know how `this` works. How is it related to class declaration? – Qwertiy Apr 26 '16 at 17:24
  • Well, you have to ensure that your the implementation of your function has a reference to the class instance. Inside the constructor, `this` refers to that instance. The linked question describes a couple of solutions of how to refer to that `this` value inside another function (i.e. your function implementation). – Felix Kling Apr 26 '16 at 17:26
  • But the constructed instance is the same object that I'm calling as function. It doesn't seem reasonable to use `call` every time - in that case I just don't need to extend fucntion. – Qwertiy Apr 26 '16 at 17:28
  • *"But the constructed instance is the same object that I'm calling as function."* Really depends on your implementation. If you are using `super('....')` then yes. If you are using Bergi's suggestion with a closure, then no. If you go with the first solution, then all you can do is `return this.bind(this);` from the constructor. But it would actually be nice to know which problem you are trying to solve with this. In general, functions don't have a reference to themselves... – Felix Kling Apr 26 '16 at 17:31
  • @FelixKling, they do if function expression is named: `var any = function f() { /* f is self-reference for the function */ }`. – Qwertiy Apr 26 '16 at 17:33
  • 1
    @Qwertiy: I would argue that this is the *exception*, not the general case. This is also very specific to *function expressions*, but you are using the `Function` constructor (runtime) which is very different from a function expression (syntax). – Felix Kling Apr 26 '16 at 17:35
  • Personally I think that whatever you trying to solve is made more complicated by are trying to use `class ... extends Function`. A simple factory function which produces functions would probably solve your issue too. I mean, why do you need to implement a class? Why do you need multiple instances of `Smth`? – Felix Kling Apr 26 '16 at 17:37
  • @AlexanderO'Mara, цhy there is a special tag [tag:javascript-inheritance] with only 2 questions? Can you tell about it in the [chat](//chat.stackexchange.com/rooms/38918/discussion-between-qwertiy-and-grundy)? – Qwertiy Apr 26 '16 at 20:05

12 Answers12

76

The super call will invoke the Function constructor, which expects a code string. If you want to access your instance data, you could just hardcode it:

class Smth extends Function {
  constructor(x) {
    super("return "+JSON.stringify(x)+";");
  }
}

but that's not really satisfying. We want to use a closure.

Having the returned function be a closure that can access your instance variables is possible, but not easy. The good thing is that you don't have to call super if you don't want to - you still can return arbitrary objects from your ES6 class constructors. In this case, we'd do

class Smth extends Function {
  constructor(x) {
    // refer to `smth` instead of `this`
    function smth() { return x; };
    Object.setPrototypeOf(smth, Smth.prototype);
    return smth;
  }
}

But we can do even better, and abstract this thing out of Smth:

class ExtensibleFunction extends Function {
  constructor(f) {
    return Object.setPrototypeOf(f, new.target.prototype);
  }
}

class Smth extends ExtensibleFunction {
  constructor(x) {
    super(function() { return x; }); // closure
    // console.log(this); // function() { return x; }
    // console.log(this.prototype); // {constructor: …}
  }
}
class Anth extends ExtensibleFunction {
  constructor(x) {
    super(() => { return this.x; }); // arrow function, no prototype object created
    this.x = x;
  }
}
class Evth extends ExtensibleFunction {
  constructor(x) {
    super(function f() { return f.x; }); // named function
    this.x = x;
  }
}

Admittedly, this creates an additional level of indirection in the inheritance chain, but that's not necessarily a bad thing (you can extend it instead of the native Function). If you want to avoid it, use

function ExtensibleFunction(f) {
  return Object.setPrototypeOf(f, new.target.prototype);
}
ExtensibleFunction.prototype = Function.prototype;

but notice that Smth will not dynamically inherit static Function properties.

PJ Eby
  • 8,760
  • 5
  • 23
  • 19
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I want to get access to class state from the function. – Qwertiy Apr 26 '16 at 17:13
  • 3
    @Qwertiy: Then use Bergi's second suggestion. – Felix Kling Apr 26 '16 at 17:15
  • @AlexanderO'Mara: You don't get around mutating the prototype of the function if you want your `Smth` instances to be `instanceof Smth` (as everyone would expect). You can omit the `Object.setPrototypeOf` call if you don't need this or any of your prototype methods declared in your class. – Bergi Apr 26 '16 at 20:18
  • @AlexanderO'Mara: Also `Object.setPrototypeOf` is not that much of an optimisation hazard as long it is done right after creating the object. It's just if you mutate the [[prototype]] of an object back and forth during its lifetime that it will be bad. – Bergi Apr 26 '16 at 20:21
  • `new Anth(100)` and `new Evth(100)` - _Uncaught ReferenceError: this is not defined_ - Google Chrome 49.0.2623.112 – Qwertiy Apr 26 '16 at 20:24
  • @Qwertiy: Oh my, of course. Thanks for hint, I fixed it. – Bergi Apr 26 '16 at 20:28
  • 1
    The function prototype defines a read-only name attribute that child classes end up inheriting. I worked around that by redefining it: `Object.defineProperty(f, 'name', {value: null, writable: true})` – seanlinsley Jan 08 '20 at 15:46
  • You have to call the super constructor when extending a class. – Armen Michaeli Jun 22 '20 at 19:27
  • 4
    @amn No, you do not, when you don't use `this` and `return` an object. – Bergi Jun 22 '20 at 19:32
44

This is an approach to creating callable objects that correctly reference their object members, and maintain correct inheritance, without messing with prototypes.

Simply:

class ExFunc extends Function {
  constructor() {
    super('...args', 'return this.__self__.__call__(...args)')
    var self = this.bind(this)
    this.__self__ = self
    return self
  }

  // Example `__call__` method.
  __call__(a, b, c) {
    return [a, b, c];
  }
}

Extend this class and add a __call__ method, more below...

An explanation in code and comments:

// This is an approach to creating callable objects
// that correctly reference their own object and object members,
// without messing with prototypes.

// A Class that extends Function so we can create
// objects that also behave like functions, i.e. callable objects.
class ExFunc extends Function {
  constructor() {
    super('...args', 'return this.__self__.__call__(...args)');
    // Here we create a function dynamically using `super`, which calls
    // the `Function` constructor which we are inheriting from. Our aim is to create
    // a `Function` object that, when called, will pass the call along to an internal
    // method `__call__`, to appear as though the object is callable. Our problem is
    // that the code inside our function can't find the `__call__` method, because it
    // has no reference to itself, the `this` object we just created.
    // The `this` reference inside a function is called its context. We need to give
    // our new `Function` object a `this` context of itself, so that it can access
    // the `__call__` method and any other properties/methods attached to it.
    // We can do this with `bind`:
    var self = this.bind(this);
    // We've wrapped our function object `this` in a bound function object, that
    // provides a fixed context to the function, in this case itself.
    this.__self__ = self;
    // Now we have a new wrinkle, our function has a context of our `this` object but
    // we are going to return the bound function from our constructor instead of the
    // original `this`, so that it is callable. But the bound function is a wrapper
    // around our original `this`, so anything we add to it won't be seen by the
    // code running inside our function. An easy fix is to add a reference to the
    // new `this` stored in `self` to the old `this` as `__self__`. Now our functions
    // context can find the bound version of itself by following `this.__self__`.
    self.person = 'Hank'
    return self;
  }
  
  // An example property to demonstrate member access.
  get venture() {
    return this.person;
  }
  
  // Override this method in subclasses of ExFunc to take whatever arguments
  // you want and perform whatever logic you like. It will be called whenever
  // you use the obj as a function.
  __call__(a, b, c) {
    return [this.venture, a, b, c];
  }
}

// A subclass of ExFunc with an overridden __call__ method.
class DaFunc extends ExFunc {
  constructor() {
    super()
    this.a = 'a1'
    this.b = 'b2'
    this.person = 'Dean'
  }

  ab() {
    return this.a + this.b
  }
  
  __call__(ans) {
    return [this.ab(), this.venture, ans];
  }
}

// Create objects from ExFunc and its subclass.
var callable1 = new ExFunc();
var callable2 = new DaFunc();

// Inheritance is correctly maintained.
console.log('\nInheritance maintained:');
console.log(callable2 instanceof Function);  // true
console.log(callable2 instanceof ExFunc);  // true
console.log(callable2 instanceof DaFunc);  // true

// Test ExFunc and its subclass objects by calling them like functions.
console.log('\nCallable objects:');
console.log( callable1(1, 2, 3) );  // [ 'Hank', 1, 2, 3 ]
console.log( callable2(42) );  // [ 'a1b2', Dean', 42 ]

// Test property and method access
console.log(callable2.a, callable2.b, callable2.ab())

View on repl.it

Further explanation of bind:

function.bind() works much like function.call(), and they share a similar method signature:

fn.call(this, arg1, arg2, arg3, ...); more on mdn

fn.bind(this, arg1, arg2, arg3, ...); more on mdn

In both the first argument redefines the this context inside the function. Additional arguments can also be bound to a value. But where call immediately calls the function with the bound values, bind returns an "exotic" function object that transparently wraps the original, with this and any arguments preset.

So when you define a function then bind some of its arguments:

var foo = function(a, b) {
  console.log(this);
  return a * b;
}

foo = foo.bind(['hello'], 2);

You call the bound function with only the remaining arguments, its context is preset, in this case to ['hello'].

// We pass in arg `b` only because arg `a` is already set.
foo(2);  // returns 4, logs `['hello']`
Adrien
  • 667
  • 6
  • 7
  • Can you please add an explanation why `bind` works (i.e. why it returns an instance of `ExFunc`)? – Bergi Nov 30 '16 at 02:58
  • @Bergi `bind` returns a transparent function object that wraps the function object it was called on, which is our callable object, just with the `this` context rebound. So it really returns a transparently wrapped instance of `ExFunc`. Post updated with more info on `bind`. – Adrien Nov 30 '16 at 05:22
  • "*transparent wrapper*" sounds too much like a proxy, especially as if property accesses would be forwarded. But they are not! The bound function is a totally distinct object with its own properties, it only does forward calls. But if its a different object, how can it still be `instanceof ExFunc`? That's what I wanted you to explain. (And please remove the statement about the similarity between `call` and `bind`. Their signature is *not* the same) – Bergi Nov 30 '16 at 05:42
  • 1
    @Bergi All getters/setters and methods are accessible, properties/attributes must be assigned in the `constructor` after `bind` in `ExFunc`. In subclasses of ExFunc, all members are accessible. As for `instanceof`; in es6 bound functions are referred to as exotic, so their inner workings are not apparent, but I'm thinking it passes the call on to its wrapped target, via `Symbol.hasInstance`. It is much like a Proxy, but it's a simple method to accomplish the desired effect. Their signature is _similar_ not the same. – Adrien Nov 30 '16 at 06:51
  • Yes, all properties must be assigned to the bound function. The original (`super(…)` return value) function's properties are *not* accessible. Yes, it's an exotic object, but not a host object, so it's inner working *are* detailed in the spec. Especially the inheritance one which is so important one, that makes prototype methods available and `instanceof` work. But you shouldn't take this for granted… – Bergi Nov 30 '16 at 07:04
  • I wanted to do something like this (Have the real function be in a property of the function object) but I needed to preserve `this`. To do that, instead of `.bind` and using `this` (Which I thought would break `(new ExFunc()) instanceof ExFunc` but doesn't), I used `arguments.callee`, which is a reference to the original function. So `super('return arguments.callee.__call__.apply(this, arguments)')`. This works because the Function constructor is always in the global scope, so `'use strict';` only applies if you write it in the string. – Artyer Aug 30 '18 at 17:10
  • doesn't seem to work if you add any functions/varialbes to the class – rob Sep 15 '19 at 02:52
  • @rob You need to add the props and methods to a subclass of `ExFunc`, I've updated the [repl](https://repl.it/@arccoza/Javascript-Callable-Object-Class-Constructor) with an example. – Adrien Sep 16 '19 at 03:34
  • 1
    @Adrien but from inside `__call__` I can't access `this.a` or `this.ab()`. e.g. https://repl.it/repls/FelineFinishedDesktopenvironment – rob Sep 17 '19 at 14:33
  • 1
    @rob well spotted, there is a reference error, I've updated the answer and code with a fix and a new explanation. – Adrien Sep 20 '19 at 06:14
  • Nice work thx. - but - please - don't write "simply" in this context ;) – halfbit Oct 05 '19 at 23:30
  • You're genious! This is the best of all solutions. – Monsieur Pierre Doune Dec 01 '19 at 11:13
  • It doesn't work properly under TS and says: `TypeError: this.__self__.__call__ is not a function` – KInGcC Dec 18 '20 at 09:15
  • I'm wondering about how secure this is. Isn't using new Function almost the same as using eval() ? I realize you abstract away the string constructor but I'm still wondering if this has the same security flaws. Does anyone know? – Marvin Brouwer Mar 22 '21 at 16:13
25

You can wrap the Smth instance in a Proxy with an apply (and maybe construct) trap:

class Smth extends Function {
  constructor (x) {
    super();
    return new Proxy(this, {
      apply: function(target, thisArg, argumentsList) {
        return x;
      }
    });
  }
}
new Smth(256)(); // 256
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Cool idea. Like this. Should I implement somemore logic instead of placing in inside of apply? – Qwertiy Apr 26 '16 at 17:25
  • 5
    A proxy would incur quite some overhead, wouldn't it? Also, `this` is still an empty function (check `new Smth().toString()`). – Bergi Apr 26 '16 at 17:27
  • 3
    @Bergi No idea about performance. MDN has a big red bold warning about [`setPrototypeOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) and doesn't say anything about proxies. But I guess proxies can be as problematic as `setPrototypeOf`. And about `toString`, it can be shadowed with a custom method in `Smth.prototype`. The native one is implementation-dependent anyways. – Oriol Apr 26 '16 at 17:29
  • @Qwertiy You can add a `construct` trap to specify the behavior of `new new Smth(256)()`. And add custom methods that shadow the native ones which access the code of a function, like `toString` as Bergi noted. – Oriol Apr 26 '16 at 17:37
  • I ment is your `apply` method implemented in the way it is supposed to be used, or it is just a demonstration and I need to look through more information about `Proxy` and `Reflect` to use it in proper way? – Qwertiy Apr 26 '16 at 17:41
  • @Qwertiy The value of `apply` is a function which will be called when you call the proxy object. It can be more powerful than in the example (notice I didn't use `target`, `thisArg` nor `argumentsList`). But yes, it's supposed to be used like this. – Oriol Apr 26 '16 at 18:04
  • Shouldn't you also assign `this` to the proxy? That way, you can still get other data about the function? Or would it act like it's a proxy, not a function? – Someone Nov 06 '21 at 15:01
5

Update:

Unfortunately this doesn't quite work because it's now returning a function object instead of a class, so it seems this actually can't be done without modifying the prototype. Lame.


Basically the problem is there is no way of setting the this value for the Function constructor. The only way to really do this would be to use the .bind method afterwards, however this is not very Class-friendly.

We could do this in a helper base class, however this does does not become available until after the initial super call, so it's a bit tricky.

Working Example:

'use strict';

class ClassFunction extends function() {
    const func = Function.apply(null, arguments);
    let bound;
    return function() {
        if (!bound) {
            bound = arguments[0];
            return;
        }
        return func.apply(bound, arguments);
    }
} {
    constructor(...args) {
        (super(...args))(this);
    }
}

class Smth extends ClassFunction {
    constructor(x) {
        super('return this.x');
        this.x = x;
    }
}

console.log((new Smth(90))());

(Example requires modern browser or node --harmony.)

Basically the base function ClassFunction extends will wrap the Function constructor call with a custom function which is similar to .bind, but allows binding later, on the first call. Then in the ClassFunction constructor itself, it calls the returned function from super which is now the bound function, passing this to finish setting up the custom bind function.

(super(...))(this);

This is all quite a bit complicated, but it does avoid mutating the prototype, which is considered bad-form for optimization reasons and can generate warnings in browser consoles.

Jed Fox
  • 2,979
  • 5
  • 28
  • 38
Alexander O'Mara
  • 58,688
  • 18
  • 163
  • 171
  • 1
    You are overcomplicating things. `bound` will refer to the function that you `return` from that anonymous class. Just name it, and refer to it directly. I also would recommend to avoid passing code strings around, they're just a mess to work with (in *every* step of the development process). – Bergi Apr 26 '16 at 20:14
  • That `extends` doesn't really seem to work as expected, as `Function.isPrototypeOf(Smth)` and also `new Smth instanceof Function` are false. – Bergi Apr 26 '16 at 20:16
  • @Bergi What JS engine are you using? `console.log((new Smth) instanceof Function);` is `true` for me in Node v5.11.0 and the latest Firefox. – Alexander O'Mara Apr 26 '16 at 20:18
  • Oops, wrong example. It's `new Smth instanceof Smth` that is not working with your solution. Also no methods of `Smth` will be avaible on your instances - as you just return a standard `Function`, not a `Smth`. – Bergi Apr 26 '16 at 20:19
  • 1
    @Bergi Darn it, looks like you're right. However extending any native types seems to have the same problem. `extend Function` also makes `new Smth instanceof Smth` false. – Alexander O'Mara Apr 26 '16 at 20:27
  • That sounds like an implementation bug. `class X extends Function {}; new X instanceof X` should return `true` [according to the spec](http://www.ecma-international.org/ecma-262/6.0/#sec-function-constructor). – Bergi Apr 26 '16 at 20:35
  • @Bergi I just posted a question on this issue: http://stackoverflow.com/questions/36875299/es6-class-extending-native-type-make-instanceof-behave-unexpectedly-in-some-java – Alexander O'Mara Apr 26 '16 at 20:41
4

I took the advice from Bergi's answer and wrapped it into an NPM module.

var CallableInstance = require('callable-instance');

class ExampleClass extends CallableInstance {
  constructor() {
    // CallableInstance accepts the name of the property to use as the callable
    // method.
    super('instanceMethod');
  }

  instanceMethod() {
    console.log("instanceMethod called!");
  }
}

var test = new ExampleClass();
// Invoke the method normally
test.instanceMethod();
// Call the instance itself, redirects to instanceMethod
test();
// The instance is actually a closure bound to itself and can be used like a
// normal function.
test.apply(null, [ 1, 2, 3 ]);
Ryan Patterson
  • 499
  • 3
  • 13
4

Having read this article and all the answers here, I finally found a satisfying answer in an older thread.

Here's an example:

class Hey {
  constructor() {
    function hey() {
      return "hey";
    }
    
    this.value = "yo";

        Object.assign(hey, this);
        Object.setPrototypeOf(hey, Object.getPrototypeOf(this));

        return hey;
  }
  
  yo() {
    return this.value;
  }
}

const hey = new Hey();

console.log(hey());              // it's callable 
console.log(hey.yo());           // methods are correctly bound to `this` 

console.log(hey instanceof Hey); // it type-checks 

Inheritance works too:

class HeyHey extends Hey {
  constructor() {
    super();
  }
  
  yoyo() {
    return this.value + "!";
  }
}

const lol = new HeyHey();

console.log(lol());                 // it's callable 
console.log(lol.yo());              // inherited methods are correctly bound to `this` 
console.log(lol.yoyo());            // new methods are correctly bound to `this` as well 

console.log(lol instanceof Hey);    // it type-checks for the super class 
console.log(lol instanceof HeyHey); // it type-checks for the derived class 

You can run the example here to see for yourself.

This approach:

  • correctly binds methods to this
  • is type-safe (can be type-checked using instanceof)
  • correctly supports inheritance (correctly binds and type-checks derived classes)
  • doesn't rely on any features newer than class - getPrototypeOf and setPrototypeOf were widely available some years before class
  • doesn't rely on the Function constructor (avoids parsing source code at run-time)
  • doesn't rely on Proxy (which isn't great for performance)

All in all, this approach is definitely simpler and easier to implement, and it should perform better as well.

(In theory - please feel free to benchmark this and post your results.)

mindplay.dk
  • 7,085
  • 3
  • 44
  • 54
  • For better or worse, `hey instanceof Function === false`, even though `hey()` looks like a function being called. Same situation with `lol`. I don't know if that's evil or genius! – Moh Aug 15 '23 at 15:36
  • A natural consequence of this is that `hey.call === undefined` and `hey.apply === undefined` and `hey.bind === undefined`. This can be undesirable in some circumstances that would try to use those features of a `Function`. (If these were all implemented somehow, maybe `hey.bind` would have to be a no-op to mimic the intended semantics of a `Function`?) – Moh Aug 15 '23 at 15:43
3

This is the solution I've worked out that serves all my needs of extending functions and has served me quite well. The benefits of this technique are:

  • When extending ExtensibleFunction, the code is idiomatic of extending any ES6 class (no, mucking about with pretend constructors or proxies).
  • The prototype chain is retained through all subclasses, and instanceof / .constructor return the expected values.
  • .bind() .apply() and .call() all function as expected. This is done by overriding these methods to alter the context of the "inner" function as opposed to the ExtensibleFunction (or it's subclass') instance.
  • .bind() returns a new instance of the functions constructor (be it ExtensibleFunction or a subclass). It uses Object.assign() to ensure the properties stored on the bound function are consistent with those of the originating function.
  • Closures are honored, and arrow functions continue to maintain the proper context.
  • The "inner" function is stored via a Symbol, which can be obfuscated by modules or an IIFE (or any other common technique of privatizing references).

And without further ado, the code:

// The Symbol that becomes the key to the "inner" function 
const EFN_KEY = Symbol('ExtensibleFunctionKey');

// Here it is, the `ExtensibleFunction`!!!
class ExtensibleFunction extends Function {
  // Just pass in your function. 
  constructor (fn) {
    // This essentially calls Function() making this function look like:
    // `function (EFN_KEY, ...args) { return this[EFN_KEY](...args); }`
    // `EFN_KEY` is passed in because this function will escape the closure
    super('EFN_KEY, ...args','return this[EFN_KEY](...args)');
    // Create a new function from `this` that binds to `this` as the context
    // and `EFN_KEY` as the first argument.
    let ret = Function.prototype.bind.apply(this, [this, EFN_KEY]);
    // For both the original and bound funcitons, we need to set the `[EFN_KEY]`
    // property to the "inner" function. This is done with a getter to avoid
    // potential overwrites/enumeration
    Object.defineProperty(this, EFN_KEY, {get: ()=>fn});
    Object.defineProperty(ret, EFN_KEY, {get: ()=>fn});
    // Return the bound function
    return ret;
  }

  // We'll make `bind()` work just like it does normally
  bind (...args) {
    // We don't want to bind `this` because `this` doesn't have the execution context
    // It's the "inner" function that has the execution context.
    let fn = this[EFN_KEY].bind(...args);
    // Now we want to return a new instance of `this.constructor` with the newly bound
    // "inner" function. We also use `Object.assign` so the instance properties of `this`
    // are copied to the bound function.
    return Object.assign(new this.constructor(fn), this);
  }

  // Pretty much the same as `bind()`
  apply (...args) {
    // Self explanatory
    return this[EFN_KEY].apply(...args);
  }

  // Definitely the same as `apply()`
  call (...args) {
    return this[EFN_KEY].call(...args);
  }
}

/**
 * Below is just a bunch of code that tests many scenarios.
 * If you run this snippet and check your console (provided all ES6 features
 * and console.table are available in your browser [Chrome, Firefox?, Edge?])
 * you should get a fancy printout of the test results.
 */

// Just a couple constants so I don't have to type my strings out twice (or thrice).
const CONSTRUCTED_PROPERTY_VALUE = `Hi, I'm a property set during construction`;
const ADDITIONAL_PROPERTY_VALUE = `Hi, I'm a property added after construction`;

// Lets extend our `ExtensibleFunction` into an `ExtendedFunction`
class ExtendedFunction extends ExtensibleFunction {
  constructor (fn, ...args) {
    // Just use `super()` like any other class
    // You don't need to pass ...args here, but if you used them
    // in the super class, you might want to.
    super(fn, ...args);
    // Just use `this` like any other class. No more messing with fake return values!
    let [constructedPropertyValue, ...rest] = args;
    this.constructedProperty = constructedPropertyValue;
  }
}

// An instance of the extended function that can test both context and arguments
// It would work with arrow functions as well, but that would make testing `this` impossible.
// We pass in CONSTRUCTED_PROPERTY_VALUE just to prove that arguments can be passed
// into the constructor and used as normal
let fn = new ExtendedFunction(function (x) {
  // Add `this.y` to `x`
  // If either value isn't a number, coax it to one, else it's `0`
  return (this.y>>0) + (x>>0)
}, CONSTRUCTED_PROPERTY_VALUE);

// Add an additional property outside of the constructor
// to see if it works as expected
fn.additionalProperty = ADDITIONAL_PROPERTY_VALUE;

// Queue up my tests in a handy array of functions
// All of these should return true if it works
let tests = [
  ()=> fn instanceof Function, // true
  ()=> fn instanceof ExtensibleFunction, // true
  ()=> fn instanceof ExtendedFunction, // true
  ()=> fn.bind() instanceof Function, // true
  ()=> fn.bind() instanceof ExtensibleFunction, // true
  ()=> fn.bind() instanceof ExtendedFunction, // true
  ()=> fn.constructedProperty == CONSTRUCTED_PROPERTY_VALUE, // true
  ()=> fn.additionalProperty == ADDITIONAL_PROPERTY_VALUE, // true
  ()=> fn.constructor == ExtendedFunction, // true
  ()=> fn.constructedProperty == fn.bind().constructedProperty, // true
  ()=> fn.additionalProperty == fn.bind().additionalProperty, // true
  ()=> fn() == 0, // true
  ()=> fn(10) == 10, // true
  ()=> fn.apply({y:10}, [10]) == 20, // true
  ()=> fn.call({y:10}, 20) == 30, // true
  ()=> fn.bind({y:30})(10) == 40, // true
];

// Turn the tests / results into a printable object
let table = tests.map((test)=>(
  {test: test+'', result: test()}
));

// Print the test and result in a fancy table in the console.
// F12 much?
console.table(table);

Edit

Since I was in the mood, I figured I'd publish a package for this on npm.

Aaron Levine
  • 124
  • 1
  • 5
3

Generalizing Oriol's answer:

class Smth extends Function {
  constructor(x) {
    super();

    this.x = x;

    return new Proxy(this, {
      apply: (target, that, args) => target.__call__(...args)
    });
  }

  __call__(v) {
    return this.x * v;
  }
}
fserb
  • 4,004
  • 2
  • 26
  • 23
2

Firstly I came to solution with arguments.callee, but it was awful.
I expected it to break in global strict mode, but seems like it works even there.

class Smth extends Function {
  constructor (x) {
    super('return arguments.callee.x');
    this.x = x;
  }
}

(new Smth(90))()

It was a bad way because of using arguments.callee, passing the code as a string and forcing its execution in non-strict mode. But than idea to override apply appeared.

var global = (1,eval)("this");

class Smth extends Function {
  constructor(x) {
    super('return arguments.callee.apply(this, arguments)');
    this.x = x;
  }
  apply(me, [y]) {
    me = me !== global && me || this;
    return me.x + y;
  }
}

And the test, showing I'm able to run this as function in different ways:

var f = new Smth(100);

[
f instanceof Smth,
f(1),
f.call(f, 2),
f.apply(f, [3]),
f.call(null, 4),
f.apply(null, [5]),
Function.prototype.apply.call(f, f, [6]),
Function.prototype.apply.call(f, null, [7]),
f.bind(f)(8),
f.bind(null)(9),
(new Smth(200)).call(new Smth(300), 1),
(new Smth(200)).apply(new Smth(300), [2]),
isNaN(f.apply(window, [1])) === isNaN(f.call(window, 1)),
isNaN(f.apply(window, [1])) === isNaN(Function.prototype.apply.call(f, window, [1])),
] == "true,101,102,103,104,105,106,107,108,109,301,302,true,true"

Version with

super('return arguments.callee.apply(arguments.callee, arguments)');

in fact contains bind functionality:

(new Smth(200)).call(new Smth(300), 1) === 201

Version with

super('return arguments.callee.apply(this===(1,eval)("this") ? null : this, arguments)');
...
me = me || this;

makes call and apply on window inconsistent:

isNaN(f.apply(window, [1])) === isNaN(f.call(window, 1)),
isNaN(f.apply(window, [1])) === isNaN(Function.prototype.apply.call(f, window, [1])),

so the check should be moved into apply:

super('return arguments.callee.apply(this, arguments)');
...
me = me !== global && me || this;
Qwertiy
  • 19,681
  • 15
  • 61
  • 128
  • 2
    What are you actually trying to do? – Mulan Apr 26 '16 at 17:15
  • 2
    I think Classes are always in strict mode: http://stackoverflow.com/questions/29283935/which-ecmascript-6-features-imply-strict-mode – Alexander O'Mara Apr 26 '16 at 17:16
  • @AlexanderO'Mara, by the way, `this` is window, not undefined, so the function created is not in strict mode (at least in chrome). – Qwertiy Apr 26 '16 at 17:21
  • Please, stop downwoting this answer. I've already wrote that it is a bad way. But it really is an answer - it works both in FF and Chrome (don't have Edge to check). – Qwertiy Apr 26 '16 at 17:38
  • I'm guessing this works because `Function` is not in strict mode. Though awful, it is interesting +1. You probably wouldn't be able to walk a chain any further though. – Alexander O'Mara Apr 26 '16 at 18:01
  • @AlexanderO'Mara, what do you mean by chain? – Qwertiy Apr 26 '16 at 18:26
  • @Qwertiy You can walk the `callee` chain until you get to a function in strict mode, so I guess this works because the function it creates is not in strict mode, only the calling function will be. – Alexander O'Mara Apr 26 '16 at 18:49
  • @AlexanderO'Mara, what do you think about updated answer? – Qwertiy Apr 26 '16 at 20:04
  • I think you really should pass `this` instead of `arguments.callee` to your `apply` method. That way, `me` (the parameter) is not the same as `this` (the `Smth` instance) any more. – Bergi Apr 26 '16 at 20:25
  • @Bergi, ```super('/* `this` is window here */')``` - what `this` are you talking about? – Qwertiy Apr 26 '16 at 20:28
  • @Qwertiy: Yes, exactly that `this`. It's `window` (because you're in sloppy mode to get `arguments.callee`) in most cases, yes, but it might not be. You still could `.call()` your `new Smth` or use it as a method of an object or as an event handler etc. – Bergi Apr 26 '16 at 20:31
  • @Bergi, `(new Smth(200)).call(new Smth(100), 1)` is 201 instead of 101 - are you talking about that case? Let's go to the [chat](//chat.stackexchange.com/rooms/38918/discussion-between-qwertiy-and-grundy)? – Qwertiy Apr 26 '16 at 20:33
  • Yes, a case such as that. It should end up with `this.x == 200` and `me.x == 100`, so that you can choose in the `apply` method. The call should be equivalent to `(new Smth(200)).apply(new Smth(100), [1])` – Bergi Apr 26 '16 at 20:37
  • @Qwertiy: I don't think you should do that global object detection. The function could reasonably be explicitly called on `window` (or whatever), and in that case you would have to not replace it with `null`. Just accept that it's sloppy and don't try to do magic :-) – Bergi Apr 26 '16 at 20:58
  • @Bergi, but it will always be called on window implicitly - how to handle it than? Or just move detection into apply to make different calls consistent? – Qwertiy Apr 26 '16 at 21:02
  • @Qwertiy: I mean that you *cannot* distinguish these calls in sloppy mode, and you should better leave `this` as it is. – Bergi Apr 26 '16 at 21:05
  • @Bergi, then `apply` will be always getting `window` as `me` on normal calls - in that cases it should be ignored. – Qwertiy Apr 26 '16 at 21:07
1

There is a simple solution which takes advantage of JavaScript's functional capabilities: Pass the "logic" as a function-argument to the constructor of your class, assign the methods of that class to that function, then return that function from the constructor as the result:

class Funk
{
    constructor (f)
    { let proto       = Funk.prototype;
      let methodNames = Object.getOwnPropertyNames (proto);
      methodNames.map (k => f[k] = this[k]);
      return f;
    }

    methodX () {return 3}
}

let myFunk  = new Funk (x => x + 1);
let two     = myFunk(1);         // == 2
let three   = myFunk.methodX();  // == 3

The above was tested on Node.js 8.

A shortcoming of the example above is it does not support methods inherited from the superclass-chain. To support that, simply replace "Object . getOwnPropertyNames(...)" with something that returns also the names of inherited methods. How to do that I believe is explained in some other question-answer on Stack Overflow :-). BTW. It would be nice if ES7 added a method to produce inherited methods' names as well ;-).

If you need to support inherited methods one possibility is adding a static method to the above class which returns all inherited and local method names. Then call that from the constructor. If you then extend that class Funk, you get that static method inherited along as well.

Panu Logic
  • 2,193
  • 1
  • 17
  • 21
  • I think this example gives a simple answer to the original question "... how can I implement the logic for such call". Just pass it as a function-valued argument to the constructor. In the code above the class Funk does not explicitly extend Function although it could, it doesn't really need to. As you can see you can call its "instances" juts like you call any ordinary function. – Panu Logic Dec 11 '17 at 16:51
0

Came up with a solution that works without using Object.setPrototypeOf since MDN has big red warning signs around that. Can run the example JSFiddle here. One limitation I can't figure out is how to get access to the this context at call time of the produced function within the arbitrary execute.

class ExtendedFunction extends Function {

    // Arbitrary private properties
  #foo
  #baz() { return this.#foo + 'qux' }
  
  // The thing that happens when you call your extended function
  // context is optional if you want access to the `this` 
  // provides to your extended function at call time
  #execute() {
    // Arbitrary code that can call anything in closure here
    return this.#baz()
  }
  
  constructor(a) {
    // Set `this` to simple wrapper function
    // that takes another function and returns its value
    // Use super we get an instance of Function and ExtendedFucntion
    super('execute', 'return execute()')
    this.#foo = a
    // Bind our arbitrary function to the current `this`
    // allowing it to access private properties even when passed around
    const boundExecute = this.#execute.bind(this)
    // Bind the simple wrapper and the boundExecute together and return that
    // When called our extended function will do whatever is in #execute
    var self = this.bind(null, boundExecute)
    return self
  }

}

const a = new ExtendedFunction(256)
console.log(a instanceof Function) // true
console.log(a instanceof ExtendedFunction) // true
console.log(a()) // 256qux
fynyky
  • 493
  • 2
  • 6
  • 14
-1

A little late but let me leave this here.

Recently I had to discover a way to subclass Function in order to turn normal functions into Threadable / Promisified functions without messing with the Function.prototype. I think this particular necessity forms a very reasonable basis to this question on how and why one can use class abstraction to extend Function.

So the idea is, we create a Threadable class of which the member functions are threadable. By this I mean, any normal function can easily be made Threadable and when spawned runs on a seperate thread and gives us a promise to be resolved or rejected depending on the outcome of the worker operation. However you should still be able to invoke it syncronously if need be.

class Threadable extends Function {
  
  // just use super() to wrap the given f function with a transparent function layer
  constructor(f){
    super("...as",`return ${f.toString()}.apply(this,as)`);
  }
  
  // spawn is the only method of the Threadable class.
  // Returns a promise and runs callee function on a separate thread.
  spawn(...as){
    var code = `self.onmessage = m => self.postMessage((${this.toString()}).apply(self,m.data));`,
        blob = new Blob([code], {type: "text/javascript"}),
        wrkr = new Worker(window.URL.createObjectURL(blob));
    return new Promise( (v,x) => ( wrkr.onmessage = m => (v(m.data), wrkr.terminate())
                                 , wrkr.onerror   = e => (x(e.message), wrkr.terminate())
                                 , wrkr.postMessage(as)
                                 )
                      );
  }
}

function add(...ns) {
    return ns.reduce((a,b) => a+b);
}

var addT = new Threadable(add);

addT.spawn(1,2,3,4)
    .then(m => console.log(`Promisified thread returned ${m}`));

console.log(`Synchronous invocation of addT returned ${addT(1,2,3,4,5)}`);
Redu
  • 25,060
  • 6
  • 56
  • 76