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

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

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

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

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

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

однако на самом деле это было действительно плохое решение, поскольку это (в сочетании с другими аргументами, вызываемыми и вызывающими проблемами) делает рекурсию inlining и tail невозможной в общем случае (вы можете достичь этого в отдельных случаях через трассировка и т. д., Но даже лучший код является неоптимальным из-за проверок, которые в противном случае не были бы необходимы). Другая важная проблема заключается в том, что рекурсивный вызов получит другой 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 is не устарел, хотя он использует Function.caller собственность. (arguments.callee просто дать вам ссылку на текущую функцию)

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

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


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

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

лучше, чем

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

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

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

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