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

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

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

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

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

Ответ 1

В ранних версиях 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.

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

Лучше использовать названные функции, чем arguments.callee:

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

лучше, чем

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

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

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

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

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

Утечка обусловлена ​​текущими принципами дизайна ECaAScript .

Ответ 4

по-прежнему существует аргумент для обращения к функции без необходимости жесткого кода его имени.

Ответ 5

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

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

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