Почему были споры.вызываемый.свойство 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} );
факториал, вызванный в первый раз, получает объект, но это неверно для рекурсивных вызовов.