By callable object I mean an object that's also a function similar to jQuery.
There's plenty of resources for doing this, such as this article.
However problem arise when I try to implement private fields/properties.
Only the method utilizing arguments.callee
appears to work.
new class extends Function {
constructor() {
super('return arguments.callee._call(...arguments)')
}
_call(x) {
console.log("this object is directly callable")
}
#data;
get data() {
return this.#data ??= link();
}
}
But arguments.callee is deprecated and doesn't work in strict mode so it feels dirty doing it this way.
The conventional wisdom is to use a named function, but there appears to be no way to name the function created by the constructor() that I know of.
I could abandon class and private properties altogether and use IIFE to implement private variables but I really like how clean the syntax is with class and private properties and prefer to stick with it if possible.
The approach from my answer to How to extend Function with ES6 classes? works just fine with private class fields:
function ExtensibleFunction(f) {
return Object.setPrototypeOf(f, new.target.prototype);
}
ExtensibleFunction.prototype = Function.prototype;
class Weird extends ExtensibleFunction {
#data;
constructor(data) {
super((...args) => this._call(...args));
this.#data = data;
}
_call(x) {
console.log(`This object is directly callable and has access to ${this.#data}`);
}
get data() {
return this.#data;
}
}
const example = new Weird('example data');
console.log(example.data);
example();
Or like this:
const call = Symbol('call');
function ExtensibleFunction() {
const f = () => f[call]();
return Object.setPrototypeOf(f, new.target.prototype);
}
ExtensibleFunction.prototype = Function.prototype;
class Weird extends ExtensibleFunction {
#data;
constructor(data) {
super();
this.#data = data;
}
[call](x) {
console.log(`This object is directly callable and has access to ${this.#data}`);
}
get data() {
return this.#data;
}
}
const example = new Weird('example data');
console.log(example.data);
example();