Почему свойство arguments.callee.caller было объявлено устаревшим в JavaScript?
Он был добавлен, а затем объявлен устаревшим в JavaScript, но полностью исключен в ECMAScript. Некоторые браузеры (Mozilla, IE) всегда поддерживали его и не планируют отменять поддержку. Другие (Safari, Opera) приняли его поддержку, но поддержка в старых браузерах ненадежна.
Есть ли веская причина оставить эту ценную функцию в подвешенном состоянии?
(Или, альтернативно, есть лучший способ захватить дескриптор вызывающей функции?)
(Почти все браузеры когда-то делали это, например, эта функция (и сам JS) исходит от Netscape, XHR возник в IE, Canvas в Safari и т. д. Некоторые из них полезны и используются другими браузерами. со временем (js, canvas, xhr - все примеры), некоторые (.callee) - нет.
@olliej Ваш комментарий о его поддержке, потому что он используется, а не потому, что он является стандартом (или даже несмотря на то, что он устарел в стандарте), очень верен! Вот почему я начал игнорировать стандарты всякий раз, когда мне казалось, что они мне не помогают. Мы, как разработчики, можем формировать направление стандартов, используя то, что работает, а не то, что в спецификации сказано, что мы должны делать. Так мы вернули <b> и <i> (да, в какой-то момент они устарели).



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Лучше использовать именованные функции, чем arguments.callee:
function foo () {
... foo() ...
}
лучше, чем
function () {
... arguments.callee() ...
}
Именованная функция будет иметь доступ к вызывающей стороне через свойство звонящий:
function foo () {
alert(foo.caller);
}
что лучше чем
function foo () {
alert(arguments.callee.caller);
}
Устарение связано с текущим ECMAScript принципы дизайна.
Можете ли вы описать, почему использование названной функции лучше? Разве никогда не нужно использовать вызываемый объект в анонимной функции?
Если вы используете вызываемый объект в анонимной функции, то у вас есть функция, которая не должна быть анонимной.
иногда самый простой способ отладки - использовать .caller (). В таких случаях именованные функции не помогут - вы пытаетесь выяснить, какая функция выполняет вызов.
Определите лучше. Например, IE6-8 назвали причуды функций, а arguments.callee работает.
Помимо причуд IE6-8, он также делает код тесно связанным. Если имена объектов и / или функций жестко запрограммированы, то, как упоминают ardsasd и rsk82, существуют серьезные опасности рефакторинга, которые только увеличиваются по мере увеличения базового размера кода. Модульные тесты - это линия защиты, и я использую их, но они все еще не тот ответ, который действительно насытит меня лично в отношении этой проблемы жесткого кодирования.
Ранние версии 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 не может гарантировать, что все предоставленные аргументы являются числами в момент выполнения вызова, ему необходимо либо вставить проверки для всех аргументов перед встроенным кодом, либо он не может встроить функцию.
Теперь в этом конкретном случае умный интерпретатор должен иметь возможность изменить порядок проверок, чтобы они были более оптимальными, и не проверять какие-либо значения, которые не будут использоваться. Однако во многих случаях это просто невозможно, и поэтому становится невозможным встраивание.
Вы говорите, что он устарел только потому, что его сложно оптимизировать? Это довольно глупо.
Нет, я перечислил ряд причин, помимо того, что это затрудняет оптимизацию (хотя в целом история показала, что вещи, которые трудно оптимизировать, также имеют семантику, которой люди не могут следовать)
Аргумент это немного ложный, его значение может быть установлено вызовом, если оно важно. Обычно он не используется (по крайней мере, у меня никогда не было проблем с ним в рекурсивных функциях). Вызов функции по имени имеет те же проблемы с this, поэтому я думаю, что это не имеет значения в отношении того, является ли вызываемый хорошим или плохим. Кроме того, вызываемый и звонящий "устарели" только в строгом режиме (ECMAscript ed 5, декабрь 2009 г.), но я полагаю, что это не было известно в 2008 г., когда опубликовал olliej.
) До сих пор не вижу логики. На любом языке с функциями первого класса очевидна ценность возможности определить тело функции, которое может ссылаться на себя, без необходимости знать i
arguments.callee.caller дает вам функцию, которая вызвала вас, а не функцию, в которой вы находитесь, что не слишком полезно в реальном коде и приводит к тому, что встраивание, хвостовые вызовы и т. д. становятся несостоятельными.
Обратите внимание, что вам не нужны именованные функции (или arguments.callee) для создания рекурсивных функций, так как вы можете использовать Y комбинатор.
Устаревание из-за другого this тоже глупо. Для этого у нас есть Function.prototype.call и Function.prototype.apply.
РобГ указал на это, но я не думаю, что все было так ясно: рекурсивное использование именованной функции сохранит значение this, только если this является глобальной областью. Во всех остальных случаях значение thisбуду изменяется после первого рекурсивного вызова, поэтому я думаю, что части вашего ответа, ссылающиеся на сохранение this, на самом деле не действительны.
Что касается введения arguments.callee, ради человечества, почему бы не остановиться на анонимных встроенных функциях вместе и просто передать ссылку на функцию таким функциям, как map. Это хорошая идея не потому, что JS позволяет вам писать такой код. Это скрывает структуру кода, и если вы не назовете их, они будут отображаться как «анонимные функции» в отладчиках и профилировщиках. Я бы хотел, чтобы разработчики JS изучили некоторые базовые концепции программирования, чтобы перестать писать нечитаемый, не исправляемый, непрофильный, плохо спроектированный и, следовательно, обычно ошибочный код.
@nus Если вы используете анонимные функции, передавая их в качестве обработчиков событий, это ничего не скрывает. Кроме того, он проясняет и гарантирует, что функция вызывается только при срабатывании события.
@olliej. для чего ! в !(n>1))? 1
@nus. для чего ! в !(n>1))? 1
@iLiveInAPineappleUnderTheSea ! означает НЕ как логический оператор в большинстве языков программирования, проверять операторы javascript. Весь оператор return (!(n>1))? 1 : factorial(n-1)*n; переводится как: Если n не больше 1, return 1 иначе вернет результат факториала ... В любом случае его было бы легче читать, удалив лишние скобки и отрицание: return n<=1 ? 1 : factorial( n-1 ) * n
Собственно, до ES3 был нет функционального выражения. вместо него был конструктор Function. поэтому в спецификации сказано:> Анонимные функции создаются динамически с использованием встроенного объекта Function в качестве конструктора, который называется функцией создания экземпляра> Свойство создается с именем вызываемого и атрибутами свойства {DontEnum}. Начальным значением этого свойства является выполняемый объект функции. Это позволяет анонимным функциям быть рекурсивными.
Эмм ... Почему этот ответ почти дословный, что на этой странице? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
arguments.callee.caller является устаревшим нет, хотя он использует свойство Function.caller. (arguments.callee просто даст вам ссылку на текущую функцию)
Function.caller, хотя и нестандартный согласно ECMA3, реализуется через все текущие основные браузеры.arguments.callerявляется устарел в пользу Function.caller и не реализован в некоторых текущих основных браузерах (например, Firefox 3).Таким образом, ситуация далеко не идеальная, но если вы хотите получить доступ к вызывающей функции в Javascript во всех основных браузерах, вы можете использовать свойство Function.caller, доступ к которому осуществляется либо непосредственно по ссылке на именованную функцию, либо из анонимной функции через свойство arguments.callee. .
Это лучшее объяснение того, что является устаревшим, а что нет, очень полезно. Хороший пример того, что Function.caller не может сделать (получить трассировку стека рекурсивных функций), см. В developer.mozilla.org/en/JavaScript/Reference/Global_Objects /…
Хотя в строгом режиме arguments.callee запрещен. Мне тоже стало грустно, но лучше больше не использовать.
Гиперссылка arguments.callee, которая у вас есть на MDN, говорит, что она удаляется в строгом режиме. Разве это не то же самое, что и устаревшее?
Обратите внимание, что arguments.callee.caller не рекомендуется в строгом режиме ES5: «Еще одна устаревшая функция - arguments.callee.caller или, более конкретно, Function.caller». (Источник)
Просто расширение. Значение this изменяется во время рекурсии. В следующем (измененном) примере факториал получает объект {foo: true}.
[1,2,3,4,5].map(function factorial(n) {
console.info(this);
return (!(n>1))? 1 : factorial(n-1)*n;
}, {foo:true} );
Факториал, вызываемый в первый раз, получает объект, но это неверно для рекурсивных вызовов.
Потому что вы делаете это неправильно. Если this необходимо поддерживать, напишите factorial.call(this, n-1), который я обнаружил при написании рекурсивного кода, что обычно нет this, или this относится к некоторому узлу в дереве, и на самом деле хорошо, что он изменяется.
Он поддерживается другими браузерами, потому что любая функция, которая получит хоть какое-то широкое распространение, станет ошибкой совместимости для другого браузера. Если сайт использует функцию, которая существует только в одном браузере, сайт не работает во всех остальных, и обычно пользователи думают, что это браузер неисправен.