Подтвердить что ты не робот

Почему руководство по стилю Airbnb говорит, что полагаться на вывод имени функции не рекомендуется?

// bad
class Listing extends React.Component {
  render() {
    return <div>{this.props.hello}</div>;
  }
}

// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
  <div>{hello}</div>
);

// good
function Listing({ hello }) {
  return <div>{hello}</div>;
}

Это взято из руководства по стилю реакции Airbnb. Может кто-нибудь объяснить, почему "полагаться на вывод имени функции не рекомендуется"? Это всего лишь проблема стиля?

4b9b3361

Ответ 1

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

Скажем, например, кто-то понял функцию стрелки:

(x) => x+2;

Чтобы регулярная функция была эквивалентной:

function(x) {
  return x+2;
}

Было бы довольно легко ожидать этот код:

let foo = (x) => x+2;

Чтобы быть эквивалентным:

let foo = function(x) {
  return x+2;
}

Если функция остается анонимной и неспособна ссылаться на себя, чтобы делать такие вещи, как рекурсия.

Итак, если тогда, в нашем блаженном невежестве, у нас было что-то вроде этого:

let foo = (x) => (x<2) ? foo(2) : "foo(1)? I should be a reference error";
console.log(foo(1));

Он успешно выполнится, потому что эта функция явно не была анонимной:

let foo = function foo(x) {
  return (x<2) ? foo(2) : "foo(1)? I should be a reference error";
}  

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

Например:

let foo = {
  bar: function() {}
} 

// Will surprisingly transpile to..

var foo = {
  bar: function bar() {}
}; 


// But doing something like:

var foo = {
  bar: function(x) {
    return (x<2) ? bar(2) : 'Whats happening!?';
  }
}

console.log(foo.bar(1));

// Will correctly cause a ReferenceError: bar is not defined

Вы можете проверить 'view compiled' на этом быстром DEMO, чтобы увидеть, как Babel фактически транслирует это, чтобы поддерживать поведение анонимной функции.


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

И, вероятно, подъем. Но эй, веселая прогулка.

Ответ 2

РЕДАКТИРОВАТЬ № 2: найти причину AirBnbs в руководство по стилю Javascript

Не забудьте назвать выражение - анонимные функции могут затруднить поиск проблемы в стеке вызовов ошибок (Обсуждение)

Оригинальный ответ ниже

В MDN есть хорошее представление о том, как работает выражение имени функции, включая два предупреждения:

Наблюдения

Существует нестандартное поведение вывода <function>.name в следующих двух сценариях:

  • при использовании интерпретаторов script

Интерпретатор script установит свойство имени функции только в том случае, если функция не имеет собственного свойства с именем name...

  1. при использовании js-инструментария

Будьте внимательны при использовании преобразований Function.name и исходного кода, таких как выполняемые компрессорами JavaScript (minifiers) или обфускаторами

....

В несжатой версии программа запускается в правую ветвь, а logs "foo" - это экземпляр "Foo", тогда как в сжатой версии он ведет себя по-разному и переходит в else-ветку. Поэтому, если вы полагаетесь на Function.name, как в приведенном выше примере, убедитесь, что ваш конвейер сборки не меняет имена функций или не предполагает, что функция имеет определенное имя.

Что такое вывод имени функции?

Свойство name возвращает имя функции или (до реализации ES6) пустую строку для анонимных функций

function doSomething() {}

console.log(doSomething.name); // logs "doSomething"

Функции, созданные с помощью синтаксиса new Function (...) или просто Function (...), имеют свойство name, заданное пустой строкой. В следующих примерах создаются анонимные функции, поэтому имя возвращает пустую строку

var f = function() {};
var object = {
  someMethod: function() {}
};

console.log(f.name == ''); // true
console.log(object.someMethod.name == ''); // also true

Браузеры, реализующие функции ES6, могут вывести имя анонимной функции из ее синтаксической позиции. Например:

var f = function() {};
console.log(f.name); // "f"

Мнение

Лично я предпочитаю (стрелка) функции, назначенные переменной по трем основным причинам:

Во-первых, я никогда не использую function.name

Во-вторых, смешивание лексического объема названных функций с назначением немного ослабляет:

// This...
function Blah() {
   //...
}
Blah.propTypes = {
 thing: PropTypes.string
}
// ...is the same as...
Blah.propTypes = {
 thing: PropTypes.string
}
function Blah() {
   //...
}

// ALTERNATIVELY, here lexical-order is enforced
const Blah = () => {
   //...
}
Blah.propTypes = {
    thing: PropTypes.string
}

И в-третьих, при прочих равных условиях я предпочитаю функции стрелок:

  • сообщите читателю, что нет this, no arguments и т.д.
  • выглядит лучше (imho)
  • (в прошлый раз я смотрел, функции стрелок были немного быстрее)

EDIT: моментальные снимки памяти

Я слушал Podcast, и гость рассказал о ситуации, когда ему приходилось иметь дело с ограничениями использования функций стрелок с профилированием памяти, Раньше я был в той же ситуации.

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

Плюс я использовал только снимки памяти один раз, поэтому мне комфортно оставлять по умолчанию "субъективность" для (субъективной) ясности.

Ответ 3

Это происходит потому, что:

const Listing = ({ hello }) => (
  <div>{hello}</div>
);

имеет выведенное имя листинга, хотя похоже, что вы его именовали, на самом деле это не так:

Пример

// we know the first three ways already...

let func1 = function () {};
console.log(func1.name); // func1

const func2 = function () {};
console.log(func2.name); // func2

var func3 = function () {};
console.log(func3.name); // func3

как насчет этого?

const bar = function baz() {
    console.log(bar.name); // baz
    console.log(baz.name); // baz
};

function qux() {
  console.log(qux.name); // qux
}

Ответ 4

Как и любое другое руководство по стилю, Airbnb самоуверен и не всегда хорошо аргументирован.

Свойство name функции не должно использоваться ни для чего, кроме отладки в клиентском приложении, потому что оригинальное имя функции теряется во время минимизации. Что касается отладки, она становится менее эффективной, если функция не имеет значимого имени в стеке вызовов, поэтому в некоторых случаях полезно сохранить ее.

Функция получает name с определением функции, например function Foo =() => {} и функцией с именем expression, как стрелкой в const Foo =() => {}. Это приводит к тому, что функция Foo имеет заданное имя, Foo.name === 'Foo'.

Некоторые транспортеры соответствуют спецификации. Бабель переносит этот код в ES5:

var Foo = function Foo() {};

И TypeScript нарушает спецификацию:

var Foo = function () {};

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

Проблема применима к переносимым приложениям. Это зависит от используемого транспилятора и необходимости сохранять свойство name функции. Проблема не существует в родной ES6.