Shaw
Shaw

Reputation: 173

Create callable objects with private properties without arguments.callee

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.

Upvotes: 0

Views: 100

Answers (1)

Bergi
Bergi

Reputation: 665475

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();

Upvotes: 1

Related Questions