「矢印関数」と「関数」は同等/交換可能ですか?


520

ES2015の矢印関数は、より簡潔な構文を提供します。

  • 関数の宣言/式をすべて矢印関数に置き換えることはできますか?
  • 何に注意する必要がありますか?

例:

コンストラクター関数

function User(name) {
  this.name = name;
}

// vs

const User = name => {
  this.name = name;
};

プロトタイプの方法

User.prototype.getName = function() {
  return this.name;
};

// vs

User.prototype.getName = () => this.name;

オブジェクト(リテラル)メソッド

const obj = {
  getName: function() {
    // ...
  }
};

// vs

const obj = {
  getName: () => {
    // ...
  }
};

コールバック

setTimeout(function() {
  // ...
}, 500);

// vs

setTimeout(() => {
  // ...
}, 500);

可変関数

function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// vs
const sum = (...args) => {
  // ...
};

5
ES2015の人気が高まるにつれ、矢印関数に関する同様の質問がますます増えています。この問題について良い標準的な質問/回答があるように感じなかったので、これを作成しました。すでに良いものがあるとお考えの場合は、お知らせください。重複しているか、削除するか、私が閉じます。サンプルを改善したり、新しいものを追加したりしてください。
Felix Kling

2
何についての機能を矢印にはJavaScript ecma6変更正常な機能?もちろん、通常の質問は、正規の質問として具体的に書かれた質問ほどよくて一般的ではありません。
Bergi

このPlnkrの例を見てください。変数thistimesCalled、ボタンが呼び出されるたびに1だけ大きく異なります。これは私の個人的な質問に答える:.click( () => { } )そして.click(function() { }) あなたがPlnkrのGUIDカウントから見ることができるようにループ内で使用する場合、両方が同じ関数の数を作成します。
abbaf33f 2016

回答:


750

tl; dr: いいえ!アロー関数と関数宣言/式は同等ではなく、盲目的に置き換えることはできません。
置き換える関数がを使用せthis、でもarguments呼び出されない場合は、newはい。


頻繁に:それは依存します。アロー関数は関数宣言/式とは異なる動作をするので、まず違いを見てみましょう:

1.語彙thisarguments

矢印関数には、独自の関数thisargumentsバインディングはありません。代わりに、これらの識別子は他の変数と同様に字句スコープで解決されます。ことを意味する矢印関数内、ことthisargumentsの値を参照thisし、arguments矢印機能がされている環境で定義された(すなわち、「外部」矢印関数)で:

// Example using a function expression
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: function() {
      console.log('Inside `bar`:', this.foo);
    },
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

// Example using a arrow function
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: () => console.log('Inside `bar`:', this.foo),
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

関数式の場合、this内で作成されたオブジェクトを参照しますcreateObject。矢印関数場合、thisを意味するthiscreateObject自体。

これによりthis、現在の環境のにアクセスする必要がある場合に、矢印関数が役立ちます。

// currently common pattern
var that = this;
getData(function(data) {
  that.data = data;
});

// better alternative with arrow functions
getData(data => {
  this.data = data;
});

これは、アロー関数をやで設定できないことも意味することに注意しくださいthis.bind.call

に慣れていない場合はthis、以下をお読みください。

2.アロー関数は、で呼び出すことはできません new

ある関数間ES2015区別呼んでいることや機能の構築が可能。関数が構築可能であるならば、それはで呼び出すことができ new、すなわちnew User()。関数が呼び出し可能であれば、それなしでnew呼び出すことができます(つまり、通常の関数呼び出し)。

関数宣言/式によって作成された関数は、構築可能であり、呼び出し可能です。
矢印関数(およびメソッド)は呼び出しのみ可能です。 classコンストラクタは構築可能です。

呼び出し不可能な関数を呼び出そうとしたり、構成不可能な関数を構築しようとすると、ランタイムエラーが発生します。


これを知って、次のように述べることができます。

交換可能:

  • thisまたはを使用しない関数arguments
  • で使用される関数 .bind(this)

交換不可

  • コンストラクター関数
  • プロトタイプに追加された関数/メソッド(通常使用するためthis
  • 可変関数(使用する場合arguments(下記参照))

あなたの例を使ってこれを詳しく見てみましょう:

コンストラクター関数

アロー関数はで呼び出せないため、これは機能しませんnew。関数の宣言/式を使い続けるか、を使用しますclass

プロトタイプの方法

プロトタイプメソッドは通常this、インスタンスへのアクセスに使用するため、ほとんどの場合そうではありません。を使用しない場合はthis、交換できます。ただし、主に簡潔な構文を重視する場合はclass、その簡潔なメソッド構文で使用します。

class User {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

オブジェクトのメソッド

オブジェクトリテラルのメソッドについても同様です。メソッドがを介してオブジェクト自体を参照する場合は、this関数式を引き続き使用するか、新しいメソッド構文を使用します。

const obj = {
  getName() {
    // ...
  },
};

コールバック

場合によります。アウターにエイリアスを付けている場合、thisまたは使用している場合は、必ず置き換えてください.bind(this)

// old
setTimeout(function() {
  // ...
}.bind(this), 500);

// new
setTimeout(() => {
  // ...
}, 500);

ただし、コールバックを呼び出すコードが明示的にthis特定の値に設定されている場合、特にjQueryのイベントハンドラーでよくあることであり、コールバックがthis(またはarguments)を使用する場合、矢印関数使用できません

可変関数

アロー関数には独自のargumentsがないため、単純にアロー関数に置き換えることはできません。ただし、ES2015ではargumentsrestパラメータを使用する代わりの方法が導入されています

// old
function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// new
const sum = (...args) => {
  // ...
};

関連する質問:

その他のリソース:


6
語彙thisも影響しsuper、影響がないことを言及する価値があるかもしれません.prototype
loganfsmyth

1
また、それらは構文的に交換可能ではないことにも言及しておくとよいでしょう-アロー関数(AssignmentExpression)は、関数式(PrimaryExpression)がどこにでもドロップできず、かなり頻繁に人々をつまずきます(特に、構文解析が行われて以来)主要なJS実装のエラー)。
JMM 2016

@JMM:「かなり頻繁に人をつまずかせます具体的な例を教えていただけますか?仕様をざっと見てみると、FEを配置できてもAFを配置できない場所では、とにかくランタイムエラーが発生するようです...
Felix Kling

もちろん、関数式(() => {}())のような矢印関数をすぐに呼び出そうとしたり、のようなことをしたりすることを意味しx || () => {}ます。つまり、実行時(解析)エラーです。(それが事実であっても、かなり頻繁にエラーがエラーであると考えられます。)解析または実行されたときに必ずしもエラーではないために気付かれないであろう論理エラーをカバーしようとしているだけですか?newの1つは実行時エラーですよね?
JMM

これが実際に登場するいくつかのリンクがあります:substack / node-browserify#1499babel / babel-eslint#245(これは非同期矢印ですが、基本的な問題は同じだと思います)、および今では見つけるのが難しいバベルですが、ここにT2847があります。
JMM

11

矢印関数=>これまでのところ最高のES6機能。これらは、私が常に使用している、ES6の非常に強力な追加機能です。

待ってください、あなたはコードのどこでも矢印関数を使うことができません、それはthis矢印関数が使用できない場所のようなすべてのケースで機能することはありません。間違いなく、arrow関数はコードを単純化する優れた機能です。

ただし、動的コンテキストが必要な場合は、矢印関数を使用できません。メソッドの定義、コンストラクターを使用したオブジェクトの作成、イベントの処理時にこれからターゲットを取得します。

矢印関数は次の理由で使用しないでください。

  1. 彼らは持っていない this

    「字句スコープ」を使用して、「this」の値がどうあるべきかを理解します。簡単な単語のレキシカルスコープではthis、関数本体の内部から「」を使用します。

  2. 彼らは持っていない arguments

    矢印関数にはargumentsオブジェクトがありません。ただし、残りのパラメーターを使用して同じ機能を実現できます。

    let sum = (...args) => args.reduce((x, y) => x + y, 0) sum(3, 3, 1) // output - 7 `

  3. で使用することはできません new

    アロー関数はプロトタイププロパティを持たないため、コンストラクターになることはできません。

アロー機能を使用する場合と使用しない場合:

  1. これにアクセスできないため、オブジェクトリテラルのプロパティとして関数を追加するために使用しないでください。
  2. 関数式はオブジェクトメソッドに最適です。アロー機能は次のようにコールバックや方法のために最適ですmapreduceまたはforEach
  3. 名前で呼び出す関数には関数宣言を使用します(関数が巻き上げられるため)。
  4. コールバックには矢印関数を使用します(より簡潔になる傾向があるため)。

2
2.それらには引数がありません。申し訳ありませんが、真実ではありません。...演算子を使用せずに引数を持つことができます。おそらく、配列に引数が含まれていないと言いたいかもしれません
Carmine Tambascia

特別な程度@CarmineTambascia読むarguments:ここに矢印の機能では使用できませんオブジェクトdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
vichle

0

で矢印関数を使用するためfunction.prototype.callに、オブジェクトプロトタイプにヘルパー関数を作成しました。

  // Using
  // @func = function() {use this here} or This => {use This here}
  using(func) {
    return func.call(this, this);
  }

使用法

  var obj = {f:3, a:2}
  .using(This => This.f + This.a) // 5

編集する

あなたはヘルパーを必要としません。あなたがすることができます:

var obj = {f:3, a:2}
(This => This.f + This.a).call(undefined, obj); // 5
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.