Eu tenho isto:
this.f = function instance(){};
Eu gostaria de ter isto:
this.f = function ["instance:" + a](){};
Eu tenho isto:
this.f = function instance(){};
Eu gostaria de ter isto:
this.f = function ["instance:" + a](){};
this["instance" + a] = function() { }
. Isso não ficou claro para mim.
Respostas:
Como outros mencionaram, esta não é a solução mais rápida nem a mais recomendada. A solução de Marcosc abaixo é o caminho a percorrer.
Você pode usar eval:
var code = "this.f = function " + instance + "() {...}";
eval(code);
eval()
(o Function
construtor faz isso por dentro).
Isso basicamente fará no nível mais simples:
"use strict";
var name = "foo";
var func = new Function(
"return function " + name + "(){ alert('sweet!')}"
)();
//call it, to test it
func();
Se você quiser ser mais sofisticado, escrevi um artigo sobre " Nomes de funções dinâmicas em JavaScript ".
eval
para avaliar o javascript - abrindo assim o seu código para uma série de vulnerabilidades.
Você pode usar Object.defineProperty conforme observado na Referência de JavaScript MDN [1]:
var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
function fn()
, fn
sendo o nome original. Esquisito.
Em motores recentes, você pode fazer
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})
meu próprio código, pois acho que talvez seja um pouco mais natural e compreensível.
{[expr]: val}
é um inicializador de objeto (como é um objeto JSON) onde expr
está alguma expressão; tudo o que for avaliado é a chave. {myFn (..){..} }
é uma abreviação de {myFn: function myFn(..){..} }
. Observe que function myFn(..) {..}
pode ser usada como uma expressão, assim como uma função anônima, apenas myFn
teria um nome. O último [name]
é apenas acessar o membro do objeto (assim como obj.key
ou obj['key']
). ...
é o operador de propagação. (Fonte principal: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
this
. Por exemplo obj={x:7,getX(){return this.x}}; obj.getX=nameFunction('name',obj.getX); obj.getX();
, não funciona. Você pode editar sua resposta e usar function nameFunction(name, body) { return {[name](...args) {return body.apply(this, args)}}[name] }
!
Eu acho que a maioria das sugestões aqui são subótimas, usando eval, soluções hacky ou invólucros. A partir do ES2015, os nomes são inferidos da posição sintática para variáveis e propriedades.
Então, isso vai funcionar muito bem:
const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'
Resista à tentação de criar métodos de fábrica de função nomeados, pois você não seria capaz de passar a função de fora e retrofit para a posição sintática para inferir seu nome. Então já é tarde demais. Se você realmente precisa disso, deve criar um invólucro. Alguém fez isso aqui, mas essa solução não funciona para classes (que também são funções).
Uma resposta muito mais detalhada com todas as variantes descritas foi escrita aqui: https://stackoverflow.com/a/9479081/633921
A respeito
this.f = window["instance:" + a] = function(){};
A única desvantagem é que a função em seu método toSource não indica um nome. Normalmente, isso é um problema apenas para depuradores.
A sintaxe function[i](){}
implica em um objeto com valores de propriedade que são funções function[]
,, indexados pelo nome [i]
,.
Assim
{"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]
preservará a identificação do nome da função. Veja as notas abaixo a respeito :
.
Então,
javascript: alert(
new function(a){
this.f={"instance:1":function(){}, "instance:A":function(){}} ["instance:"+a]
}("A") . toSource()
);
exibe ({f:(function () {})})
no FireFox.
(Esta é quase a mesma ideia que esta solução , só que usa um objeto genérico e não preenche mais diretamente o objeto de janela com as funções.)
Este método preenche explicitamente o ambiente com instance:x
.
javascript: alert(
new function(a){
this.f=eval("instance:"+a+"="+function(){})
}("A") . toSource()
);
alert(eval("instance:A"));
monitores
({f:(function () {})})
e
function () {
}
Embora a função de propriedade f
faça referência a um anonymous function
e não instance:x
, este método evita vários problemas com essa solução .
javascript: alert(
new function(a){
eval("this.f=function instance"+a+"(){}")
}("A") . toSource()
);
alert(instanceA); /* is undefined outside the object context */
exibe apenas
({f:(function instanceA() {})})
:
torna o javascript function instance:a(){}
inválido.eval
.O seguinte não é necessariamente problemático,
instanceA
função não está diretamente disponível para uso comoinstanceA()
e, portanto, é muito mais consistente com o contexto do problema original.
Dadas essas considerações,
this.f = {"instance:1": function instance1(){},
"instance:2": function instance2(){},
"instance:A": function instanceA(){},
"instance:Z": function instanceZ(){}
} [ "instance:" + a ]
mantém o ambiente de computação global com a semântica e sintaxe do exemplo de OP tanto quanto possível.
(name => ({[name]:function(){}})[name])('test')
funciona, mas (name => {var x={}; x[name] = function(){}; return x[name];})('test')
não
A resposta mais votada já possui o corpo da função [String] definido. Eu estava procurando a solução para renomear o nome da função já declarada e finalmente após uma hora de luta resolvi o problema. Isto:
.toString()
métodofunction
e(
new Function()
construtorfunction 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){...}
Os métodos dinâmicos de um objeto podem ser criados usando as extensões literais de objeto fornecidas pelo 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);
O resultado da execução do código acima é:
"called method instance: foo"
"called method instance: bar"
Object {
"instance: bar": [Function anonymous],
"instance: foo": [Function anonymous]
}
o={}; o[name]=(()=>{})
vez defunction <<name>>(){}
Para definir o nome de uma função anônima existente :
(Com base na resposta 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
Demo .
Nota : Não faça isso; /
Existem dois métodos para conseguir isso, e eles têm seus prós e contras.
name
definição de propriedadeDefinindo a name
propriedade imutável de uma função.
() 全 {}/1/얏호/ :D #GO(@*#%! /*
)name
valor de propriedade.Fazendo uma expressão de função nomeada e avaliando -a com o construtor.Function
name
valor de propriedade.(){}/1//
, a expressão é return function (){}/1//() {}
, dá em NaN
vez de uma função.).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 você deseja ter uma função dinâmica como a __call
função em PHP, você pode usar 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
})()
Você pode usar o nome da função dinâmica e parâmetros como este.
1) Defina a função Separate e chame-a
let functionName = "testFunction";
let param = {"param1":1 , "param2":2};
var func = new Function(
"return " + functionName
)();
func(param);
function testFunction(params){
alert(params.param1);
}
2) Definir código de função dinâmico
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);
Esta função de utilitário mescla várias funções em uma (usando um nome personalizado), o único requisito é que as funções fornecidas sejam devidamente "alinhadas" no início e no final de seu furo.
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
Eu tive melhor sorte na combinação de resposta de Darren e resposta das 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
é definido para true
corresponder à especificação ES2015 padrão para Function.name 1
Isso ajudou especialmente a contornar um erro no Webpack semelhante a este .
Atualização: eu estava pensando em publicar isso como um pacote npm, mas este pacote do sindresorhus faz exatamente a mesma coisa.
Eu lutei muito com esse problema. A solução @Albin funcionou perfeitamente durante o desenvolvimento, mas não funcionou quando mudei para produção. Depois de algumas depurações, percebi como conseguir o que precisava. Estou usando o ES6 com CRA (criar-reagir-app), o que significa que é empacotado pelo Webpack.
Digamos que você tenha um arquivo que exporta as funções de que precisa:
myFunctions.js
export function setItem(params) {
// ...
}
export function setUser(params) {
// ...
}
export function setPost(params) {
// ...
}
export function setReply(params) {
// ...
}
E você precisa chamar dinamicamente essas funções em outro lugar:
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);
}
}
}
}
}
}
a melhor maneira é criar um objeto com lista de funções dinâmicas como:
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();
Posso estar perdendo o óbvio aqui, mas o que há de errado em apenas adicionar o nome? as funções são chamadas independentemente de seu nome. nomes são usados apenas por razões de escopo. se você atribuí-lo a uma variável e está no escopo, pode ser chamado. O que acontece é que você está executando uma variável que por acaso é uma função. se você deve ter um nome por motivos de identificação ao depurar, insira-o entre a função de palavra-chave e a chave de abertura.
var namedFunction = function namedFunction (a,b) {return a+b};
alert(namedFunction(1,2));
alert(namedFunction.name);
alert(namedFunction.toString());
uma abordagem alternativa é envolver a função em um shim externo renomeado, que você também pode passar para um wrapper externo, se não quiser sujar o namespace circundante. se você deseja realmente criar dinamicamente a função a partir de strings (o que a maioria desses exemplos faz), é trivial renomear a fonte para fazer o que você deseja. se, no entanto, você deseja renomear funções existentes sem afetar sua funcionalidade quando chamadas em outro lugar, um shim é a única maneira de conseguir isso.
(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
é útil, pois agora é inferido de variáveis e propriedades. Também é usado em rastreamentos de pilha. Ex var fn = function(){}; console.log(fn.name)
. É imutável, então você não pode alterá-lo depois. Se você escrever um método de fábrica que nomeie todas as funções fn
, isso tornará a depuração mais difícil.
this["instance"] = function() { }