2011-01-23 4 views
28

Ecco lo script:Come spostare "argomenti"?

function runScripts() { 
    if (arguments.length === 0) return; 
    chrome.tabs.executeScript(null, { 
     file: arguments[0] 
    }, function() { 
     arguments.shift(); 
     runScripts.apply(null, arguments); 
    }); 
} 

Non funziona perché arguments non è in realtà un array, è semplicemente array-like. Quindi, come posso "spostarlo" o hackerare il primo elemento in modo da poter applicare questa funzione in modo ricorsivo?

risposta

19

Presumo che si desidera fare riferimento alla originalearguments, invece che dalla richiamata si sta passando a chrome.tabs.executeScript.

In tal caso, è necessario prima memorizzarlo nella cache.

function runScripts() { 
    if (arguments.length === 0) return; 
    var args = []; 
    Array.prototype.push.apply(args, arguments); 

    chrome.tabs.executeScript(null, { 
     file: args.shift(); 
    }, function() { 
      // using the modified Array based on the original arguments object 
     runScripts.apply(null, args); 
    }); 
} 
+0

Eh ...? Perché lo assumeresti? Ogni iterazione gestisce solo il primo elemento, quindi chiama se stesso con il resto finché non ne rimane nessuno. Non sono sicuro di cosa dovrebbe fare la tua versione. – mpen

+0

Oh..nevermind. A causa della funzione anonima ... hai ragione, a parte il fatto che non dovrebbe ancora passare nel primo elemento ... dovrebbe passare tutto tranne il primo. – mpen

+0

@Mark: hai 2 funzioni. Forse ho sbagliato, ma ho interpretato la tua domanda come se volessi passare 'arguments' da' runScripts() '* a * il callback che stai passando a' executeScript'. Non è un'ipotesi strana da fare dato che stai facendo riferimento a 'arguments [0]' * outside * al callback, quindi provando a '.shift()' * dentro * il callback. – user113716

31
var params = Array.prototype.slice.call(arguments); 
params.shift(); 

È possibile controllare questo blog post che lo spiega in ulteriori dettagli.

+0

Mi sono imbattuto anche in quel post del blog, ma se eseguo 'alert (args.length)' mi dice 0, mentre 'arguments.length' mi dice 2. Qualcosa non va. ** Modifica: ** Crap ... Capisco. La funzione anonima sta rovinando tutto. – mpen

+1

Ecco una piccola ottimizzazione che ti proteggerà da una 'Array' modificata:' [] .slice.call (argomenti) '. – fny

+1

Perché non usare semplicemente '[] .shift.call (argomenti)'? ;) – thewildpendulum

2

Avrete bisogno di convertirlo in un array e quindi spostare. Oppure, in alternativa, rilascia il primo oggetto durante la conversione in un array. Array.prototype.slice.call(arguments, 1) funzionerebbe per questo.

6

È possibile trasformare arguments in una serie regolare come questo:

var args = Array.prototype.slice.call(arguments); 
1

è possibile convertire gli argomenti per una matrice reale e quindi utilizzare tale matrice nel resto della logica nella funzione.

function runScripts() 
{ 
    var i=0, l=arguments.length, arr=[]; 
    while(i<l) 
    { 
    arr.push(arguments[i++]); 
    } 
...rest of your function code 

Modifica per aggiungere: ho avuto problemi con prototype e call nelle vecchie versioni di IE, quindi è veramente dipende da che tipo di sostegno di cui ha bisogno.

+0

Fortunatamente non ho bisogno di supportare IE :) Solo Chrome. – mpen

9

[].shift.call(arguments) è valido anche. Sto usando questo nel codice di produzione e funziona come previsto.

Con questo approccio, la funzione diventa un po 'più succinta:

function executeScripts() { 
    if (arguments.length === 0) return; 
    chrome.tabs.executeScript(null, { 
     file: [].shift.call(arguments) 
    }, function() { 
     executeScripts.apply(null, arguments); 
    }); 
} 

Se si guarda il MDN, essi affermano che shift() è stato attuato con questa flessibilità in mente.

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift

+0

Peccato che non abbiano semplicemente implementato' arguments' come un vero array per cominciare. Ma hai ragione. In effetti, tutti i metodi da 'Array' possono essere usati su * qualsiasi * oggetto, purché quell'oggetto abbia una proprietà' length'. Questo è richiesto dalle specifiche del linguaggio. Il segreto di questa magia è che sotto la superficie, gli array sono solo oggetti con numeri come chiavi di proprietà. –

3

Volevo solo far notare un potenziale problema con [] .shift.call (argomenti).

Questo sembra avere l'intento forse poco chiaro di spostare i tuoi argomenti - anche per i parametri della tua funzione denominati - anche se utilizzati prima dell'istruzione shift.

Per esempio,

function testShift (param1, param2) { 
    [].shift.call(arguments); 
    if (param1=="ONE") alert("ONE"); 
} 

se si effettua la seguente chiamata, quello che potrebbe aspettare che accada?

testShift("ONE", "TWO"); 

Se vi aspettavate param1 di soggiorno "ONE", il fix è quello di impostare un var per Param1 prima che si verifichi il cambiamento. Sembra che javascript non sia impegnativo param1 fino alla riga su cui è chiamato - non quando la funzione viene chiamata ...quindi le modifiche agli argomenti precedenti a un parametro in uso possono avere effetti imprevisti.

Speriamo che ora te lo aspetti.

+0

TIL. Questo è un comportamento abbastanza inaspettato. Grazie per aver condiviso. – mpen

0

sono andato con questo:

function executeScripts() { 
    if (arguments.length === 0) return; 
    var args = Array.prototype.slice.call(arguments); 
    chrome.tabs.executeScript(null, { 
     file: args.shift() 
    }, function() { 
     executeScripts.apply(null, args); 
    }); 
} 

E 'utile quando si scrivono estensioni di Google Chrome. Volevo usare jQuery nel mio script di contenuto, ma poi devi caricarlo prima. Risulta da concatenare chiamate a chrome.tabs.executeScript si può fare questo:

chrome.browserAction.onClicked.addListener(function(tab) { 
    executeScripts('jquery-1.4.4.min.js', 'content.js'); 
}); 
1

Ecco un articolo spiega molto bene. Ho copiato alcuni punti chiave di seguito. http://www.javascriptkit.com/javatutors/arrayprototypeslice.shtml

Per array, ricordare che è possibile chiamare la funzione slice per ottenere un sub array.

var abc = [1,2,3,4,5]; 
abc.slice(0); //[1,2,3,4,5] 
abc.slice(1,3); //[2,3] 

Poiché l'argomento oggetto è solo array come, non proprio un array. La funzione call()/apply() fondamentalmente solo "prende in prestito" la funzione slice da Array e la usa sull'oggetto Argument, e puoi anche passare i parametri nella funzione slice proprio come agire sull'array.

var myobject ={ // array-like collection 
    length: 4, 
    '0': 'zero', 
    '1': 'one', 
    '2': 'two', 
    '3': 'three' 
} 

var myarray = Array.prototype.slice.call(myobject) 
// returns myobject as a true array: ["zero", "one", "two", "three"] 

var myarray = Array.prototype.slice.call(myobject, 1) 
// returns ["one", "two", "three"] 

L'unica domanda che rimane è per questo che stiamo chiamando slice() sull'oggetto prototipo di Array invece di un'istanza array. Il motivo è perché questa è la via più diretta per accedere al metodo slice() di Array quando è tutto ciò di cui siamo interessati; avremmo potuto prima creato un'istanza Array, ma questo è meno efficiente e probabilmente più astruse:

var myarray = new Array().prototype.slice.call(myobject) // less efficient 
2

In ES6 è ora possibile utilizzare Array.from() MDN ref

esempio

const args = Array.from(arguments); 
const str = args.shift(); 
+1

In ES6 puoi usare anche variadic args: 'function f (... args) {args.shift(); } ' – mpen