Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

>Solution :

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();
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading