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