Ответ 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 не может гарантировать, что все предоставленные аргументы являются числами в точке, в которой выполняется вызов, ей необходимо либо вставить проверки всех аргументов перед встроенным кодом, либо не может встроить функцию.
Теперь в этом конкретном случае интеллектуальный интерпретатор должен иметь возможность переставить проверки более оптимальными и не проверять любые значения, которые не будут использоваться. Однако во многих случаях это просто невозможно, и поэтому становится невозможным встроить.