webentwicklung-frage-antwort-db.com.de

Dynamischer Funktionsname in Javascript?

Ich habe das:

this.f = function instance(){};

Ich hätte gerne folgendes:

this.f = function ["instance:" + a](){};
62
Totty.js

aktualisieren

Wie bereits erwähnt, ist dies weder die schnellste noch empfohlene Lösung. Marcoscs Lösung unten ist der Weg zu gehen.

Sie können eval verwenden:

var code = "this.f = function " + instance + "() {...}";
eval(code);
4
Mo Valipour

Dies wird im Grunde auf der einfachsten Ebene erledigt: 

"use strict";
var name = "foo";
var func = new Function(
     "return function " + name + "(){ alert('sweet!')}"
)();

//call it, to test it
func();

Wenn Sie mehr Lust haben wollen, habe ich einen Artikel über " Dynamische Funktionsnamen in JavaScript " geschrieben. 

104
Marcosc

Sie können Object.defineProperty wie in der MDN-JavaScript-Referenz [1] angegeben verwenden:

var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
  1. https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Description
26
Darren

In aktuellen Engines können Sie das tun

function nameFunction(name, body) {
  return {[name](...args) {return body(...args)}}[name]
}



const x = nameFunction("wonderful function", (p) => p*2)
console.log(x(9)) // => 18
console.log(x.name) // => "wonderful function"

12
kybernetikos

Ich denke, dass die meisten Vorschläge hier suboptimal sind, indem eval, hacky-Lösungen oder Wrapper verwendet werden. Ab ES2015 werden Namen aus der syntaktischen Position für Variablen und Eigenschaften abgeleitet.

Also das wird gut funktionieren:

const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'

Widerstehen Sie der Versuchung, Named Function Factory-Methoden zu erstellen, da Sie die Funktion nicht von außen übergeben können und sie an der syntaktischen Position nachrüsten, um ihren Namen abzuleiten. Dann ist es schon zu spät. Wenn Sie das wirklich brauchen, müssen Sie einen Wrapper erstellen. Jemand hat das hier gemacht, aber diese Lösung funktioniert nicht für Klassen (die auch Funktionen sind).

Eine viel ausführlichere Antwort mit allen beschriebenen Varianten wurde hier geschrieben: https://stackoverflow.com/a/9479081/633921 ​​

5
Albin

Die am meisten gewählte Antwort hat bereits den [String] -Funktionskörper definiert. Ich suchte nach einer Lösung, um den bereits deklarierten Funktionsnamen umzubenennen, und nach einer Stunde des Kampfes habe ich mich damit befasst. Es:

  • übernimmt die bereits deklarierte Funktion
  • parst es mit der .toString()-Methode in [String]
  • überschreibt dann den Namen (der benannten Funktion) oder fügt den neuen an (wenn anonym) zwischen function und (
  • erstellt dann die neue umbenannte Funktion mit dem Konstruktor new Function()

function nameAppender(name,fun){
  const reg = /^(function)(?:\s*|\s+([A-Za-z0-9_$]+)\s*)(\()/;
  return (new Function(`return ${fun.toString().replace(reg,`$1 ${name}$3`)}`))();
}

//WORK FOR ALREADY NAMED FUNCTIONS:
function hello(name){
  console.log('hello ' + name);
}

//rename the 'hello' function
var greeting = nameAppender('Greeting', hello); 

console.log(greeting); //function Greeting(name){...}


//WORK FOR ANONYMOUS FUNCTIONS:
//give the name for the anonymous function
var count = nameAppender('Count',function(x,y){ 
  this.x = x;
  this.y = y;
  this.area = x*y;
}); 

console.log(count); //function Count(x,y){...}

3
Paweł

Die Syntax function[i](){} impliziert ein Objekt mit Eigenschaftswerten, die Funktionen sind, function[], indiziert durch den Namen [i].
Somit
{"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] speichert die Identifikation des Funktionsnamens. Beachten Sie die nachstehenden Hinweise zu :.

So,

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

zeigt ({f:(function () {})}) in FireFox an.
(Dies ist fast die gleiche Idee wie diese Lösung , nur verwendet es ein generisches Objekt und füllt das Fensterobjekt nicht mehr direkt mit den Funktionen.)

Diese Methode füllt die Umgebung explizit mit instance:x.

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

zeigt

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

und

function () {
}

Obwohl die Eigenschaftsfunktion f auf einen anonymous function und nicht auf einen instance:x verweist, vermeidet diese Methode verschiedene Probleme mit diese Lösung .

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

wird nur angezeigt

({f:(function instanceA() {})})
  • Der eingebettete : macht das Javascript function instance:a(){} ungültig.
  • Anstelle einer Referenz wird die tatsächliche Textdefinition der Funktion von eval analysiert und interpretiert.

Das Folgende ist nicht unbedingt problematisch,

  • Die Funktion instanceA ist nicht direkt für die Verwendung als instanceA() verfügbar.

und ist so viel konsistenter mit dem ursprünglichen Problemkontext.

Angesichts dieser Überlegungen,

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

pflegt die globale Rechnerumgebung mit der Semantik und Syntax des OP-Beispiels so weit wie möglich.

3
Ekim

Dynamische Methoden eines Objekts können mit den von ECMAScript 2015 (ES6) bereitgestellten Object Literal Extensions erstellt werden:

const postfixes = ['foo', 'bar'];

const mainObj = {};

const makeDynamic = (postfix) => {
  const newMethodName = 'instance: ' + postfix;
  const tempObj = {
    [newMethodName]() {
      console.log(`called method ${newMethodName}`);
    }
  }
  Object.assign(mainObj, tempObj);
  return mainObj[newMethodName]();
}

const processPostfixes = (postfixes) => { 
  for (const postfix of postfixes) {
    makeDynamic(postfix); 
  }
};

processPostfixes(postfixes);

console.log(mainObj);

Die Ausgabe des obigen Codes lautet:

"called method instance: foo"
"called method instance: bar"
Object {
  "instance: bar": [Function anonymous],
  "instance: foo": [Function anonymous]
}
2
Luke Schoen

Wie wäre es mit

this.f = window["instance:" + a] = function(){};

Der einzige Nachteil ist, dass die Funktion in ihrer toSource-Methode keinen Namen anzeigen würde. Das ist normalerweise nur ein Problem für Debugger. 

2
entonio

So legen Sie den Namen einer anonymen Funktion exists fest:
(Basierend auf @ Marcoscs Antwort)

var anonymous = function() { return true; }

var name = 'someName';
var strFn = anonymous.toString().replace('function ', 'return function ' + name);
var fn = new Function(strFn)();

console.log(fn()); // —> true

Demo .

Hinweis: Tu es nicht; /

2

Danke, Marcosc! Aufbauend auf seiner Antwort, wenn Sie any - Funktion umbenennen möchten, verwenden Sie diese:

// returns the function named with the passed name
function namedFunction(name, fn) {
    return new Function('fn',
        "return function " + name + "(){ return fn.apply(this,arguments)}"
    )(fn)
}
0
B T

Ich hatte besseres Glück, Darrens Antwort und Kyernetikos Antwort zu kombinieren.

const nameFunction = function (fn, name) {
  return Object.defineProperty(fn, 'name', {value: name, configurable: true});
};

/* __________________________________________________________________________ */

let myFunc = function oldName () {};

console.log(myFunc.name); // oldName

myFunc = nameFunction(myFunc, 'newName');

console.log(myFunc.name); // newName

Hinweis: configurable ist auf true gesetzt, um der ES2015-Standardspezifikation für Function.name zu entsprechen1

Dies hat insbesondere dazu beigetragen, dass ein Fehler in Webpack umgangen wurde, der diesem ähnelt.

Update: Ich dachte daran, dies als npm-Paket zu veröffentlichen, aber dieses Paket von sindresorhus macht genau das gleiche.

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
0
Scott Rudiger

Diese Dienstprogrammfunktion fügt mehrere Funktionen (unter Verwendung eines benutzerdefinierten Namens) in einer zusammen. Die einzige Voraussetzung ist, dass die bereitgestellten Funktionen am Anfang und am Ende ihres Bereichs richtig "neu ausgekleidet" sind.

const createFn = function(name, functions, strict=false) {

    var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];

    for(var i=0, j=functions.length; i<j; i++) {
        var str = functions[i].toString();
        var s = str.indexOf(cr) + 1;
        a.Push(str.substr(s, str.lastIndexOf(cr) - s));
    }
    if(strict == true) {
        a.unshift('\"use strict\";' + cr)
    }
    return new Function(a.join(cr) + cr + '}')();
}

// test
var a = function(p) {
    console.log("this is from a");
}
var b = function(p) {
    console.log("this is from b");
}
var c = function(p) {
    console.log("p == " + p);
}

var abc = createFn('aGreatName', [a,b,c])

console.log(abc) // output: function aGreatName()

abc(123)

// output
this is from a
this is from b
p == 123
0
MetalGodwin

Es gibt zwei Methoden, um dies zu erreichen, und sie haben ihre Vor- und Nachteile.


name Eigenschaftendefinition

Definieren unveränderlich nameEigenschaft einer Funktion.

Pros

  • Für den Namen steht jedes Zeichen zur Verfügung. (zB () 全 {}/1/얏호/ :D #GO(@*#%! /*)

Cons

  • Der syntaktische ("expressionale") Name der Funktion [] stimmt möglicherweise nicht mit dem Wert ihrer name-Eigenschaft überein.

Funktionsausdruck Auswertung

Erstellen einer named - Funktion Ausdruck und Auswertung mit Function-Konstruktor.

Pros

  • Der syntaktische ("expressionale") Name der Funktion [] entspricht immer dem Wert der name-Eigenschaft.

Cons

  • Für den Namen sind keine Whitespaces (und usw.) verfügbar.
  • Ausdruck-injizierbar (z. B. für die Eingabe (){}/1// ist der Ausdruck return function (){}/1//() {}, gibt NaN anstelle einer Funktion.).

const demoeval = expr => (new Function(`return ${expr}`))();

// `name` property definition
const method1 = func_name => {
    const anon_func = function() {};
    Object.defineProperty(anon_func, "name", {value: func_name, writable: false});
    return anon_func;
};

const test11 = method1("DEF_PROP"); // No whitespace
console.log("DEF_PROP?", test11.name); // "DEF_PROP"
console.log("DEF_PROP?", demoeval(test11.toString()).name); // ""

const test12 = method1("DEF PROP"); // Whitespace
console.log("DEF PROP?", test12.name); // "DEF PROP"
console.log("DEF PROP?", demoeval(test12.toString()).name); // ""

// Function expression evaluation
const method2 = func_name => demoeval(`function ${func_name}() {}`);

const test21 = method2("EVAL_EXPR"); // No whitespace
console.log("EVAL_EXPR?", test21.name); // "EVAL_EXPR"
console.log("EVAL_EXPR?", demoeval(test21.toString()).name); // "EVAL_EXPR"

const test22 = method2("EVAL EXPR"); // Uncaught SyntaxError: Unexpected identifier

Ich habe viel mit diesem Problem zu kämpfen. @Albin-Lösung hat bei der Entwicklung wie ein Zauber gewirkt, aber es hat nicht funktioniert, als ich es auf Produktion umgestellt habe. Nach einigem Debuggen wurde mir klar, wie ich das erreichen konnte, was ich brauchte. Ich verwende ES6 mit CRA (create-react-app), was bedeutet, dass es von Webpack gebündelt wird.

Nehmen wir an, Sie haben eine Datei, die die Funktionen exportiert, die Sie benötigen:

myFunctions.js

export function setItem(params) {
  // ...
}

export function setUser(params) {
  // ...
}

export function setPost(params) {
  // ...
}

export function setReply(params) {
  // ...
}

Und Sie müssen diese Funktionen dynamisch an anderer Stelle aufrufen:

myApiCalls.js

import * as myFunctions from 'path_to/myFunctions';
/* note that myFunctions is imported as an array,
 * which means its elements can be easily accessed
 * using an index. You can console.log(myFunctions).
 */

function accessMyFunctions(res) {
  // lets say it receives an API response
  if (res.status === 200 && res.data) {
    const { data } = res;
    // I want to read all properties in data object and 
    // call a function based on properties names.
    for (const key in data) {
      if (data.hasOwnProperty(key)) {
        // you can skip some properties that are usually embedded in
        // a normal response
        if (key !== 'success' && key !== 'msg') {
          // I'm using a function to capitalize the key, which is
          // used to dynamically create the function's name I need.
          // Note that it does not create the function, it's just a
          // way to access the desired index on myFunctions array.
          const name = `set${capitalizeFirstLetter(key)}`;
          // surround it with try/catch, otherwise all unexpected properties in
          // data object will break your code.
          try {
            // finally, use it.
            myFunctions[name](data[key]);
          } catch (error) {
            console.log(name, 'does not exist');
            console.log(error);
          }
        }
      }
    }
  }
}

0
Rodrigo M.

Wenn Sie eine dynamische Funktion wie die __call-Funktion in PHP haben möchten, können Sie Proxies verwenden.

const target = {};

const handler = {
  get: function (target, name) {
    return (myArg) => {
      return new Promise(resolve => setTimeout(() => resolve('some' + myArg), 600))
    }
  }
};

const proxy = new Proxy(target, handler);

(async function() {
  const result = await proxy.foo('string')
  console.log('result', result) // 'result somestring' after 600 ms
})()
0
blablabla