Почему были такие споры.вызываемый.свойство caller устарело в JavaScript?


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

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

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

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

5 204

5 ответов:

ранние версии 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и deprecated в пользу Function.caller, и не реализуется в некоторых текущих основных браузеры (например, Firefox 3).

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

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

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

лучше, чем

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

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

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

лучше, чем

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

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

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

просто расширение. Значение "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}     );

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