In realtà è abbastanza semplice e un compito divertente scrivere tali script.
Ecco un lungo esempio come trasformare una normale funzione in qualcosa di simile a questo:
mi piacerebbe iniziare con uno script immaginario. Ho incluso uno scriptLoader che carica un file javascript in modo asincrono:
window.loadScript = function(src){
const scriptTag = document.createElement('script');
scriptTag.async = true;
scriptTag.src = src;
const anyOtherScriptTag = document.getElementsByTagName('script')[0];
anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag);
}
Quando chiamato in questo modo: loadScript("/url.js")
che verrà inserire un nuovo tag script (prima del primo tag script) nel DOM e il browser scaricherà lo script .
Fin qui tutto bene. Diciamo che voglio passare questo argomento di script prima che sia caricato. All'interno dello script che verrà caricato accedo a un oggetto globale univoco. Chiamiamolo window.myScriptArgs
. Quindi idealmente, una volta caricato lo script, legge window.myScriptArgs ed esegue di conseguenza.
Ora ho potuto fare window.myScriptArgs = []
e chiamare un giorno, ma dato che il mio esempio ipotetico servirà solo a caricare un file di script singolo, aggiungo la logica alla funzione CaricaScript pure.
window.loadScript = function(src){
window.myScriptArgs = window.myScriptArgs || [];
const scriptTag = document.createElement('script');
scriptTag.async = true;
scriptTag.src = src;
const anyOtherScriptTag = document.getElementsByTagName('script')[0];
anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag);
}
loadScript("/my-script.js");
Ok, controllo se myScriptArgs è già presente e se non impostato a un array vuoto. Ora so anche che my-script.js
espone un metodo globale myScript(). Quindi scrivo uno stub per questo. Questo stub metterà ogni argomento ha ricevuto ad esso nella matrice myScriptArgs:
window.myScript =() => {
window.myScriptArgs = window.myScriptArgs || [];
window.myScriptArgs.push(arguments);
}
ora posso chiamare CaricaScript e chiamare immediatamente myScript() con determinati argomenti. Non è necessario preoccuparsi di problemi di caricamento o quant'altro. Una volta caricato "my-script.js", viene letto window.myScriptArgs
e funziona come escluso. Il codice è simile al seguente:
window.myScript =() => {
window.myScriptArgs = window.myScriptArgs || [];
window.myScriptArgs.push(arguments);
}
window.loadScript = function(src){
window.myScriptArgs = window.myScriptArgs || [];
const scriptTag = document.createElement('script');
scriptTag.async = true;
scriptTag.src = src;
const anyOtherScriptTag = document.getElementsByTagName('script')[0];
anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag);
}
loadScript("/my-script.js");
myScript('command', 'args', 'args1');
myScript('command2', 'args3', 'args4');
Ok, funziona come previsto.Cerchiamo di ottimizzarlo Per prima cosa ho combinare lo stub loadScript
e myScript
ad una singola funzione chiamata initMyScript():
window.initMyScript = function(src){
window.myScriptArgs = window.myScriptArgs || [];
window.myScript = window.myScript || function(){
window.myScriptArgs.push(arguments);
}
const scriptTag = document.createElement('script');
scriptTag.async = true;
scriptTag.src = src;
const anyOtherScriptTag = document.getElementsByTagName('script')[0];
anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag);
}
initMyScript("/my-script.js");
myScript('command', 'args', 'args1');
myScript('command2', 'args3', 'args4');
Non è niente di troppo atm fantasia. Ora ho intenzione di sbarazzarmi delle chiamate multiple window.
passando window
come argomento a initMyScript
. Lo farò anche con document
.
Lo script si presenta così:
window.initMyScript = function(p, a, src){
p.myScriptArgs = p.myScriptArgs || [];
p.myScript = p.myScript || function(){
p.myScriptArgs.push(arguments);
}
const scriptTag = a.createElement('script');
scriptTag.async = true;
scriptTag.src = src;
const anyOtherScriptTag = a.getElementsByTagName('script')[0];
anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag);
}
initMyScript(window, document, "/my-script.js");
Ora vediamo dove mi ripeto a me stesso di risparmiare un po 'più bit. Io uso la stringa script
due volte, lo stesso per myScript
:
window.initMyScript = function(p, a, s, c, src){
p.myScriptArgs = p.myScriptArgs || [];
p[c] = p[c] || function(){
p.myScriptArgs.push(arguments);
}
const scriptTag = a.createElement(s);
scriptTag.async = true;
scriptTag.src = src;
const anyOtherScriptTag = a.getElementsByTagName(s)[0];
anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag);
}
initMyScript(window, document, 'script', 'myScript', "/my-script.js");
Il prossimo passo nel mio viaggio è quello di rendere le variabili breve. E ho anche disattivare questa funzione in una funzione self-executing per salvare la definizione window.initMyScript
:
(function(p, a, s, c, src){
p.myScriptArgs = p.myScriptArgs || [];
p[c] = p[c] || function(){
p.myScriptArgs.push(arguments);
}
const q = a.createElement(s);
q.async = true;
q.src = src;
const d = a.getElementsByTagName(s)[0];
d.parentNode.insertBefore(q, d);
})(window, document, 'script', 'myScript', "/my-script.js");
e al mio ultimo mistero: modifico i parametri della funzione per confondere la gente e anche minify il codice ancora di più. Puoi effettivamente concatenare le funzioni in javascript usando le virgole;).
(function(p, a, s, c, A, l, i){
p["myScriptArgs"]=p["myScriptArgs"]||[],p[c] = p[c]||function(){
p["myScriptArgs"].push(arguments)},
l = a.createElement(s);l.async = true;l[A] = A;
i = a.getElementsByTagName(s)[0];
i.parentNode.insertBefore(l, i);
})(window, document, 'script', 'myScript', "/my-script.js");
myScript("arg1", "arg2");
myScript("arg2", "arg3");
notano che aggiungo due parametri supplementari in funzione, è perché ho bisogno di salvare l'elemento restituito da createElement
e non voglio utilizzare un'istruzione var
;).
Puoi fare ancora di più, ma ottieni il punto. Per piccole funzioni, puoi farlo da solo senza problemi.
Inoltre, è possibile utilizzare un minifier come UglifyJS e quindi rinominare le variabili da soli dopo se siete veramente in che cosa intera isogram ...
Nota: Non ho prove di tutto questo codice. Ecco i draghi. Il codice immaginario è il mio cattivo tentativo di de-offuscare l'esempio di Google. Lo snippet di google-analytics funziona quasi come il mio snippet personalizzato. GA ottimizza un po 'di più (ad esempio trasformando true in 1) ma ottieni il punto.
Per saperne di più le cose utilizzate nel mio esempio: Immediately Invoked Function Expression Property accessors (especially Bracket notation)
e JavaScript cose specifiche come passare tre argomenti a una funzione che prende 5.
Non so, ma è uno script così minuscolo, potrebbe facilmente essere fatto a mano usando i nomi dei parametri di funzione desiderati. –
... ciò che è interessante è che lo script 'ga' usa la notazione della parentesi con una stringa letterale:' i ['GoogleAnalyticsObject'] ', che i minificatori (incluso Closure Compiler) di solito convertono in sintassi a punti, quindi questo mi fa pensare probabilmente è fatto a mano, con le parentesi usate solo nel caso in cui funzioni attraverso CC, quindi non lo trasforma in 'window.b =" ga "'. –
@squint Grazie! Sì, sembra che sia stato fatto a mano, mi stavo chiedendo se non mi manca qualcosa – lmenus