J'ai ceci:
this.f = function instance(){};
J'aimerais avoir ceci:
this.f = function ["instance:" + a](){};
J'ai ceci:
this.f = function instance(){};
J'aimerais avoir ceci:
this.f = function ["instance:" + a](){};
this["instance" + a] = function() { }
. Ce n'était pas clair pour moi.
Réponses:
Comme d'autres l'ont mentionné, ce n'est pas la solution la plus rapide ni la plus recommandée. La solution de Marcosc ci - dessous est la voie à suivre.
Vous pouvez utiliser eval:
var code = "this.f = function " + instance + "() {...}";
eval(code);
eval()
(le Function
constructeur le fait à l'intérieur).
Cela le fera essentiellement au niveau le plus simple:
"use strict";
var name = "foo";
var func = new Function(
"return function " + name + "(){ alert('sweet!')}"
)();
//call it, to test it
func();
Si vous voulez avoir plus de fantaisie, j'ai écrit un article sur " Noms de fonctions dynamiques en JavaScript ".
eval
pour évaluer le javascript - ouvrant ainsi votre code à une multitude de vulnérabilités.
Vous pouvez utiliser Object.defineProperty comme indiqué dans la référence JavaScript MDN [1]:
var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
function fn()
, fn
étant le nom d'origine. Bizarre.
Dans les moteurs récents, vous pouvez faire
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"
Object.defineProperty(func, 'name', {value: name})
mon propre code, car je pense que c'est peut-être un peu plus naturel et compréhensible.
{[expr]: val}
est un initialiseur d'objet (tout comme un objet JSON) où se expr
trouve une expression; tout ce qu'il évalue est la clé. {myFn (..){..} }
est un raccourci pour {myFn: function myFn(..){..} }
. Notez que function myFn(..) {..}
peut être utilisé comme une expression tout comme une fonction anonyme, seule myFn
aurait un nom. Le dernier [name]
est simplement d'accéder au membre de l'objet (comme obj.key
ou obj['key']
). ...
est l'opérateur de diffusion. (Source principale: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
this
. Par exemple obj={x:7,getX(){return this.x}}; obj.getX=nameFunction('name',obj.getX); obj.getX();
ne fonctionnera pas. Vous pouvez modifier votre réponse et l'utiliser à la function nameFunction(name, body) { return {[name](...args) {return body.apply(this, args)}}[name] }
place!
Je pense que la plupart des suggestions ici sont sous-optimales, en utilisant eval, des solutions hacky ou des wrappers. À partir de ES2015, les noms sont déduits de la position syntaxique des variables et des propriétés.
Donc, cela fonctionnera très bien:
const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'
Résistez à la tentation de créer des méthodes de fabrique de fonctions nommées car vous ne pourriez pas passer la fonction de l'extérieur et la mettre à niveau dans la position syntaxique pour déduire son nom. Alors c'est déjà trop tard. Si vous en avez vraiment besoin, vous devez créer un wrapper. Quelqu'un l'a fait ici, mais cette solution ne fonctionne pas pour les classes (qui sont également des fonctions).
Une réponse beaucoup plus approfondie avec toutes les variantes décrites a été écrite ici: https://stackoverflow.com/a/9479081/633921
Qu'en est-il de
this.f = window["instance:" + a] = function(){};
Le seul inconvénient est que la fonction dans sa méthode toSource n'indique pas de nom. Ce n'est généralement un problème que pour les débogueurs.
La syntaxe function[i](){}
implique un objet avec des valeurs de propriété qui sont des fonctions function[]
,, indexées par le nom [i]
,.
Ainsi
{"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]
conservera l'identification du nom de la fonction. Voir les notes ci-dessous concernant:
.
Donc,
javascript: alert(
new function(a){
this.f={"instance:1":function(){}, "instance:A":function(){}} ["instance:"+a]
}("A") . toSource()
);
s'affiche ({f:(function () {})})
dans FireFox.
(C'est presque la même idée que cette solution , seulement elle utilise un objet générique et ne remplit plus directement l'objet window avec les fonctions.)
Cette méthode remplit explicitement l'environnement avec instance:x
.
javascript: alert(
new function(a){
this.f=eval("instance:"+a+"="+function(){})
}("A") . toSource()
);
alert(eval("instance:A"));
affiche
({f:(function () {})})
et
function () {
}
Bien que la fonction de propriété f
référence un anonymous function
et non instance:x
, cette méthode évite plusieurs problèmes avec cette solution .
javascript: alert(
new function(a){
eval("this.f=function instance"+a+"(){}")
}("A") . toSource()
);
alert(instanceA); /* is undefined outside the object context */
affiche uniquement
({f:(function instanceA() {})})
:
rend le javascriptfunction instance:a(){}
invalide.eval
.Ce qui suit n'est pas nécessairement problématique,
instanceA
fonction n'est pas directement disponible pour une utilisationinstanceA()
et est donc beaucoup plus cohérent avec le contexte du problème d'origine.
Compte tenu de ces considérations,
this.f = {"instance:1": function instance1(){},
"instance:2": function instance2(){},
"instance:A": function instanceA(){},
"instance:Z": function instanceZ(){}
} [ "instance:" + a ]
maintient l'environnement informatique global avec la sémantique et la syntaxe de l'exemple OP autant que possible.
(name => ({[name]:function(){}})[name])('test')
fonctionne mais (name => {var x={}; x[name] = function(){}; return x[name];})('test')
ne fonctionne pas
La réponse la plus votée a déjà un corps de fonction [String] défini. Je cherchais la solution pour renommer le nom de la fonction déjà déclarée et finalement après une heure de lutte, je l'ai traité. Il:
.toString()
méthodefunction
et(
new Function()
constructeurfunction 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){...}
Les méthodes dynamiques d'un objet peuvent être créées à l'aide des extensions littérales d'objet fournies par ECMAScript 2015 (ES6):
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);
Le résultat de l'exécution du code ci-dessus est:
"called method instance: foo"
"called method instance: bar"
Object {
"instance: bar": [Function anonymous],
"instance: foo": [Function anonymous]
}
o={}; o[name]=(()=>{})
plutôt qu'àfunction <<name>>(){}
Pour définir le nom d'une fonction anonyme existante :
(Basé sur la réponse de @ Marcosc)
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
Démo .
Remarque : ne le faites pas; /
Il existe deux méthodes pour y parvenir, et elles ont leurs avantages et leurs inconvénients.
name
définition de propriétéDéfinition de la name
propriété immuable d'une fonction.
() 全 {}/1/얏호/ :D #GO(@*#%! /*
)name
valeur de propriété.Créer une expression de fonction nommée et l' évaluer avec le constructeur.Function
name
valeur de propriété.(){}/1//
, l'expression est return function (){}/1//() {}
, donne NaN
au lieu d'une fonction.).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
Si vous voulez avoir une fonction dynamique comme la __call
fonction en PHP, vous pouvez utiliser des Proxies.
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
})()
Vous pouvez utiliser le nom de la fonction dynamique et des paramètres comme celui-ci.
1) Définissez la fonction Separate et appelez-la
let functionName = "testFunction";
let param = {"param1":1 , "param2":2};
var func = new Function(
"return " + functionName
)();
func(param);
function testFunction(params){
alert(params.param1);
}
2) Définir le code de fonction dynamique
let functionName = "testFunction(params)";
let param = {"param1":"1" , "param2":"2"};
let functionBody = "{ alert(params.param1)}";
var func = new Function(
"return function " + functionName + functionBody
)();
func(param);
Merci Marcosc! En vous basant sur sa réponse, si vous souhaitez renommer une fonction, utilisez ceci:
// 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)
}
Cette fonction utilitaire fusionne plusieurs fonctions en une seule (en utilisant un nom personnalisé), la seule exigence est que les fonctions fournies soient correctement «nouvelle ligne» au début et à la fin de son scoop.
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
J'ai eu plus de chance à combiner la réponse de Darren et la réponse de kyernetikos .
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
Remarque: configurable
est défini sur true
pour correspondre à la spécification standard ES2015 pour Function.name 1
Cela a particulièrement aidé à contourner une erreur dans Webpack similaire à celle-ci .
Mise à jour: je pensais publier ceci en tant que package npm, mais ce package de sindresorhus fait exactement la même chose.
J'ai beaucoup lutté avec ce problème. La solution @Albin a fonctionné à merveille pendant le développement, mais elle n'a pas fonctionné lorsque je l'ai changée en production. Après quelques débogages, j'ai réalisé comment réaliser ce dont j'avais besoin. J'utilise ES6 avec CRA (create-react-app), ce qui signifie qu'il est fourni par Webpack.
Disons que vous avez un fichier qui exporte les fonctions dont vous avez besoin:
myFunctions.js
export function setItem(params) {
// ...
}
export function setUser(params) {
// ...
}
export function setPost(params) {
// ...
}
export function setReply(params) {
// ...
}
Et vous devez appeler dynamiquement ces fonctions ailleurs:
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);
}
}
}
}
}
}
la meilleure façon de créer un objet avec une liste de fonctions dynamiques comme:
const USER = 'user';
const userModule = {
[USER + 'Action'] : function () { ... },
[USER + 'OnClickHandler'] : function () { ... },
[USER + 'OnCreateHook'] : function () { ... },
}
function myFunction() {
console.log('It works!');
}
var name = 'myFunction';
window[name].call();
Il me manque peut-être l'évidence ici, mais qu'est-ce qui ne va pas avec l'ajout du nom? les fonctions sont appelées quel que soit leur nom. les noms ne sont utilisés que pour des raisons de portée. si vous l'assignez à une variable et qu'elle est dans la portée, elle peut être appelée. Il se produit que vous exécutez une variable qui se trouve être une fonction. si vous devez avoir un nom pour des raisons d'identification lors du débogage, insérez-le entre la fonction mot-clé et l'accolade ouvrante.
var namedFunction = function namedFunction (a,b) {return a+b};
alert(namedFunction(1,2));
alert(namedFunction.name);
alert(namedFunction.toString());
une approche alternative consiste à envelopper la fonction dans un shim externe renommé, que vous pouvez également passer dans un wrapper externe, si vous ne voulez pas salir l'espace de noms environnant. si vous souhaitez réellement créer dynamiquement la fonction à partir de chaînes (ce que font la plupart de ces exemples), il est trivial de renommer la source pour faire ce que vous voulez. si toutefois vous souhaitez renommer des fonctions existantes sans affecter leurs fonctionnalités lorsqu'elles sont appelées ailleurs, un shim est le seul moyen d'y parvenir.
(function(renamedFunction) {
alert(renamedFunction(1,2));
alert(renamedFunction.name);
alert(renamedFunction.toString());
alert(renamedFunction.apply(this,[1,2]));
})(function renamedFunction(){return namedFunction.apply(this,arguments);});
function namedFunction(a,b){return a+b};
name
est utile car elle est désormais déduite de la variable et des propriétés. Il est également utilisé dans les traces de pile. Ex var fn = function(){}; console.log(fn.name)
. C'est immuable, vous ne pouvez donc pas le changer plus tard. Si vous écrivez une méthode d'usine qui nomme toutes les fonctions, fn
cela rendra le débogage plus difficile.
this["instance"] = function() { }