Почему свойство arguments.callee.caller устарело в JavaScript?

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

  •  01-07-2019
  •  | 
  •  

Вопрос

Почему был arguments.callee.caller свойство устарело в JavaScript?

Он был добавлен, а затем устарел в JavaScript, но в ECMAScript он был полностью опущен.Некоторые браузеры (Mozilla, IE) всегда поддерживали это, и у них нет никаких планов по удалению поддержки.Другие (Safari, Opera) поддерживают его, но поддержка в старых браузерах ненадежна.

Есть ли веская причина поместить эту ценную функциональность в подвешенное состояние?

(Или, с другой стороны, есть ли лучший способ захватить дескриптор вызывающей функции?)

Это было полезно?

Решение

Ранние версии JavaScript не допускали выражений именованных функций, и из-за этого мы не могли создать выражение рекурсивной функции:

 // 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;
 });

Чтобы обойти это, arguments.callee был добавлен для того, чтобы мы могли сделать:

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : arguments.callee(n-1)*n;
 });

Однако на самом деле это было действительно плохое решение, поскольку это (в сочетании с другими аргументами, вызываемым объектом и проблемами вызывающего абонента) делает встраивание и хвостовую рекурсию невозможными в общем случае (вы можете достичь этого в избранных случаях с помощью трассировки и т.д., Но даже лучший код неоптимален из-за проверок, которые в противном случае не были бы необходимы).Другая серьезная проблема заключается в том, что рекурсивный вызов получит другой this значение, например:

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();

Во всяком случае, ECMAScript 3 решил эти проблемы, разрешив выражения именованных функций, например:

 [1,2,3,4,5].map(function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 });

Это имеет множество преимуществ:

  • Функция может быть вызвана, как и любая другая, изнутри вашего кода.

  • Это не загрязняет пространство имен.

  • Ценность this не меняется.

  • Это более производительно (доступ к аргументы объекта стоит дорого).

Упс,

Просто понял, что в дополнение ко всему остальному вопрос заключался в следующем arguments.callee.caller, или более конкретно Function.caller.

В любой момент времени вы можете найти самый глубокий вызывающий объект любой функции в стеке, и, как я уже говорил выше, просмотр стека вызовов имеет один важный эффект:Это делает большое количество оптимизаций невозможными или намного, намного более сложными.

Например.если мы не можем гарантировать, что функция f не будет вызывать неизвестную функцию, тогда невозможно встроить f.По сути, это означает, что любой сайт вызова, который, возможно, был тривиально встроенным, накапливает большое количество охранников, принимая:

 function f(a, b, c, d, e) { return a ? b * c : d * e; }

Если интерпретатор js не может гарантировать, что все предоставленные аргументы являются числами в момент выполнения вызова, ему необходимо либо вставить проверки для всех аргументов перед встроенным кодом, либо он не сможет встроить функцию.

Теперь, в этом конкретном случае, умный интерпретатор должен быть в состоянии изменить порядок проверок, чтобы они были более оптимальными, и не проверять какие-либо значения, которые не были бы использованы.Однако во многих случаях это просто невозможно, и поэтому становится невозможным встроить.

Другие советы

arguments.callee.caller является нет устарел, хотя и использует Function.caller собственность.(arguments.callee просто дам вам ссылку на текущую функцию)

  • Function.caller, хотя и нестандартный в соответствии с ECMA3, реализован во всех все текущие основные браузеры.
  • arguments.caller является устарел в пользу Function.caller, и не реализован в некоторых современных основных браузерах (напримерFirefox 3).

Таким образом, ситуация далека от идеальной, но если вы хотите получить доступ к вызывающей функции в Javascript во всех основных браузерах, вы можете использовать Function.caller свойство, доступ к которому осуществляется либо непосредственно по ссылке на именованную функцию, либо изнутри анонимной функции через arguments.callee собственность.

Лучше использовать именованные функции чем аргументы.вызываемый абонент:

 function foo () {
     ... foo() ...
 }

это лучше, чем

 function () {
     ... arguments.callee() ...
 }

Именованная функция будет иметь доступ к своему вызывающему объекту через вызывающий абонент собственность:

 function foo () {
     alert(foo.caller);
 }

что лучше, чем

 function foo () {
     alert(arguments.callee.caller);
 }

Устаревание связано с текущим ECMAScript принципы проектирования.

Просто расширение.Значение "this" изменяется во время рекурсии.В следующем (измененном) примере factorial получает объект {foo:true}.

[1,2,3,4,5].map(function factorial(n) {
  console.log(this);
  return (!(n>1))? 1 : factorial(n-1)*n;
},     {foo:true}     );

факториал, вызываемый в первый раз, получает объект, но это неверно для рекурсивных вызовов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top