Come eseguire una funzione JavaScript quando ho il suo nome come stringa


1051

Ho il nome di una funzione in JavaScript come stringa. Come posso convertirlo in un puntatore a funzione in modo da poterlo chiamare in seguito?

A seconda delle circostanze, potrei aver bisogno di passare vari argomenti anche nel metodo.

Alcune funzioni possono assumere la forma di namespace.namespace.function(args[...]).

Risposte:


1440

Non usare evalse non assolutamente, positivamente altra scelta.

Come è stato menzionato, utilizzare qualcosa di simile sarebbe il modo migliore per farlo:

window["functionName"](arguments);

Che, tuttavia, non funzionerà con una funzione namespace:

window["My.Namespace.functionName"](arguments); // fail

Ecco come lo faresti:

window["My"]["Namespace"]["functionName"](arguments); // succeeds

Per facilitare ciò e offrire una certa flessibilità, ecco una comoda funzione:

function executeFunctionByName(functionName, context /*, args */) {
  var args = Array.prototype.slice.call(arguments, 2);
  var namespaces = functionName.split(".");
  var func = namespaces.pop();
  for(var i = 0; i < namespaces.length; i++) {
    context = context[namespaces[i]];
  }
  return context[func].apply(context, args);
}

Lo chiameresti così:

executeFunctionByName("My.Namespace.functionName", window, arguments);

Nota, puoi passare in qualsiasi contesto tu voglia, quindi farebbe lo stesso come sopra:

executeFunctionByName("Namespace.functionName", My, arguments);

4
sai che non hai bisogno dell'intero costrutto "func"? "context.apply" da solo va bene
annakata,

16
Certo, lo so - ma il modo in cui ho scritto la funzione fornisce un po 'di chiarezza a coloro che la leggono che potrebbero non comprendere completamente ciò che sta accadendo. Ho scritto questa funzione realizzando che le persone che la leggono potrebbero aver bisogno di aiuto. Ti fornirò un supplente, dal momento che hai chiesto ...
Jason Bunting l'

108
Grattalo: il codice è abbastanza chiaro e quelli che lo sanno lo sanno. Se sei come me e sai cosa stai facendo, puoi apportare tali modifiche da solo se hai utilizzato questo codice. Stack Overflow è per educare gli altri e penso che il mio codice sia più facile da capire per i principianti. Grazie comunque!
Jason Bunting,

4
C'è una situazione in cui la finestra ["funcName"] restituisce un valore indefinito? Questo è il problema che sto riscontrando al momento. Il codice chiamante e la funzione sono definiti in due file js separati. Ho provato ad aggiungerli allo stesso file ma questo non ha fatto differenza.
codemonkey

5
Penso che ci sia un problema qui. Quando chiami My.Namespace.functionName(), thisfarà riferimento My.Namespaceall'oggetto. Ma quando chiami executeFunctionByName("My.Namespace.functionName", window), non c'è modo di fare thisriferimento alla stessa cosa. Forse dovrebbe usare l'ultimo spazio dei nomi come ambito o windowse non ci sono spazi dei nomi. Oppure potresti consentire all'utente di specificare l'ambito come argomento.
JW.

100

Ho pensato di pubblicare una versione leggermente modificata della funzione molto utile di Jason Bunting .

Innanzitutto, ho semplificato la prima istruzione fornendo un secondo parametro a slice () . La versione originale funzionava bene in tutti i browser tranne IE.

In secondo luogo, ho sostituito questo con il contesto nella dichiarazione di ritorno; in caso contrario, questo indicava sempre la finestra durante l'esecuzione della funzione target.

function executeFunctionByName(functionName, context /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    var namespaces = functionName.split(".");
    var func = namespaces.pop();
    for (var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
    }
    return context[func].apply(context, args);
}

Non c'è controllo per vedere se "functionName" esiste davvero?
Crashalot,

Penso che la risposta di Mac sia sottovalutata. Non sono un esperto ma sembra ben ponderato e robusto.
Martin Hansen Lennox,

65

La risposta a questa altra domanda ti mostra come farlo: equivalente JavaScript dei locali di Python ()?

Fondamentalmente, puoi dire

window["foo"](arg1, arg2);

o come molti altri hanno suggerito, puoi semplicemente usare eval:

eval(fname)(arg1, arg2);

anche se questo è estremamente pericoloso a meno che tu non sia assolutamente sicuro di ciò che stai valutando.


6
la prima forma è di gran lunga preferibile
annakata,

19
Usa eval solo come ultima risorsa, quando tutto il resto fallisce.
Jason Bunting,

1
È ... ma funzionerà con funzioni come questa: xyz (args)?
Kieron,

@keiron: si. vedi la mia risposta qui sotto
annakata,

55

Non potresti semplicemente fare questo:

var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();

Puoi anche eseguire qualsiasi altro JavaScript usando questo metodo.


3
funziona quando vengono passati anche argomenti con la funzione
adeel41

Che dire della funzione return?
Peter Denev,

12
In che modo è diverso da eval("My.Namespace.functionName()");?
Developerbmw,

@PeterDenev cambia solo la prima riga invar codeToExecute = "return My.Namespace.functionName()";
Developerbmw

2
@developerbmw, qui è la risposta stackoverflow.com/questions/4599857/...
Tejasvi Hegde

48

Penso che un modo elegante per farlo sia quello di definire le tue funzioni in un oggetto hash. Quindi puoi avere un riferimento a quelle funzioni dall'hash usando la stringa. per esempio

var customObject = {
  customFunction: function(param){...}
};

Quindi puoi chiamare:

customObject['customFunction'](param);

Dove customFunction sarà una stringa corrispondente a una funzione definita nel tuo oggetto.


@ibsenv, grazie per il tuo commento per aiutarmi a identificare questa risposta come la migliore. Ho creato un array di oggetti funzione e, a sua volta, l'ho usato per creare un array di deferred.promises. Ho inserito un codice di esempio di seguito. (Non volevo creare una nuova risposta e prendere in prestito la risposta di Ruben.)
user216661

funzione getMyData (arrayOfObjectsWithIds) {var functionArray = arrayOfObjectsWithIds.map (function (value) {return {myGetDataFunction: MyService.getMyData (value.id)};}) var promises = functionArray.map (function (getDataFunction) {var deferred = $ q.defer (); getDataFunction.myGetDataFunction.success (function (data) {deferred.resolve (data)}). error (function (error) {deferred.reject ();}); return deferred.promise;}); $ q.all (promesse) .then (funzione (dataArray) {// do stuff})};
user216661

Funziona in modo eccellente Aggiungo solo il trattino basso / lodash per verificare se è una funzione. E poi corri
elporfirio il

35

Con ES6 è possibile accedere ai metodi di classe per nome:

class X {
  method1(){
    console.log("1");
  }
  method2(){
    this['method1']();
    console.log("2");
  }
}
let x  = new X();
x['method2']();

l'output sarebbe:

1
2

1
Miglior javascript PURE ... Dio .. elimina la classe non funziona e va bene. Grazie!
KingRider,

1
Questa è la cosa che cercavo da molto tempo. Grazie!
PaladiN,

ES2015 non ha nulla a che fare qui. È possibile raggiungere lo stesso obiettivo utilizzando oggetti puri o la delega del prototipo tramite Object.create(). const myObj = {method1 () {console.log ('1')}, method2 () {console.log ('2')}} myObj ['method1'] (); // 1 myObj ['method2'] (); // 2
sminutoli,

1
Questo è oro !!! Sono sorpreso di non averci mai pensato prima. Bello!!!
thxmike

Penso anche che questo sia il modo migliore per raggiungere il nostro obiettivo.
Chris Jung,

24

Due cose:

  • evitare l'eval, è terribilmente pericoloso e lento

  • in secondo luogo, non importa dove esista la tua funzione, il carattere "globale" è irrilevante. x.y.foo()può essere abilitato tramite x.y['foo']()o x['y']['foo']()o anche window['x']['y']['foo'](). Puoi concatenare indefinitamente in questo modo.


1
ma non puoi fare window ['xyz'] () per chiamare xyz ()
nickf

17

Tutte le risposte presuppongono che le funzioni siano accessibili tramite ambito globale (finestra). Tuttavia, l'OP non ha formulato questa ipotesi.

Se le funzioni vivono in un ambito locale (aka chiusura) e non sono referenziate da qualche altro oggetto locale, sfortuna: devi usare eval()AFAIK, vedi chiamare dinamicamente la funzione locale in javascript


2
Amico (o dudette), grazie mille per averlo sottolineato! Pensavo di impazzire per un secondo.
Funktr0n

13

Devi solo convertire la stringa in un puntatore di window[<method name>]. esempio:

var function_name = "string";
function_name = window[function_name];

e ora puoi usarlo come un puntatore.


Questo sembra essere un modo molto più sicuro.
James Poulose,

12

Ecco il mio contributo alle eccellenti risposte di Jason Bunting / Alex Nazarov, in cui includo il controllo degli errori richiesto da Crashalot.

Dato questo preambolo (inventato):

a = function( args ) {
    console.log( 'global func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] );
    }
};
ns = {};
ns.a = function( args ) {
    console.log( 'namespace func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] ); 
    }
};
name = 'nsa';
n_s_a = [ 'Snowden' ];
noSuchAgency = function(){};

quindi la seguente funzione:

function executeFunctionByName( functionName, context /*, args */ ) {
    var args, namespaces, func;

    if( typeof functionName === 'undefined' ) { throw 'function name not specified'; }

    if( typeof eval( functionName ) !== 'function' ) { throw functionName + ' is not a function'; }

    if( typeof context !== 'undefined' ) { 
        if( typeof context === 'object' && context instanceof Array === false ) { 
            if( typeof context[ functionName ] !== 'function' ) {
                throw context + '.' + functionName + ' is not a function';
            }
            args = Array.prototype.slice.call( arguments, 2 );

        } else {
            args = Array.prototype.slice.call( arguments, 1 );
            context = window;
        }

    } else {
        context = window;
    }

    namespaces = functionName.split( "." );
    func = namespaces.pop();

    for( var i = 0; i < namespaces.length; i++ ) {
        context = context[ namespaces[ i ] ];
    }

    return context[ func ].apply( context, args );
}

ti consentirà di chiamare una funzione javascript in base al nome memorizzato in una stringa, spaziale o globale, con o senza argomenti (compresi gli oggetti Array), fornendo feedback su eventuali errori riscontrati (speriamo di catturarli).

L'output di esempio mostra come funziona:

// calling a global function without parms
executeFunctionByName( 'a' );
  /* OUTPUT:
  global func passed:
  */

// calling a global function passing a number (with implicit window context)
executeFunctionByName( 'a', 123 );
  /* OUTPUT:
  global func passed:
  -> 123
  */

// calling a namespaced function without parms
executeFunctionByName( 'ns.a' );
  /* OUTPUT:
  namespace func passed:
  */

// calling a namespaced function passing a string literal
executeFunctionByName( 'ns.a', 'No Such Agency!' );
  /* OUTPUT:
  namespace func passed:
  -> No Such Agency!
  */

// calling a namespaced function, with explicit context as separate arg, passing a string literal and array 
executeFunctionByName( 'a', ns, 'No Such Agency!', [ 007, 'is the man' ] );
  /* OUTPUT:
  namespace func passed:
  -> No Such Agency!
  -> 7,is the man
  */

// calling a global function passing a string variable (with implicit window context)
executeFunctionByName( 'a', name );
  /* OUTPUT:
  global func passed:
  -> nsa
  */

// calling a non-existing function via string literal
executeFunctionByName( 'n_s_a' );
  /* OUTPUT:
  Uncaught n_s_a is not a function
  */

// calling a non-existing function by string variable
executeFunctionByName( n_s_a );
  /* OUTPUT:
  Uncaught Snowden is not a function
  */

// calling an existing function with the wrong namespace reference
executeFunctionByName( 'a', {} );
  /* OUTPUT:
  Uncaught [object Object].a is not a function
  */

// calling no function
executeFunctionByName();
  /* OUTPUT:
  Uncaught function name not specified
  */

// calling by empty string
executeFunctionByName( '' );
  /* OUTPUT:
  Uncaught  is not a function
  */

// calling an existing global function with a namespace reference
executeFunctionByName( 'noSuchAgency', ns );
  /* OUTPUT:
  Uncaught [object Object].noSuchAgency is not a function
  */

Non so ... è un ottimo sforzo, è chiaro. Ma mi sembra "troppo vasto" ...
TechNyquist

2
Eh? SO è una piattaforma di domanda / risposta / insegnamento. Sarò lieto di fornire tutti gli esempi a cui riesco a pensare di trasmettere l'illuminazione. Per me, questo è il punto .
Mac

Se stai valutando la funzione Name, comunque, perché non utilizzarla?
dati

Questo non funziona per me. Ho una funzione namespace abcd dove d è il nome della funzione. la chiamata executeFunctionByName ("abcd", finestra) non riesce sulla linea che controlla if( typeof context[ functionName ] !== 'function' )perché il contesto - finestra - è definito, è un oggetto e un array, ma la finestra ['abcd'] non esiste poiché è stato identificato come un problema nell'accettato risposta: window["My.Namespace.functionName"](arguments); // fail
akousmata,

12

A seconda di dove ti trovi puoi anche usare:

this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();

o, in nodejs

global["funcname"]()

9

Se si desidera chiamare una funzione di un oggetto anziché una funzione globale con window["functionName"]. Puoi farlo come;

var myObject=new Object();
myObject["functionName"](arguments);

Esempio:

var now=new Date();
now["getFullYear"]()

8

STAI ATTENTO!!!

Si dovrebbe cercare di evitare di chiamare una funzione tramite stringa in JavaScript per due motivi:

Motivo 1: alcuni offuscatori di codice annulleranno il codice poiché cambieranno i nomi delle funzioni, rendendo la stringa non valida.

Motivo 2: è molto più difficile mantenere il codice che utilizza questa metodologia in quanto è molto più difficile individuare gli usi dei metodi chiamati da una stringa.


7

Ecco il mio approccio Es6 che ti consente di chiamare la tua funzione con il suo nome come stringa o il suo nome di funzione e ti consente anche di passare diversi numeri di argomenti a diversi tipi di funzioni:

function fnCall(fn, ...args)
{
  let func = (typeof fn =="string")?window[fn]:fn;
  if (typeof func == "function") func(...args);
  else throw new Error(`${fn} is Not a function!`);
}


function example1(arg1){console.log(arg1)}
function example2(arg1, arg2){console.log(arg1 + "  and   " + arg2)}
function example3(){console.log("No arguments!")}

fnCall("example1", "test_1");
fnCall("example2", "test_2", "test3");
fnCall(example3);
fnCall("example4"); // should raise an error in console


6

Sorpreso di non menzionare setTimeout.

Per eseguire una funzione senza argomenti:

var functionWithoutArguments = function(){
    console.log("Executing functionWithoutArguments");
}
setTimeout("functionWithoutArguments()", 0);

Per eseguire la funzione con argomenti:

var functionWithArguments = function(arg1, arg2) {
    console.log("Executing functionWithArguments", arg1, arg2);
}
setTimeout("functionWithArguments(10, 20)");

Per eseguire la funzione profondamente spaziata:

var _very = {
    _deeply: {
        _defined: {
            _function: function(num1, num2) {
                console.log("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}
setTimeout("_very._deeply._defined._function(40,50)", 0);

Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti a un autore, lascia un commento sotto il suo post: puoi sempre commentare i tuoi post e una volta che avrai una reputazione sufficiente sarai in grado di commentare qualsiasi post .
AstroCB,

Aggiungi un esempio di come chiameresti runMecon alcuni argomenti.
lessico

1
@lexicore Ho votato per la cancellazione in una coda di recensioni, perché non fornisce chiaramente una risposta sostanziale alla domanda ed è di per sé di scarso valore.
AstroCB,

1
Questo metodo ha potenzialmente un grosso difetto, poiché mette fine alla coda di rendering , rendendo così asincrona
PeterM,

1
Mi piace questa risposta, sembra funzionare per le mie esigenze.
Quintonn,

3

Quindi, come altri hanno detto, sicuramente l'opzione migliore è:

window['myfunction'](arguments)

E come ha detto Jason Bunting , non funzionerà se il nome della tua funzione include un oggetto:

window['myobject.myfunction'](arguments); // won't work
window['myobject']['myfunction'](arguments); // will work

Quindi, ecco la mia versione di una funzione che eseguirà tutte le funzioni per nome (incluso un oggetto o meno):

my = {
    code : {
        is : {
            nice : function(a, b){ alert(a + "," + b); }
        }
    }
};

guy = function(){ alert('awesome'); }

function executeFunctionByName(str, args)
{
    var arr = str.split('.');
    var fn = window[ arr[0] ];
    
    for (var i = 1; i < arr.length; i++)
    { fn = fn[ arr[i] ]; }
    fn.apply(window, args);
}

executeFunctionByName('my.code.is.nice', ['arg1', 'arg2']);
executeFunctionByName('guy');


3
  let t0 = () => { alert('red0') }
  var t1 = () =>{ alert('red1') }
  var t2 = () =>{ alert('red2') }
  var t3 = () =>{ alert('red3') }
  var t4 = () =>{ alert('red4') }
  var t5 = () =>{ alert('red5') }
  var t6 = () =>{ alert('red6') }

  function getSelection(type) {
    var evalSelection = {
      'title0': t0,
      'title1': t1,
      'title2': t2,
      'title3': t3,
      'title4': t4,
      'title5': t5,
      'title6': t6,
      'default': function() {
        return 'Default';
      }
    };
    return (evalSelection[type] || evalSelection['default'])();
  }
  getSelection('title1');

Una soluzione più OOP ...


2

Un altro dettaglio sui post di Jason e Alex. Ho trovato utile aggiungere un valore predefinito al contesto. Metti solo context = context == undefined? window:context;all'inizio della funzione. Puoi passare windowa qualunque sia il tuo contesto preferito, quindi non dovrai passare nella stessa variabile ogni volta che lo chiami nel tuo contesto predefinito.


2

Per aggiungere alla risposta di Jason Bunting, se stai usando nodejs o qualcosa del genere (e funziona anche in dom js), puoi usare thisinvece di window(e ricordare: eval is evil :

this['fun'+'ctionName']();

2

C'è una cosa molto simile nel mio codice. Ho una stringa generata dal server che contiene un nome di funzione che devo passare come callback per una libreria di terze parti. Quindi ho un codice che accetta la stringa e restituisce un "puntatore" alla funzione, oppure null se non viene trovato.

La mia soluzione era molto simile alla " utilissima funzione di Jason Bunting " * , sebbene non si eseguisse automaticamente e il contesto fosse sempre sulla finestra. Ma questo può essere facilmente modificato.

Spero che questo possa essere utile a qualcuno.

/**
 * Converts a string containing a function or object method name to a function pointer.
 * @param  string   func
 * @return function
 */
function getFuncFromString(func) {
    // if already a function, return
    if (typeof func === 'function') return func;

    // if string, try to find function or method of object (of "obj.func" format)
    if (typeof func === 'string') {
        if (!func.length) return null;
        var target = window;
        var func = func.split('.');
        while (func.length) {
            var ns = func.shift();
            if (typeof target[ns] === 'undefined') return null;
            target = target[ns];
        }
        if (typeof target === 'function') return target;
    }

    // return null if could not parse
    return null;
}


1

Non posso fare a meno di menzionare un altro trucco, che aiuta se hai un numero sconosciuto di argomenti che vengono passati anche come parte della stringa contenente il nome della funzione. Per esempio:

var annoyingstring = 'call_my_func(123, true, "blah")';

Se il tuo Javascript è in esecuzione su una pagina HTML, tutto ciò che serve è un link invisibile; è possibile passare una stringa onclicknell'attributo e chiamare il clickmetodo

<a href="#" id="link_secret"><!-- invisible --></a>

$('#link_secret').attr('onclick', annoyingstring);
$('#link_secret').click();

Oppure crea l' <a>elemento in fase di esecuzione.


Soluzione creativa, ma non funzionerà con argomenti di tipo oggetto o matrice.
Dennis Heiden,

1
Questo sta usando eval sotto il cofano ... E davvero battendo intorno al cespuglio per farlo
Juan Mendes,

1

Il modo più semplice è accedervi come ha element

window.ClientSideValidations.forms.location_form

è lo stesso di

window.ClientSideValidations.forms['location_form']

1

Puoi chiamare la funzione javascript all'interno di eval("functionname as string")entrambi. Come sotto: (eval è pura funzione javascript)

function testfunc(){
    return "hello world";
}

$( document ).ready(function() {

     $("div").html(eval("testfunc"));
});

Esempio di lavoro: https://jsfiddle.net/suatatan/24ms0fna/4/


Funziona benissimo ed è così semplice
Carlos E

1
E anche molto lento.
Marco

1

Questo funziona per me:

var command = "Add";
var tempFunction = new Function("Arg1","Arg2", "window." + command + "(Arg1,Arg2)");
tempFunction(x,y);

Spero che funzioni.


1

Non penso che tu abbia bisogno di complicate funzioni intermedie o eval o di dipendere da variabili globali come window:

function fun1(arg) {
  console.log(arg);
}

function fun2(arg) {
  console.log(arg);
}

const operations = {
  fun1,
  fun2
};

let temp = "fun1";

try {
  // You have to use square brackets property access
  operations["fun1"]("Hello World");
  operations["fun2"]("Hello World");
  // You can use variables
  operations[temp]("Hello World");
} catch (error) {
  console.error(error);
}

Funzionerà anche con le funzioni importate:

// mode.js
export function fun1(arg) {
  console.log(arg);
}

export function fun2(arg) {
  console.log(arg);
}
// index.js
import { fun1, fun2 } from "./mod";

const operations = {
  fun1,
  fun2
};

try {
  operations["fun1"]("Hello World");
  operations["fun2"]("Hello World");
} catch (error) {
  console.error(error);
}

0

Senza usare eval('function()')potresti creare una nuova funzione usando new Function(strName). Il codice seguente è stato testato utilizzando FF, Chrome, IE.

<html>
<body>
<button onclick="test()">Try it</button>
</body>
</html>
<script type="text/javascript">

  function test() {
    try {    
        var fnName = "myFunction()";
        var fn = new Function(fnName);
        fn();
      } catch (err) {
        console.log("error:"+err.message);
      }
  }

  function myFunction() {
    console.log('Executing myFunction()');
  }

</script>

0
use this

function executeFunctionByName(functionName, context /*, args */) {
      var args = [].slice.call(arguments).splice(2);
      var namespaces = functionName.split(".");
      var func = namespaces.pop();
      for(var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
      }
      return context[func].apply(context, args);
    }

1
Perché? Le risposte senza spiegazione sono probabilmente inutili.
Daniel W.

0

Sembra di base:

var namefunction = 'jspure'; // String

function jspure(msg1 = '', msg2 = '') { 
  console.log(msg1+(msg2!=''?'/'+msg2:''));
} // multiple argument

// Results ur test
window[namefunction]('hello','hello again'); // something...
eval[namefunction] = 'hello'; // use string or something, but its eval just one argument and not exist multiple

Esistono altri tipi di funzioni: class e look example nils petersohn


0

Grazie per la risposta molto utile. Sto usando la funzione di Jason Bunting nei miei progetti.

L'ho esteso per usarlo con un timeout opzionale, perché il normale modo di impostare un timeout non funzionerà. Vedi la domanda di abhishekisnot

function executeFunctionByName(functionName, context, timeout /*, args */ ) {
	var args = Array.prototype.slice.call(arguments, 3);
	var namespaces = functionName.split(".");
	var func = namespaces.pop();
	for (var i = 0; i < namespaces.length; i++) {
		context = context[namespaces[i]];
	}
	var timeoutID = setTimeout(
		function(){ context[func].apply(context, args)},
		timeout
	);
    return timeoutID;
}

var _very = {
    _deeply: {
        _defined: {
            _function: function(num1, num2) {
                console.log("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}

console.log('now wait')
executeFunctionByName("_very._deeply._defined._function", window, 2000, 40, 50 );

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.