Home > database >  JavaScript: How to dynamically get the name of the method inside the method in a class?
JavaScript: How to dynamically get the name of the method inside the method in a class?

Time:07-20

I would like to get the name of the method inside the method in a class and throw an error when one argument is passed to the method. For example, if the name of the instance is instance_of_cookie and the name of the method is (for simplicity) the_method, then passing one argument to that method should throw an error like "instance_of_cookie.the_method" expected two arguments or simply "the_method" expected two arguments.

I can get the name of the function inside the function using arguments.calle.name. But that doesn't seem to work inside class methods and throws the following error:

VM10:4 Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
    at Cookie.the_method (<anonymous>:4:28)
    at eval (eval at the_method (:4:28), <anonymous>:1:32)
    at Cookie.the_method (<anonymous>:4:28)
    at <anonymous>:13:32

I came across a post which mentioned this.constructor.name but that gives me the name of the class.

class Cookie {
  the_method(x, y) {
    if (y == undefined) {
      return `'${this.constructor.name}' expects two arguments; one was given`;
    } else {
        return "Everything's OK"
    }
  }
}

let instance_of_cookie = new Cookie();

console.log(instance_of_cookie.the_method(8))

Is there any way I can get the name of the method inside the method in a class?

(Note: I can hard-code the name of the method, but for some reasons, I don't want to do that.)

CodePudding user response:

This seems like a complete hack and probably isn't the best solution, but you could use this. I create an error object and use regex to pull the name of the method off the call stack:

class Cookie {
  the_method(x, y) {
    //regular expression to capture the method name
    const reg = new RegExp("("   `${this.constructor.name}`   "\.[A-Z|a-z|0-9|_] )", 'g');
    
    if (y == undefined) {
      //create an error object
      var err = new Error();
      //capture matches with regex
      const matches = err.stack.match(reg);
      //get the method name
      const methodname = matches[0].replace(`${this.constructor.name}`   '.', '');
      return `${methodname} expects two arguments; one was given`;
    } else {
        return "Everything's OK"
    }
  }
}

let instance_of_cookie = new Cookie();

console.log(instance_of_cookie.the_method(8))

CodePudding user response:

Instead of dynamically getting the name, you can use a string (which you can change in a later iteration of your program and also use in other places in your code) to define the function name and the method name on the class during construction:

References:

'use strict';

class Example {
  constructor () {
    const methodName = 'getName';

    const selfReferencingMethodWhoseNameMightChangeInTheFuture = (options) => {
      if (options?.throw) throw new Error('Oops');
      const name = options?.divulge ? methodName : 'Anonymous';
      console.log(`My name is "${name}"`);
    };

    // set the function name for stack diagnostics, etc.
    Object.defineProperty(
      selfReferencingMethodWhoseNameMightChangeInTheFuture,
      'name',
      {
        // use existing descriptor details
        ...Object.getOwnPropertyDescriptor(selfReferencingMethodWhoseNameMightChangeInTheFuture, 'name'),
        // but update the name
        value: methodName,
      },
    );

    this[methodName] = selfReferencingMethodWhoseNameMightChangeInTheFuture;
  }
}

const example = new Example();
example.getName(); // My name is "Anonymous"
example.getName({divulge: true}); // My name is "getName"
console.log(example.getName.name); // getName

try {
  example.getName({throw: true});
}
catch (ex) {
  if (ex instanceof Error) {
    // Needed for the Stack Overflow code snippet demo environment
    // because it reformats error messages in its virtual console
    console.error(ex.stack);
  }
  else throw ex;
}

Using this technique, you could easily define the name outside the class (or even export it from your module so that — at other call sites in dependent code — invocations can take place dynamically without knowing the method name. Example:

import {Example, methodName} from './very-dynamic-class-module.js';

const example = new Example();
example[methodName]({divulge: true});

Obligatory warning: If you use source transformation tools during build (e.g. minifiers, etc.) which rewrite variable names, be sure to test the interaction with this technique.

CodePudding user response:

No, there's no (non-hacky) way to do that. Just hardcode the name, or if you think that's a refactoring hazard, refer to the method's .name:

class Cookie {
  theMethod(x, y) {
    if (y == undefined) {
      return `'${this.theMethod.name}' expects two arguments; one was given`;
    } else {
        return "Everything's OK"
    }
  }
}

const cookie = new Cookie();
console.log(cookie.theMethod(8))

  • Related