Perché la proprietà argument.callee.caller è stata deprecata in JavaScript?

StackOverflow https://stackoverflow.com/questions/103598

  •  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?)

È stato utile?

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 di Function.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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top