Perché la proprietà argument.callee.caller è stata deprecata in JavaScript?
-
01-07-2019 - |
Domanda
Perché la proprietà argument.callee.caller
è stata deprecata in JavaScript?
È stato aggiunto e quindi deprecato in JavaScript, ma è stato omesso del tutto da ECMAScript. Alcuni browser (Mozilla, IE) lo hanno sempre supportato e non hanno piani sulla mappa per rimuovere il supporto. Altri (Safari, Opera) hanno adottato il supporto per esso, ma il supporto su browser meno recenti non è affidabile.
C'è una buona ragione per mettere questa preziosa funzionalità nel limbo?
(O in alternativa, c'è un modo migliore per afferrare un handle sulla funzione di chiamata?)
Soluzione
Le prime versioni di JavaScript non consentivano espressioni di funzioni denominate e per questo motivo non siamo riusciti a creare un'espressione di funzione ricorsiva:
// This snippet will work:
function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
}
[1,2,3,4,5].map(factorial);
// But this snippet will not:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : /* what goes here? */ (n-1)*n;
});
Per aggirare questo, argument.callee
è stato aggiunto in modo da poter fare:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
Tuttavia, questa è stata in realtà una soluzione davvero negativa in quanto (in combinazione con altri argomenti, chiamate e problemi del chiamante) rende impossibile ricorrere all'allineamento e alla ricorsione della coda nel caso generale (è possibile ottenerlo in determinati casi attraverso la traccia ecc., ma anche il codice migliore è sub ottimale a causa di controlli che altrimenti non sarebbero necessari). L'altro problema principale è che la chiamata ricorsiva otterrà un questo
diverso, ad esempio:
var global = this;
var sillyFunction = function (recursed) {
if (!recursed)
return arguments.callee(true);
if (this !== global)
alert("This is: " + this);
else
alert("This is the global");
}
sillyFunction();
In ogni caso, EcmaScript 3 ha risolto questi problemi consentendo espressioni di funzioni denominate, ad esempio:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
Ciò ha numerosi vantaggi:
-
La funzione può essere chiamata come qualsiasi altra dall'interno del tuo codice.
-
Non inquina lo spazio dei nomi.
-
Il valore di
this
non cambia. -
È più performante (accedendo agli argomenti oggetto è costoso).
Ops,
Ho appena realizzato che, oltre a tutto il resto, la domanda riguardava argument.callee.caller
, o più specificamente Function.caller
.
In qualsiasi momento puoi trovare il chiamante più profondo di qualsiasi funzione nello stack e, come ho detto sopra, guardare lo stack di chiamate ha un singolo effetto principale: rende impossibile un gran numero di ottimizzazioni, o molto più difficile.
Eg. se non possiamo garantire che una funzione f
non chiamerà una funzione sconosciuta, non è possibile incorporare f
. Fondamentalmente significa che qualsiasi sito di chiamata che potrebbe essere stato banalmente inlinabile accumula un gran numero di guardie, prendere:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
Se l'interprete js non può garantire che tutti gli argomenti forniti siano numeri nel punto in cui viene effettuata la chiamata, deve inserire i controlli per tutti gli argomenti prima del codice inline oppure non può incorporare la funzione.
Ora, in questo caso particolare, un interprete intelligente dovrebbe essere in grado di riorganizzare i controlli per essere più ottimale e non verificare alcun valore che non verrebbe utilizzato. Tuttavia, in molti casi ciò non è possibile e quindi diventa impossibile inline.
Altri suggerimenti
arguments.callee.caller
è non deprecato, sebbene faccia uso del < a href = "https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/caller" rel = "noreferrer"> Function.call er
proprietà. ( argomenti.call ee
ti fornirà semplicemente un riferimento alla funzione corrente)
Function.caller
, sebbene non standard secondo ECMA3, è implementato su tutti i principali browser attuali .-
argomenti.call er
è deprecato in favore diFunction.caller
e non è implementato in alcuni dei principali browser attuali ( ad esempio Firefox 3).
Quindi la situazione è tutt'altro che ideale, ma se si desidera accedere alla funzione di chiamata in Javascript su tutti i principali browser, è possibile utilizzare la proprietà Function.caller
, a cui si accede direttamente su un riferimento di funzione denominato o da una funzione anonima tramite la proprietà arguments.callee
.
È meglio usare funzioni con nome rispetto a topics.callee:
function foo () {
... foo() ...
}
è meglio di
function () {
... arguments.callee() ...
}
La funzione nominata avrà accesso al suo chiamante tramite proprietà chiamante :
function foo () {
alert(foo.caller);
}
che è meglio di
function foo () {
alert(arguments.callee.caller);
}
La deprecazione è dovuta agli attuali ECMAScript principi di progettazione .
Solo un'estensione. Il valore di " questo " cambiamenti durante la ricorsione. Nel seguente esempio (modificato), factorial ottiene l'oggetto {foo: true}.
[1,2,3,4,5].map(function factorial(n) {
console.log(this);
return (!(n>1))? 1 : factorial(n-1)*n;
}, {foo:true} );
fattoriale chiamato la prima volta ottiene l'oggetto, ma questo non è vero per le chiamate ricorsive.