Почему `obj.foo = function () {};` не присваивает функции имя `foo`?

Начиная с ES2015 (ES6), функции имеют собственные имена (включая официальное свойство name), и имена присваиваются при создании функции различными способами в дополнение к очевидному объявлению функции и именованному выражению функции, например, присвоение переменным (имя функции устанавливается равным имени переменной), присвоение свойствам объекта (имя функции устанавливается равным имени свойства), даже значения по умолчанию для параметров функции (имя функции устанавливается равным имени параметра). Но присвоение свойству существующего объекта (например, не в инициализаторе объекта) не присваивает функции имя этого свойства. Почему нет? Конечно, должна быть конкретная причина, по которой это нежелательно / невозможно. Что это было?

Чтобы было ясно: я не спрашиваю, как это обойти. Я спрашиваю, что мешает разрешить этот, казалось бы, очевидный случай, когда так много других (включая значения параметров по умолчанию!). Должна быть веская причина.

Пожалуйста, не рассуждайте и не теоретизируйте. У TC39 была причина не включать его. Мне интересно, в чем была причина. Я просмотрел заметки о совещании TC39, но еще не нашел их. Ближайшее, что я нашел до сих пор, - это Аллен Вирфс-Брок, отвечая Берги, чтобы сказать консенсуса по поводу этой формы не было из-за «различных возражений», но, к сожалению, он не сказал, в чем заключаются эти возражения.

Подробности:

Все перечисленное ниже присваивает имя foo функции в совместимом браузере:

// Requires a compliant browser

// Assigning to a variable or constant...
// ...whether in the initializer...
{
    let foo = function() { };
    console.log("1:", foo.name); // "foo"
}
{
    const foo = function() { };
    console.log("2:", foo.name); // "foo"
}
// ...or later...
{
    let foo;
    foo = function() { };
    console.log("3:", foo.name); // "foo"
}
// As an initializer for an object property
{
    const obj = {
        foo: function() { }
    };
    console.log("4:", obj.foo.name); // "foo"
}
// Or as a method
{
    const obj = {
        foo() { }
    };
    console.log("5:", obj.foo.name); // "foo"
}
// Even if it's a computed property name
{
    let name = "f";
    const obj = {
        [name + "o" + "o"]() { }
    };
    console.log("6:", obj.foo.name); // "foo"
}
// As a default value for a parameter
(function(foo = function() { }) {
    console.log("7:", foo.name); // "foo"
})();
// ...and a bunch of others

Но присвоение свойству существующего объекта вне инициализатора объекта не приводит:

const obj = {};
obj.foo = function() { };
console.log("Nope:", obj.foo.name);

Насколько я могу судить, это покрывается этот раздел спецификации, который явно устанавливает имя функции только в том случае, если IsIdentifierRef из LeftHandSideExpression истинно (что, очевидно, не для ссылок на свойства).

Итак, повторяя сверху: почему бы и нет? Конечно, должна быть конкретная причина, по которой это нежелательно / невозможно. Что это было?


person T.J. Crowder    schedule 12.12.2016    source источник
comment
Интересно, связано ли это с тем фактом, что foo["2for1"] = function... полностью действителен, но 2for1 не является допустимым именем функции ... хотя это в равной степени применимо к вычисленному примеру.   -  person Jared Smith    schedule 12.12.2016
comment
@JaredSmith: 2for1 - допустимое имя функции: jsfiddle.net/Lx825mgf Function имена, как и имена свойств, не обязательно должны быть действительными идентификаторами. :-) (Изменить: Хех, только что видел, как отредактировали ваш комментарий.)   -  person T.J. Crowder    schedule 12.12.2016
comment
В вашей скрипке есть функция, назначаемая имени свойства объекта с использованием непрозрачной строки, foo.2for1 = function... и function 2for1() {} оба выбрасывают, потому что идентификаторы JS не могут начинаться с чисел.   -  person Jared Smith    schedule 12.12.2016
comment
@ ДжаредСмит: Да. Имена функций, как и имена свойств, не обязательно должны быть действительными идентификаторами. Но, конечно, если они не являются действительными идентификаторами, вы не можете использовать их в качестве идентификаторов.   -  person T.J. Crowder    schedule 12.12.2016
comment
Я спросил об этом на esdiscuss, но причины не были упомянуты.   -  person Bergi    schedule 12.12.2016
comment
@Bergi: Спасибо. Как ... неудовлетворительно. К сожалению, AWB так и не вернулся к вам с различными возражениями. :-(   -  person T.J. Crowder    schedule 12.12.2016
comment
Нет, здесь нет ничего, основанного на мнении. Вопрос ясен в том, чтобы запрашивать достоверную информацию, а не мнение.   -  person T.J. Crowder    schedule 12.12.2016
comment
@ T.J.Crowder: да, это кажется довольно неприятным из-за того, что не реализовано присвоение имен для случая MemberExpression[Expression] = FunctionExpression.   -  person Jared Smith    schedule 12.12.2016
comment
Я постоянно удивлялся тому, что в форме выражения члена тоже не упоминаются функции. Может стоит новую ветку или пинговать ту старую?   -  person loganfsmyth    schedule 13.12.2016
comment
@loganfsmyth: Я копаюсь в заметках о встрече. Если не найду, то и сделаю.   -  person T.J. Crowder    schedule 13.12.2016
comment
Какое имя следует присвоить obj.prop1 = obj.prop2 = function() {}? Ничего не назначать - очень удобный способ избежать подобных спорных вопросов.   -  person artem    schedule 13.12.2016
comment
@artem Было бы prop2. Идентично поведению var x = y = function();. имя функции == y. Никакой двусмысленности нет, имя функции присваивается при создании функции. Фактически, то, что obj.prop1 = obj.prop2 не работает, а var x = y работает, само по себе сбивает с толку.   -  person CodingIntrigue    schedule 13.12.2016
comment
Теперь, с var x = y = function() ..., первое имя, которое я вижу, это var x, поэтому тот факт, что функция называется y, возможно, сбивает с толку и может считаться ошибкой. Я думаю, что правильный способ присвоить имя при создании функции - использовать однозначный синтаксис именованной функции: var x = y = function y() {}   -  person artem    schedule 13.12.2016
comment
@artem: Это имеет смысл и полностью соответствует тому, как работает язык. x = y = function() { }; оценивается как y = function() { } (создание функции, которая дает ей ее имя, присвоение y), затем это результирующее значение (существующая ссылка на функцию) присваивается x. Выполнение чего-либо другого нарушило бы семантику стандартных выражений и потребовало бы чрезвычайно сложных механизмов в спецификации для достижения.   -  person T.J. Crowder    schedule 13.12.2016
comment
Я бы сказал, что присвоение имени такой функции всегда нарушает семантику выражения присваивания. a = b присваивает значение b a, обычно b не зависит от a каким-либо образом. Создание особого случая, когда b является функцией, производной от a, кажется произвольным и ненужным, учитывая, что в течение долгого времени существовал способ дать имя любой функции.   -  person artem    schedule 13.12.2016
comment
@artem: Ничего страшного, вы имеете право не согласиться с TC39. (Я согласен с ними, что это была хорошая идея.) Но здесь не место для обсуждения проблем, которые могут возникнуть в связи со спецификацией.   -  person T.J. Crowder    schedule 13.12.2016
comment
Не уверен, что это вообще помогает, но const obj = {}; Object.defineProperty(obj, 'foo', {value:function(){}}); obj.foo.name; возвращает _2 _... что согласованно, но, вероятно, не предназначено.   -  person Kaiido    schedule 14.12.2016
comment
@Kaiido Хороший пример. В том-то и дело - здесь так много крайних случаев (и разные мнения о том, как функция должна быть неявно названа), что единственное, что может быть достигнуто, - это согласованность - и именно здесь у меня возникают проблемы с текущая реализация   -  person CodingIntrigue    schedule 14.12.2016
comment
По моему мнению, это по замыслу связано с ключевым словом (и концепцией) const. Значение константы не может измениться путем повторного присвоения и не может быть повторно объявлено. Не используйте const для изменяемых переменных.   -  person Travis J    schedule 17.12.2016
comment
@TravisJ: Это не имеет ничего общего с const (вы получите точно такие же результаты с let), и я не использую const для изменяемых переменных - вы не можете этого сделать в совместимом браузере. Я использую const для постоянных переменных, относящихся к изменяемым объектам, что является совершенно отдельной (и нормальной в JS) вещью. В настоящее время JavaScript не имеет хорошей поддержки глубокой неизменяемости (Object.freeze и тому подобное все еще оставляет дыры), и, хотя неизменяемость фантастична и полезна, это не единственный способ делать что-то.   -  person T.J. Crowder    schedule 17.12.2016
comment
Я удалил свой ответ, объясняющий, как работает рассматриваемый синтаксис, но в основном это вывод: синтаксис не называет функции. Вы спрашиваете, почему они не программируются в особом случае, когда имя функции. Как вы думаете, почему существует четкий ответ? Синтаксис делает то же самое независимо от имени функции. Другими словами, он ведет себя стабильно.   -  person Chris Rollins    schedule 21.12.2016
comment
@ChrisRollins: Нет, я спрашиваю, почему они не учли особый случай. И да, синтаксис дает имена функциям. Что касается того, почему я думаю, что есть конкретная причина, это потому, что: A) спецификация написана не случайно, подобные пробелы имеют объяснения, и B) так сказал Аллен Вирфс-Брок.   -  person T.J. Crowder    schedule 21.12.2016
comment
В синтаксисе не называются функции. foo.bar = function(){} - это не тот же синтаксис, что и foo={bar:function(){}}, последнее называет функции, а первое - нет. у них явно разное поведение. Во всех случаях первое не изменит свойство name функции. Если бы он менял его только в случае, если свойство было равно, это был бы особый случай. Вот о чем вы спрашиваете. Это было бы непоследовательным поведением, так почему дизайнер должен явно оправдывать невыполнение этого?   -  person Chris Rollins    schedule 21.12.2016
comment
@ChrisRollins: Верно, foo.bar = function(){} не называет функцию. foo = function(){} делает. В этом весь смысл вопроса. Есть что-то вроде полдюжины синтаксисов, использующих ранее анонимные выражения, которые затем присваиваются чему-то (переменной, свойству объекта во время инициализации объекта, значению параметра по умолчанию), которые делают, начиная с ES2015, присваивают имена . Есть одно вопиющее упущение. AWB сослался на возражения без подробного объяснения. Я хочу знать, какие они (или были).   -  person T.J. Crowder    schedule 22.12.2016
comment
Это тоже другой синтаксис. переменная - это совсем другое дело, чем свойство объекта. Но я полагаю, учитывая информацию, у вас есть единственный способ выяснить, в чем заключались эти возражения, - это выяснить, кто их выдвинул, и связаться с этими людьми?   -  person Chris Rollins    schedule 22.12.2016


Ответы (3)


Аллен Вирфс-Брок ответил в списке обсуждений es-Disc. / a> с возражениями, которые помешали консенсусу TC39 по форме obj.foo = function() { }:

...за

cache[getUserSecret(user)] = function() {};

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

и для

obj[someSymbol] = function() {}

это приведет к утечке значения символа как значения имени

и для

 table[n]=function() {}

имя, скорее всего, будет числовой строкой

У этих возражений есть счетчики (в частности, последнее, очень слабое; есть много других способов автоматического присвоения функции числового строкового имени), но дело не в этом; Дело в том, что это были выдвинутые возражения.

Он также добавил, что операция IsPropertyReference, которая потребуется (где в настоящее время есть только IsIdentifierRef) ...

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

Итак, в целом, очевидно, в то время, когда было принято решение, эти возражения имели значение (и, вполне возможно, теперь тоже), поэтому эта форма не называет функции автоматически, в отличие от многих других.

person T.J. Crowder    schedule 28.01.2017

Я прочитал ответы Аллена Вирфса-Брока, и он прямо говорит о возможных проблемах безопасности. Я лично с ним согласен.

Также могут быть проблемы с безопасностью. Свойство name потенциально просачивается через объект функции - имя переменной, которой оно изначально присвоено. Но мало что можно сделать с именем локальной переменной, кроме исходной функции. Но просочившееся имя свойства потенциально имеет большие возможности.

Похоже, возражения, о которых он говорит, как-то связаны с этим. Если TC39 не объяснил дальше свое решение, будет сложно понять, почему они так оставили :).

Мне очень жаль, что я ничем не могу вам помочь.

person Gabriel    schedule 20.12.2016
comment
Однако это возражение было снято; он будет применяться в равной степени ко всем другим формам (ну, не к форме значения параметра по умолчанию). Поскольку другие формы были продвинуты, это не может быть (намеренно), почему эта одна форма этого не сделала. - person T.J. Crowder; 20.12.2016
comment
Попробуйте здесь: 2ality.com/2015/09/function-names-es6 .html. Что-то в названии функции всегда присваивается во время создания. Вы можете понять это лучше меня, я не гуру ES6: D - person Gabriel; 20.12.2016

Я не уверен, что есть конкретная причина.

obj.foo = function (){};

сначала создает ссылку на выражение функции в obj, затем связывает foo с этой ссылкой, которая уже имеет (только для чтения) имя.

So:

obj.fooC = (new Function ());
console.log(obj.fooC.name);//'anonymous'

obj.fooFE = function (){};
console.log(obj.fooFE.name);//''

obj.fooNFE = function a_name (){};
console.log(obj.fooNFE.name);//'a_name'

это нормальное поведение.

Есть ли ограничение писать:

obj.foo = (function foo(){});
person scraaappy    schedule 12.01.2017
comment
Я не уверен, что существует конкретная причина. Аллен Вирфс-Брок сказал, что были, что были различные возражения, и поэтому консенсуса по этой форме не было, и поэтому она была исключена. Есть ли какие-то ограничения на запись? Нет, конечно, нет. :-) - person T.J. Crowder; 14.01.2017