Почему `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 равен true (что, очевидно, не для ссылок на свойства).
Итак, повторяя сверху: почему бы и нет? Конечно, должна быть конкретная причина, по которой это нежелательно / невозможно. Что это было?
3 ответа
Аллен Уирфс-Брок ответил на список es-обсудить возражениями, которые помешали консенсусу TC39 по obj.foo = function() { }
форма:
...за
cache[getUserSecret(user)] = function() {};
было бы утечка секретной информации пользователя в качестве значения имени
и для
obj[someSymbol] = function() {}
было бы утечка значения символа в качестве значения имени
и для
table[n]=function() {}
имя, скорее всего, будет числовой строкой
Есть возражения против этих возражений (особенно последнее, которое чрезвычайно слабо; есть много других способов, которым функция автоматически присваивает числовое имя строки), но это не главное; Дело в том, что это были возражения.
Он также добавил, что потребуется операция IsPropertyReference (где в настоящее время есть только IsIdentifierRef)...
... является операцией во время выполнения, и новая семантика требует определения значения имени во время выполнения. Это все дополнительная работа во время выполнения, которая может замедлить создание замыканий функций, которые появляются внутри циклов.
Таким образом, в целом, по-видимому, в то время, когда решение было принято, эти возражения перенесли день (и вполне возможно, что теперь также), поэтому эта форма не называет функции автоматически, тогда как многие другие делают.
Я прочитал ответы Аллена Уирфса-Брока, и он подробно рассказал о возможных проблемах безопасности. Я лично с ним согласен.
Также могут быть проблемы с безопасностью. Свойство name потенциально может передавать через объект функции имя переменной, которой оно изначально назначено. Но мало кто мог бы сделать с именем локальной переменной, кроме исходной функции. Но просочившееся имя свойства потенциально обладает большей способностью.
Кажется, что возражения, о которых он говорит, как-то связаны с этим. Если TC39 не объяснил, почему это решение, будет сложно выяснить, почему они так его оставили:).
Извините, я не могу вам помочь.
Я не уверен, что есть конкретная причина.
obj.foo = function (){};
сначала создает ссылку на выражение функции в obj, затем связывает foo с этой ссылкой, имя которой уже (только для чтения).
Так:
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(){});
Идея о функции, имеющей имя, немного странна. Если вы рассматриваете функцию как просто переменную другого типа, то ее назначение не должно изменять внутреннее состояние назначенных переменных. Это как волшебство. Это становится более запутанным, если функции передаются (хотя это может быть решено путем назначения имен только при создании функции, а не при ее передаче).
Мне кажется, что была одна группа, которая вообще ненавидела идею имен функций, и другая группа, которая их любила. Они пошли на компромисс, чтобы создать их только для инициализаторов объектов.
Я лично согласен с группой, которая считает, что названия функций странные, но я также думаю, что, как только вы это сделаете, вы должны пройти весь путь. Но люди такие глупые. Компромиссы не всегда имеют смысл.