Вопрос по javascript, ecma262 – Почему свойство arguments.callee.caller устарело в JavaScript?

207

Почему былarguments.callee.caller собственность устарела в JavaScript?

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

Есть ли веская причина поставить этот ценный функционал в подвешенном состоянии?

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

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

Ваш Ответ

5   ответов
14

все еще есть аргумент для ссылки на функцию без необходимости жесткого кодирования ее имени.

Я всегда думал ... почему бы не иметь "thisFunction" ключевое слово с "аргументами" как собственность, а не наоборот, комментарий г-на 2009.
Да, и это очень важно, id может привести к ошибкам при рефакторинге кода.
Что подразумевается под этим ответом? Можете ли вы объяснить еще?
29

Лучше использоватьnamed functions чем arguments.callee:

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

лучше, чем

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

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

 function foo () {
     alert(foo.caller);
 }

что лучше чем

 function foo () {
     alert(arguments.callee.caller);
 }

Устаревание связано с текущим ECMAScriptпринципы дизайна.

Определись лучше. Например, IE6-8have named function quirks в то время как arguments.callee работает.
Иногда самый простой способ отладки - с помощью .caller (). В таких случаях именованные функции не помогут - вы пытаетесь определить, какая функция выполняет вызов.
Если вы используете вызываемый в анонимной функции, то у вас есть функция, которая не должна быть анонимной.
Можете ли вы описать, почему использование названной функции лучше. Нет ли необходимости использовать вызываемого абонента в анонимной функции?
Помимо особенностей IE6-8, он также делает код тесно связанным. Если имена объектов и / или функций жестко запрограммированы, то, как упоминают ardsasd и rsk82, существуют серьезные опасности при рефакторинге, которые увеличиваются только при увеличении размера базы кода. Модульные тесты - это линия защиты, и я использую их, но они все еще не являются ответом, который действительно меня лично удовлетворяет в связи с этой проблемой жесткого кода.
246

Ранние версии 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;
 });

Это имеет множество преимуществ:

The function can be called like any other from inside your code.

It does not pollute the namespace.

The value of this does not change.

It's more performant (accessing the arguments object is expensive).

Whoops,

Просто понял, что помимо всего прочего речь шла оarguments.callee.callerили, более конкретно,Function.caller.

В любой момент времени вы можете найти самого глубокого вызывающего абонента из любой функции в стеке, и, как я уже говорил выше, просмотр стека вызовов имеет один единственный важный эффект: он делает невозможным большое количество оптимизаций или намного более трудным.

Например. если мы не можем гарантировать, что функцияf не будет вызывать неизвестную функцию, тогда невозможно встроитьf, По сути, это означает, что любой сайт вызова, который, возможно, был тривиально встроен, накапливает большое количество охранников, принимает:

 function f(a, b, c, d, e) { return a ? b * c : d * e; }

Если интерпретатор js не может гарантировать, что все предоставленные аргументы являются числами в момент выполнения вызова, ему необходимо либо вставить проверки для всех аргументов перед встроенным кодом, либо он не может встроить функцию.

Теперь в этом конкретном случае умный интерпретатор должен иметь возможность переставить проверки, чтобы они были более оптимальными, и не проверять какие-либо значения, которые не будут использоваться. Однако во многих случаях это просто невозможно, и поэтому становится невозможным встраивать.

this Аргумент немного ложный, его значение может быть установлено вызовом, если это важно. Обычно он не используется (по крайней мере, у меня никогда не было проблем с этим в рекурсивных функциях). Вызов функции по имени имеет те же проблемы сthis так что я думаю, что это не имеет отношения кcallee это хорошо или плохо. Также,callee а такжеcaller только "устарели" в строгом режиме (ECMAscript ed. 5, декабрь 2009 г.), но я полагаю, что это не было известно в 2008 г., когда olliej писал.
Нет, я перечислил ряд причин, помимо того, что это затрудняет оптимизацию (хотя в целом история показала, что вещи, которые трудно оптимизировать, также имеют семантику, которой люди с трудом следуют)
Я все еще не вижу логики. В любом языке с функциями первого класса очевидно, что можно определить тело функции, которое может ссылаться на себя, не зная
Вы хотите сказать, что он ограничен только потому, что его трудно оптимизировать? Это немного глупо.
RobG указал на это, но я не думаю, что все было так ясно: повторное использование именованной функции сохранит только значениеthis еслиthis это глобальная сфера Во всех остальных случаях значениеthis will изменить после первого рекурсивного вызова, поэтому я думаю, что части вашего ответа, ссылающиеся на сохранениеthis не действительно действительны.
89

arguments.callee.caller являетсяnot устарела, хотя она используетFunction.caller имущество. (arguments.callee просто даст вам ссылку на текущую функцию)

Function.caller, though non-standard according to ECMA3, is implemented across all current major browsers. arguments.caller is deprecated in favour of Function.caller, and isn't implemented in some current major browsers (e.g. Firefox 3).

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

arguments.callee гиперссылка у вас на MDN говорит, что она убрана в строгом режиме. Разве это не то же самое, что устарел?
Это лучшее объяснение того, что является устаревшим, а что нет, очень полезно. Хороший пример того, что Function.caller не может сделать (получить трассировку стека рекурсивных функций), см.developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Хоть,arguments.callee запрещено в строгом режиме. Это меня тоже огорчило, но лучше больше его не использовать.
0

Просто продолжение. Значение «это» изменения во время рекурсии. В следующем (измененном) примере 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}     );

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

Потому что ты делаешь это неправильно. Еслиthis нужно поддерживать, пишиfactorial.call(this, n-1) Я действительно обнаружил при написании рекурсивного кода, что обычно нетthis, или жеthis ссылается на некоторый узел в дереве, и на самом деле хорошо, что он изменяется.

Похожие вопросы