web-dev-qa-db-ja.com

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の反応スタイルガイドから取られています。誰かが「関数名の推論に依存することを推奨しない」理由を説明できますか?それは単なるスタイルの懸念ですか?

46
xiaofan2406

これは、匿名関数であると予想されるものにレキシカル名を暗黙的に付与することから、予期せぬ動作に関係していると思われます。

例えば、誰かが矢印の機能を理解したとしましょう:

(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";
}  

これは、バベルが匿名関数に暗黙的に名前を追加する他の状況では、潜在的に悪化する可能性があります(実際には、最初は暗黙的な関数名をサポートすることの少しの副作用ですが、間違っている可能性がありますその上で)、それらはEdgeケースを正しく処理し、予想される場所に参照エラーをスローします。

例えば:

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

このクイック [〜#〜] demo [〜#〜] で「コンパイルされたビュー」をチェックすると、匿名関数の動作を維持するためにBabelが実際にどのように変換しているかを確認できます。


要するに、あなたがコードに何を期待するかを正確に知っているので、あなたがしていることを明示することは、一般的には良い考えです。暗黙的な関数の命名の使用をやめることは、これをサポートするための文体的な選択である可能性がありますが、簡潔で簡単なままです。

そしておそらく巻き上げ。しかし、ちょっと、楽しいサイドトリップ。

27
Brad Colthurst

編集#2:AirBnbsの理由を Javascriptスタイルガイド で見つけました

式に名前を付けることを忘れないでください-匿名関数を使用すると、エラーの呼び出しスタックで問題を特定するのが難しくなります( Discussion

以下の元の回答

MDNには、関数名の推論がどのように機能するかについて、2つの警告を含む適切な要約があります。

観察

非標準の<function>.name次の2つのシナリオでの推論動作:

  1. スクリプトインタープリターを使用する場合

関数にnameという独自のプロパティがない場合にのみ、スクリプトインタプリタは関数のnameプロパティを設定します...

  1. jsツールを使用する場合

Function.nameとJavaScriptコンプレッサー(ミニファイナー)または難読化ツールによって実行されるようなソースコード変換を使用する場合は注意してください

....

圧縮されていないバージョンでは、プログラムはtruthy-branchに実行され、 'foo'は 'Foo'のインスタンスであると記録されますが、圧縮されたバージョンでは動作が異なり、else-branchに実行されます。したがって、上記の例のように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"

意見

個人的には、3つの基本的な理由から変数に割り当てられた(矢印)関数を好みます。

まず、everを使用しない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、_argumentsなどがないことを読者に伝える
  • 良く見える(imho)
  • パフォーマンス(前回見たとき、矢印関数はわずかに高速でした)

編集:メモリのスナップショット

私は Podcast を聞いていましたが、ゲストはメモリプロファイリングで矢印関数を使用する際の制限に対処しなければならなかったという状況を話しましたが、私はexact前の状況。

現在、メモリスナップショットには変数名が含まれていません。したがって、メモリプロファイラを接続するためだけに、矢印関数を名前付き関数に変換することがあります。私の経験は非常に簡単で、矢印機能にはまだ満足しています。

さらに、メモリスナップショットは1回しか使用していません。そのため、既定では(主観的な)明確さのために、いくつかの "計装"を忘れてしまいます。

23
Ashley Coolman

それの訳は:

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
JordanHendrix

他のスタイルガイドと同様に、Airbnb'sは意見があり、常に十分な理由があるわけではありません。

Function name property は、関数の元の名前が縮小中に失われるため、クライアント側アプリケーションでのデバッグ以外には使用しないことになっています。デバッグに関しては、関数の呼び出しスタックに意味のある名前がないと効率が低下するため、場合によっては保存しておくと便利です。

関数は、function Foo = () => {}のような関数定義とconst Foo = () => {}の矢印のような式という名前の関数の両方でnameを取得します。これにより、Foo関数が特定の名前Foo.name === 'Foo'

一部のトランスパイラーは仕様に従います。 BabelはこのコードをES5に変換します。

var Foo = function Foo() {};

また、TypeScriptは仕様を破ります。

var Foo = function () {};

これは、名前付き関数式が悪いことを意味するものではありません。トランスパイラーが仕様に準拠しているか、関数名が重要でない限り、この懸念は破棄できます。

この問題は、コンパイルされたアプリケーションに適用されます。それは、使用中のトランスパイラーと、関数nameプロパティを保持する必要性に依存します。問題はネイティブES6には存在しません。

2
Estus Flask