Hướng dẫn javascript dynamic function name

The syntax function[i](){} implies an object with property values that are functions, function[], indexed by the name, [i].
Thus
{"f:1":function(){}, "f:2":function(){}, "f:A":function(){}, ... } ["f:"+i].

{"f:1":function f1(){}, "f:2":function f2(){}, "f:A":function fA(){}} ["f:"+i] will preserve function name identification. See notes below regarding :.

So,

javascript: alert(
  new function(a){
    this.f={"instance:1":function(){}, "instance:A":function(){}} ["instance:"+a]
  }("A") . toSource()
);

displays ({f:(function () {})}) in FireFox.
(This is almost the same idea as this solution, only it uses a generic object and no longer directly populates the window object with the functions.)

This method explicitly populates the environment with instance:x.

javascript: alert(
  new function(a){
    this.f=eval("instance:"+a+"="+function(){})
  }("A") . toSource()
);
alert(eval("instance:A"));

displays

({f:(function () {})})

and

function () {
}

Though the property function f references an anonymous function and not instance:x, this method avoids several problems with this solution.

javascript: alert(
  new function(a){
    eval("this.f=function instance"+a+"(){}")
  }("A") . toSource()
);
alert(instanceA);    /* is undefined outside the object context */

displays only

({f:(function instanceA() {})})
  • The embedded : makes the javascript function instance:a(){} invalid.
  • Instead of a reference, the function's actual text definition is parsed and interpreted by eval.

The following is not necessarily problematic,

  • The instanceA function is not directly available for use as instanceA()

and so is much more consistent with the original problem context.

Given these considerations,

this.f = {"instance:1": function instance1(){},
          "instance:2": function instance2(){},
          "instance:A": function instanceA(){},
          "instance:Z": function instanceZ(){}
         } [ "instance:" + a ]

maintains the global computing environment with the semantics and syntax of the OP example as much as possible.

Hướng dẫn javascript dynamic function name

Say you're writing some higher order functions that return other functions. In my case it was a utility that creates a typed hook to consume some context.

I wanted to let the consumer of this utility to have a reasonable name property on the hook returned, not some generic library code name like "useContext".

The first attempt did not work.

const someFunc = () => { ... }
someFunc.name = someConfig.name

Enter fullscreen mode Exit fullscreen mode

You get a nice error at least.

Hướng dẫn javascript dynamic function name

So then I think to myself 🤔 Do I know anything about dynamic naming in general in JavaScript?

💡

Objects can have dynamic keys! With square brackets, we can stringify runtime values.

{ [someVar]: "woohoo" }

Enter fullscreen mode Exit fullscreen mode

So, to solve my configurable function name problem, I used computed keys to create an object and computed keys again to destructure the function value.

🤯 Right?

const custom = "Jerome"
const { [custom]: someFunc } = { [custom]: () => void 0 }
someFunc.name === "Jerome" // true

Enter fullscreen mode Exit fullscreen mode

^ You can try that example in your browser console.

Pretty neat stuff! Here was my final solution, inspired by this cheatsheet.

import * as React from 'react';

export function assertContext<T>(
  context: T | undefined,
  config: {
    hookName: string;
    providerName: string;
  },
): asserts context is T {
  if (typeof context === 'undefined') {
    throw new Error(`\`${config.hookName}\` must be used within \`<${config.providerName} />\``);
  }
}

export function createUsableContext<T>(config: {
  hookName: string;
  providerName: string;
}): [() => T, React.Context<T | undefined>] {
  const Context = React.createContext<T | undefined>(undefined);

  // assign the function name dynamically by instantaneously assigning and destructuring an object field
  // const useContext = ... would result in the fn name being `useContext`. Not helpful.
  const { [config.hookName]: useContext } = {
    [config.hookName]: (): T => {
      const context = React.useContext(Context);
      assertContext(context, config);
      return context;
    },
  };

  return [useContext, Context];
}

Enter fullscreen mode Exit fullscreen mode