Ho questo:
this.f = function instance(){};
Mi piacerebbe avere questo:
this.f = function ["instance:" + a](){};
Ho questo:
this.f = function instance(){};
Mi piacerebbe avere questo:
this.f = function ["instance:" + a](){};
this["instance" + a] = function() { }. Non mi era chiaro.
Risposte:
Come altri hanno già detto, questa non è la soluzione più veloce né più consigliata. La soluzione di Marcosc di seguito è la strada da percorrere.
Puoi usare eval:
var code = "this.f = function " + instance + "() {...}";
eval(code);
eval()(il Functioncostruttore lo fa all'interno).
Questo fondamentalmente lo farà al livello più semplice:
"use strict";
var name = "foo";
var func = new Function(
"return function " + name + "(){ alert('sweet!')}"
)();
//call it, to test it
func();
Se vuoi avere più fantasia, ho scritto un articolo su " Nomi di funzioni dinamiche in JavaScript ".
evalper valutare il javascript, aprendo così il tuo codice a una serie di vulnerabilità.
È possibile utilizzare Object.defineProperty come indicato nella MDN JavaScript Reference [1]:
var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
function fn(), fnessendo il nome originale. Strano.
Nei motori recenti, puoi farlo
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})mio codice, poiché penso che sia forse un po 'più naturale e comprensibile.
{[expr]: val}è un inizializzatore di oggetti (come è un oggetto JSON) dove exprè qualche espressione; qualunque cosa valuti è la chiave. {myFn (..){..} }è una scorciatoia per {myFn: function myFn(..){..} }. Nota che function myFn(..) {..}può essere usato come un'espressione proprio come una funzione anonima, myFnavrebbe solo un nome. L'ultimo [name]è solo accedere al membro dell'oggetto (proprio come obj.keyo obj['key']). ...è l'operatore di diffusione. (Fonte principale: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
this. Ad esempio obj={x:7,getX(){return this.x}}; obj.getX=nameFunction('name',obj.getX); obj.getX();, non funzionerà. Puoi modificare la tua risposta e usarla function nameFunction(name, body) { return {[name](...args) {return body.apply(this, args)}}[name] }invece!
Penso che la maggior parte dei suggerimenti qui non siano ottimali, utilizzando eval, soluzioni hacky o wrapper. A partire da ES2015 i nomi vengono dedotti dalla posizione sintattica per variabili e proprietà.
Quindi funzionerà perfettamente:
const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'
Resisti alla tentazione di creare metodi di fabbrica di funzioni con nome poiché non saresti in grado di passare la funzione dall'esterno e adattarla alla posizione sintattica per dedurne il nome. Allora è già troppo tardi. Se ne hai davvero bisogno, devi creare un wrapper. Qualcuno l'ha fatto qui, ma quella soluzione non funziona per le classi (che sono anche funzioni).
Una risposta molto più approfondita con tutte le varianti delineate è stata scritta qui: https://stackoverflow.com/a/9479081/633921
Che dire
this.f = window["instance:" + a] = function(){};
L'unico inconveniente è che la funzione nel suo metodo toSource non indica un nome. Di solito è solo un problema per i debugger.
La sintassi function[i](){}implica un oggetto con valori di proprietà che sono funzioni function[], indicizzati dal nome, [i].
Così
{"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]conserverà l'identificazione del nome della funzione. Vedere le note di seguito in merito :.
Così,
javascript: alert(
new function(a){
this.f={"instance:1":function(){}, "instance:A":function(){}} ["instance:"+a]
}("A") . toSource()
);
viene visualizzato ({f:(function () {})})in FireFox.
(Questa è quasi la stessa idea di questa soluzione , solo che utilizza un oggetto generico e non popola più direttamente l'oggetto finestra con le funzioni.)
Questo metodo popola in modo esplicito l'ambiente con instance:x.
javascript: alert(
new function(a){
this.f=eval("instance:"+a+"="+function(){})
}("A") . toSource()
);
alert(eval("instance:A"));
display
({f:(function () {})})
e
function () {
}
Sebbene la funzione di proprietà faccia friferimento a anonymous functione not instance:x, questo metodo evita diversi problemi con questa soluzione .
javascript: alert(
new function(a){
eval("this.f=function instance"+a+"(){}")
}("A") . toSource()
);
alert(instanceA); /* is undefined outside the object context */
viene visualizzato solo
({f:(function instanceA() {})})
:rende il javascript function instance:a(){}non valido.eval.Quanto segue non è necessariamente problematico,
instanceAfunzione non è direttamente disponibile per l'uso comeinstanceA()e quindi è molto più coerente con il contesto del problema originale.
Alla luce di queste considerazioni,
this.f = {"instance:1": function instance1(){},
"instance:2": function instance2(){},
"instance:A": function instanceA(){},
"instance:Z": function instanceZ(){}
} [ "instance:" + a ]
mantiene l'ambiente informatico globale con la semantica e la sintassi dell'esempio OP il più possibile.
(name => ({[name]:function(){}})[name])('test')funziona ma (name => {var x={}; x[name] = function(){}; return x[name];})('test')non funziona
La risposta più votata ha già definito il corpo della funzione [Stringa]. Stavo cercando la soluzione per rinominare il nome di una funzione già dichiarata e finalmente dopo un'ora di fatica ce l'ho fatta. Esso:
.toString() metodofunctione(new Function()costruttorefunction 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){...}
I metodi dinamici di un oggetto possono essere creati utilizzando le estensioni letterali oggetto fornite da 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);
L'output dell'esecuzione del codice sopra è:
"called method instance: foo"
"called method instance: bar"
Object {
"instance: bar": [Function anonymous],
"instance: foo": [Function anonymous]
}
o={}; o[name]=(()=>{})piuttosto chefunction <<name>>(){}
Per impostare il nome di una funzione anonima esistente :
(Basato sulla risposta di @ 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
Demo .
Nota : non farlo; /
Ci sono due metodi per raggiungere questo obiettivo e hanno i loro pro e contro.
name definizione di proprietàDefinizione della name proprietà immutabile di una funzione.
() 全 {}/1/얏호/ :D #GO(@*#%! /*)namevalore della sua proprietà.Creare un'espressione di funzione denominata e valutarla con il costruttore.Function
namevalore della sua proprietà.(){}/1//, l'espressione è return function (){}/1//() {}, dà NaNinvece di una funzione.).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
Se vuoi avere una funzione dinamica come la __callfunzione in PHP, puoi usare Proxy.
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
})()
Puoi usare il nome della funzione dinamica e parametri come questo.
1) Definire la funzione Separate e chiamarla
let functionName = "testFunction";
let param = {"param1":1 , "param2":2};
var func = new Function(
"return " + functionName
)();
func(param);
function testFunction(params){
alert(params.param1);
}
2) Definire il codice funzione dinamico
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);
Questa funzione di utilità fonde più funzioni in una (utilizzando un nome personalizzato), l'unico requisito è che le funzioni fornite siano correttamente "nuove allineate" all'inizio e alla fine del suo 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
Ho avuto più fortuna combinando la risposta di Darren e la risposta di 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
Nota: configurableè impostato per truecorrispondere alla specifica ES2015 standard per Function.name 1
Ciò ha particolarmente aiutato a aggirare un errore in Webpack simile a questo .
Aggiornamento: stavo pensando di pubblicarlo come pacchetto npm, ma questo pacchetto di sindresorhus fa esattamente la stessa cosa.
Ho lottato molto con questo problema. La soluzione @Albin ha funzionato a meraviglia durante lo sviluppo, ma non ha funzionato quando l'ho modificata in produzione. Dopo un po 'di debug ho capito come ottenere ciò di cui avevo bisogno. Sto usando ES6 con CRA (create-react-app), il che significa che è fornito in bundle da Webpack.
Diciamo che hai un file che esporta le funzioni di cui hai bisogno:
myFunctions.js
export function setItem(params) {
// ...
}
export function setUser(params) {
// ...
}
export function setPost(params) {
// ...
}
export function setReply(params) {
// ...
}
E devi chiamare dinamicamente queste funzioni altrove:
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);
}
}
}
}
}
}
il modo migliore è creare un oggetto con un elenco di funzioni dinamiche come:
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();
Potrei perdere l'ovvio qui, ma cosa c'è di sbagliato nell'aggiungere solo il nome? le funzioni vengono richiamate indipendentemente dal loro nome. i nomi vengono utilizzati solo per motivi di scoping. se lo assegni a una variabile ed è nell'ambito, può essere chiamato. Ciò che accade è che stai eseguendo una variabile che sembra essere una funzione. se devi avere un nome per motivi di identificazione durante il debug, inseriscilo tra la funzione parola chiave e la parentesi graffa di apertura.
var namedFunction = function namedFunction (a,b) {return a+b};
alert(namedFunction(1,2));
alert(namedFunction.name);
alert(namedFunction.toString());
un approccio alternativo è racchiudere la funzione in uno shim esterno rinominato, che puoi anche passare in un wrapper esterno, se non vuoi sporcare lo spazio dei nomi circostante. se si desidera creare effettivamente dinamicamente la funzione da stringhe (cosa che fa la maggior parte di questi esempi), è banale rinominare l'origine per fare ciò che si desidera. se tuttavia si desidera rinominare le funzioni esistenti senza influire sulla loro funzionalità quando viene chiamato altrove, uno shim è l'unico modo per ottenerlo.
(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è utile poiché ora viene dedotto dalla variabile e dalle proprietà. Viene anche utilizzato nelle tracce dello stack. Es var fn = function(){}; console.log(fn.name). È immutabile, quindi non puoi modificarlo in seguito. Se scrivi un metodo factory che nomina tutte le funzioni, fnquesto renderà più difficile il debug.
this["instance"] = function() { }