この章では、ES6 で呼び出し可能なエンティティ(関数呼び出し、メソッド呼び出しなどを介して)を適切に使用する方法について説明します。
super を介した呼び出しは特定の場所に限定されるObject.prototype および Array.prototype の省略形name プロパティnamenew を介して呼び出されたかどうかを判断するにはどうすればよいですか?ES5 では、単一の構成要素である(従来の)関数が 3 つの役割を果たしていました。
ES6 では、より専門化が進んでいます。3 つの役割は、現在次のように処理されます。関数定義とクラス定義に関する限り、定義は宣言または式のいずれかです。
特にコールバックの場合、アロー関数は便利です。これは、周囲のスコープの this をシャドウしないためです。
長いコールバックやスタンドアロン関数には、従来の関数でもかまいません。一部の API では、this を暗黙的なパラメーターとして使用します。その場合、従来の関数を使用するしかありません。
注意:
これらのエンティティは動作が異なりますが(後述)、すべて関数です。例えば
一部の呼び出しはどこでも実行できますが、他の呼び出しは特定の場所に制限されます。
ES6 では、次の 3 種類の呼び出しをどこでも実行できます。
func(3, 1)obj.method('abc')new Constr(8)super を介した呼び出しは特定の場所に限定される super キーワードを介して 2 種類の呼び出しを行うことができます。これらの使用は特定の場所に限定されます。
super.method('abc')super(8)constructor() 内でのみ使用可能です。メソッドではない関数とメソッドの違いは、ECMAScript 6 でより顕著になっています。両方に対して特別なエンティティがあり、それらだけができることがあります。
this を取得します(「字句 this」)。super のサポートを提供します。このセクションでは、呼び出し可能エンティティを使用するためのヒントを示します。どのエンティティを使用するのが最適か、など。
コールバックとして、アロー関数には従来の関数よりも 2 つの利点があります。
this は字句であり、したがって使用がより安全です。this 残念ながら、一部の JavaScript API では、コールバックの暗黙的な引数として this を使用するため、アロー関数を使用できません。例:B 行の this は、A 行の関数の暗黙的な引数です。
beforeEach(function () { // (A)
    this.addMatchers({ // (B)
        toBeInRange: function (start, end) {  
            ···
        }  
    });  
});  
このパターンは明示性が低く、アロー関数の使用を妨げます。
これは簡単に修正できますが、API を変更する必要があります。
beforeEach(api => {
    api.addMatchers({
        toBeInRange(start, end) {
            ···
        }
    });
});
API を暗黙的なパラメーター this から明示的なパラメーター api に変えました。私はこの種の明示性が好きです。
this の値にアクセスする 一部の API では、this の値を取得する別の方法があります。たとえば、次のコードでは this を使用しています。
var $button = $('#myButton');
$button.on('click', function () {
    this.classList.toggle('clicked');
});
ただし、イベントのターゲットには event.target を介してアクセスすることもできます。
var $button = $('#myButton');
$button.on('click', event => {
    event.target.classList.toggle('clicked');
});
(コールバックではなく)スタンドアロン関数として、関数宣言を優先します。
function foo(arg1, arg2) {
    ···
}
利点は次のとおりです。
function は利点です。構造を目立たせたいからです。1 つの注意点があります。通常、スタンドアロン関数で this は必要ありません。それを使用する場合は、周囲のスコープ(たとえば、スタンドアロン関数を含むメソッド)の this にアクセスする必要があります。残念ながら、関数宣言ではそれができません。独自の this があり、周囲のスコープの this をシャドウします。したがって、リンターに、関数宣言内の this について警告させたい場合があります。
スタンドアロン関数のもう 1 つのオプションは、アロー関数を変数に割り当てることです。this の問題は、字句であるため回避されます。
const foo = (arg1, arg2) => {
    ···
};
メソッド定義は、super を使用するメソッドを作成する唯一の方法です。これらはオブジェクトリテラルとクラス(メソッドを定義する唯一の方法)では当然の選択肢ですが、既存のオブジェクトにメソッドを追加する場合はどうでしょうか?例:
MyClass.prototype.foo = function (arg1, arg2) {
    ···
};
以下は、ES6 で同じことを行う簡単な方法です(注意:Object.assign() は super を使用したメソッドを適切に移動しません)。
Object.assign(MyClass.prototype, {
    foo(arg1, arg2) {
        ···
    }
});
詳細と注意点については、Object.assign() に関するセクションを参照してください。
通常、関数値プロパティはメソッド定義を介して作成する必要があります。ただし、アロー関数の方が適切な場合もあります。次の 2 つのサブセクションでは、いつ何を使用するかについて説明します。前者の方がメソッドを持つオブジェクトに適しており、後者の方がコールバックを持つオブジェクトに適しています。
プロパティが本当にメソッドである場合は、メソッド定義を介して関数値プロパティを作成します。これは、プロパティ値がオブジェクト(次の例の obj)およびその兄弟メソッドと密接に関連しており、周囲のスコープ(例の surroundingMethod())には関連がない場合に当てはまります。
メソッド定義では、プロパティ値の this は、メソッド呼び出しの レシーバー(例:メソッド呼び出しが obj.m(···) の場合は obj)です。
たとえば、WHATWG Streams API を次のように使用できます。
const surroundingObject = {
    surroundingMethod() {
        const obj = {
            data: 'abc',
            start(controller) {
                ···
                console.log(this.data); // abc (*)
                this.pull(); // (**)
                ···
            },
            pull() {
                ···
            },
            cancel() {
                ···
            },
        };
        const stream = new ReadableStream(obj);
    },
};
obj は、プロパティ start、pull、および cancel が実際のメソッドであるオブジェクトです。したがって、これらのメソッドは this を使用してオブジェクトローカルの状態(* 行)にアクセスし、相互に呼び出すことができます(** 行)。
プロパティ値がコールバックである場合は、アロー関数を介して関数値プロパティを作成します。このようなコールバックは、オブジェクトに格納されている(例の obj)のではなく、周囲のスコープ(次の例の surroundingMethod())と密接に関連する傾向があります。
アロー関数の this は、周囲のスコープ(字句 this)の this です。アロー関数は優れたコールバックです。これは、コールバック(実際の、メソッドではない関数)で通常必要な動作であるためです。コールバックには、周囲のスコープの this をシャドウする独自の this を持たないようにする必要があります。
プロパティ start、pull、および cancel がアロー関数の場合、surroundingMethod()(周囲のスコープ)の this を取得します。
const surroundingObject = {
    surroundingData: 'xyz',
    surroundingMethod() {
        const obj = {
            start: controller => {
                ···
                console.log(this.surroundingData); // xyz (*)
                ···
            },
            pull: () => {
                ···
            },
            cancel: () => {
                ···
            },
        };
        const stream = new ReadableStream(obj);
    },
};
const stream = new ReadableStream();
* 行の出力に驚いた場合は、次のコードを検討してください。
const obj = {
    foo: 123,
    bar() {
        const f = () => console.log(this.foo); // 123
        const o = {
            p: () => console.log(this.foo), // 123
        };
    },
}
メソッド bar() 内では、f の動作はすぐに理解できるはずです。o.p の動作はそれほど明白ではありませんが、f の動作と同じです。両方のアロー関数には、同じ周囲の字句スコープである bar() があります。後者のアロー関数がオブジェクトリテラルで囲まれているからといって、それが変わるわけではありません。
このセクションでは、ES6 で IIFE を避けるためのヒントを示します。
ES5では、変数をローカルに保ちたい場合、IIFEを使用する必要がありました。
(function () {  // open IIFE
    var tmp = ···;
    ···
}());  // close IIFE
console.log(tmp); // ReferenceError
ECMAScript 6では、ブロックとletまたはconst宣言を使用するだけで済みます。
{  // open block
    let tmp = ···;
    ···
}  // close block
console.log(tmp); // ReferenceError
ライブラリ(RequireJS、browserify、webpackなど)を介してモジュールを使用しないECMAScript 5のコードでは、リビーリングモジュールパターンが一般的であり、IIFEに基づいています。その利点は、何がパブリックで何がプライベートかを明確に区別することです。
var my_module = (function () {
    // Module-private variable:
    var countInvocations = 0;
    function myFunc(x) {
        countInvocations++;
        ···
    }
    // Exported by module:
    return {
        myFunc: myFunc
    };
}());
このモジュールパターンはグローバル変数を生成し、次のように使用されます。
my_module.myFunc(33);
ECMAScript 6では、モジュールが組み込まれているため、モジュールを採用する障壁が低くなります。
// my_module.js
// Module-private variable:
let countInvocations = 0;
export function myFunc(x) {
    countInvocations++;
    ···
}
このモジュールはグローバル変数を生成せず、次のように使用されます。
import { myFunc } from 'my_module.js';
myFunc(33);
ES6でも即時実行関数が必要なユースケースが1つあります。つまり、単一の式ではなく、一連のステートメントによってのみ結果を生成できる場合があります。これらのステートメントをインライン化したい場合は、関数を即時実行する必要があります。ES6では、即時実行アロー関数を使用すると、数文字を節約できます。
const SENTENCE = 'How are you?';
const REVERSED_SENTENCE = (() => {
    // Iteration over the string gives us code points
    // (better for reversal than characters)
    const arr = [...SENTENCE];
    arr.reverse();
    return arr.join('');
})();
示されているように括弧で囲む必要があることに注意してください(括弧は、関数呼び出し全体ではなく、アロー関数の周りにあります)。詳細については、アロー関数の章で説明されています。
ES5では、コンストラクター関数がオブジェクトのファクトリを作成する主流の方法でした(ただし、他にも多くの手法があり、一部はよりエレガントであると言えるでしょう)。ES6では、クラスがコンストラクター関数を実装する主流の方法です。いくつかのフレームワークは、カスタム継承APIの代替としてそれらをサポートしています。
このセクションでは、各ES6の呼び出し可能エンティティを詳細に説明する前に、チートシートから始めます。
エンティティによって生成された値の特性
| 関数宣言/関数式 | アロー | クラス | メソッド | |
|---|---|---|---|---|
| 関数呼び出し可能 | ✔ | ✔ | × | ✔ | 
| コンストラクター呼び出し可能 | ✔ | × | ✔ | × | 
| プロトタイプ | F.p | F.p | SC | F.p | 
| プロパティ prototype | ✔ | × | ✔ | × | 
エンティティ全体の特性
| 関数宣言 | 関数式 | アロー | クラス | メソッド | |
|---|---|---|---|---|---|
| 巻き上げ | ✔ | × | |||
| windowプロパティを作成(1) | ✔ | × | |||
| 内部名(2) | × | ✔ | ✔ | × | 
エンティティの本体の特性
| 関数宣言 | 関数式 | アロー | クラス(3) | メソッド | |
|---|---|---|---|---|---|
| this | ✔ | ✔ | lex | ✔ | ✔ | 
| new.target | ✔ | ✔ | lex | ✔ | ✔ | 
| super.prop | × | × | lex | ✔ | ✔ | 
| super() | × | × | × | ✔ | × | 
凡例–テーブルセル
F.p:Function.prototypeFunction.prototype。詳細については、クラスの章で説明されています。凡例–脚注
ジェネレーター関数とメソッドはどうですか? これらは、2つの例外を除いて、ジェネレーターではない対応するものと同様に機能します。
(GeneratorFunction).prototypeを持っています((GeneratorFunction)は内部オブジェクトです。「イテレーションAPI内の継承(ジェネレーターを含む)」セクションの図を参照)。thisのルール | 関数呼び出し | メソッド呼び出し | new | |
|---|---|---|---|
| 従来の関数(厳格) | undefined | レシーバー | インスタンス | 
| 従来の関数(非厳格) | window | レシーバー | インスタンス | 
| ジェネレーター関数(厳格) | undefined | レシーバー | TypeError | 
| ジェネレーター関数(非厳格) | window | レシーバー | TypeError | 
| メソッド(厳格) | undefined | レシーバー | TypeError | 
| メソッド(非厳格) | window | レシーバー | TypeError | 
| ジェネレーターメソッド(厳格) | undefined | レシーバー | TypeError | 
| ジェネレーターメソッド(非厳格) | window | レシーバー | TypeError | 
| アロー関数(厳格&非厳格) | レキシカル | レキシカル | TypeError | 
| クラス(暗黙的に厳格) | TypeError | TypeError | SCプロトコル | 
凡例–テーブルセル
thisを介して新しいインスタンスを受け取ります。派生クラスはスーパークラスからインスタンスを取得します。詳細については、クラスの章で説明されています。これらは、ES5で知られている関数です。それらを作成する方法は2つあります。
const foo = function (x) { ··· };
function foo(x) { ··· }
thisのルール
thisは、厳格モードの関数ではundefinedであり、非厳格モードではグローバルオブジェクトです。thisは、メソッド呼び出しのレシーバー(またはcall/applyの最初の引数)です。thisは、新しく作成されたインスタンスです。ジェネレーター関数については、ジェネレーターの章で説明されています。構文は従来の関数に似ていますが、アスタリスクが追加されています。
const foo = function* (x) { ··· };
function* foo(x) { ··· }
thisのルールは次のとおりです。thisはジェネレーターオブジェクトを参照しないことに注意してください。
thisは、従来の関数と同じように処理されます。このような呼び出しの結果はジェネレーターオブジェクトです。TypeErrorがスローされます。メソッド定義は、オブジェクトリテラル内に記述できます。
const obj = {
    add(x, y) {
        return x + y;
    }, // comma is required
    sub(x, y) {
        return x - y;
    }, // comma is optional
};
また、クラス定義内にも記述できます。
class AddSub {
    add(x, y) {
        return x + y;
    } // no comma
    sub(x, y) {
        return x - y;
    } // no comma
}
ご覧のとおり、オブジェクトリテラルのメソッド定義はコンマで区切る必要がありますが、クラス定義ではメソッド定義間に区切り文字はありません。前者は、特にゲッターとセッターに関して、構文の一貫性を保つために必要です。
メソッド定義は、superを使用してスーパープロパティを参照できる唯一の場所です。superを使用するメソッド定義のみが、その機能に必要な内部プロパティ[[HomeObject]]を持つ関数を生成します(詳細については、クラスの章で説明されています)。
ルール
superを使用できます。TypeErrorをスローします。クラス定義内では、名前がconstructorのメソッドは、この章で後述するように特別です。
ジェネレーターメソッドについては、ジェネレーターの章で説明されています。構文はメソッド定義に似ていますが、アスタリスクが追加されています。
const obj = {
    * generatorMethod(···) {
        ···
    },
};
class MyClass {
    * generatorMethod(···) {
        ···
    }
}
ルール
thisとsuperを使用できます。アロー関数については、独自の章で説明されています。
const squares = [1,2,3].map(x => x * x);
次の変数は、アロー関数内でレキシカルです(周囲のスコープから取得されます)。
argumentssuperthisnew.targetルール
thisなど。thisは引き続きレキシカルであり、メソッド呼び出しのレシーバーを参照しません。TypeErrorを生成します。クラスについては、独自の章で説明されています。
// Base class: no `extends`
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return `(${this.x}, ${this.y})`;
    }
}
// This class is derived from `Point`
class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y);
        this.color = color;
    }
    toString() {
        return super.toString() + ' in ' + this.color;
    }
}
メソッドconstructorは、クラスの「実体」になるため、特別です。つまり、クラスはコンストラクター関数と非常に似ています。
ルール
thisはそれを参照します。派生クラスはスーパークラスからインスタンスを受け取るため、thisにアクセスする前にsuper()を呼び出す必要があります。JavaScriptには、メソッドを呼び出す方法が2つあります。
arr.slice(1)):プロパティsliceは、arrのプロトタイプチェーンで検索されます。その結果は、thisがarrに設定された状態で呼び出されます。Array.prototype.slice.call(arr, 1)):sliceは、thisがarr(call()の最初の引数)に設定された状態で直接呼び出されます。このセクションでは、これら2つがどのように機能し、ECMAScript 6でメソッドを直接呼び出すことがほとんどない理由について説明します。始める前に、プロトタイプチェーンの知識を更新しましょう。
JavaScriptの各オブジェクトは、実際には1つ以上のオブジェクトのチェーンであることを思い出してください。最初のオブジェクトは、後続のオブジェクトからプロパティを継承します。たとえば、配列['a', 'b']のプロトタイプチェーンは次のようになります。
'a'と'b'を保持しています。Array.prototype。Arrayコンストラクターによって提供されるプロパティ。Object.prototype。Objectコンストラクターによって提供されるプロパティ。null (チェーンの終端。実際にはメンバーではありません)。チェーンは、Object.getPrototypeOf()で調べることができます。
「早い」オブジェクトのプロパティは、「遅い」オブジェクトのプロパティを上書きします。たとえば、Array.prototypeはtoString()メソッドの配列固有のバージョンを提供し、Object.prototype.toString()を上書きします。
メソッド呼び出しarr.toString()を見ると、実際には2つのステップが実行されていることがわかります。
arrのプロトタイプチェーンで、名前がtoStringである最初のプロパティの値を取得します。thisをメソッド呼び出しの*レシーバー*arrに設定します。関数のcall()メソッドを使用することで、この2つのステップを明示的に行うことができます。
JavaScriptには、直接メソッド呼び出しを行う2つの方法があります。
Function.prototype.call(thisValue, arg0?, arg1?, ···)Function.prototype.apply(thisValue, argArray)メソッドcallとメソッドapplyは両方とも関数で呼び出されます。これらは、thisの値を指定するという点で、通常の関数呼び出しとは異なります。callは、個々のパラメータを介してメソッド呼び出しの引数を提供し、applyは、Arrayを介して引数を提供します。
ディスパッチされたメソッド呼び出しでは、レシーバーは2つの役割を果たします。メソッドを見つけるために使用され、暗黙的なパラメータになります。最初の役割の問題は、メソッドを呼び出すには、オブジェクトのプロトタイプチェーンにメソッドが含まれている必要があるということです。直接メソッド呼び出しでは、メソッドはどこからでも取得できます。これにより、別のオブジェクトからメソッドを借りることができます。たとえば、Object.prototype.toStringを借りて、配列arrにtoStringの元の、オーバーライドされていない実装を適用できます。
> const arr = ['a','b','c'];
> Object.prototype.toString.call(arr)
'[object Array]'
配列版のtoString()は異なる結果を生成します。
さまざまなオブジェクト(「それらの」コンストラクターのインスタンスだけでなく)で動作するメソッドは、*ジェネリック*と呼ばれます。*Speaking JavaScript*には、ジェネリックなメソッドのリストがあります。このリストには、ほとんどの配列メソッドと、Object.prototypeのすべてのメソッド(すべてのオブジェクトで動作する必要があるため、暗黙的にジェネリックです)が含まれています。
このセクションでは、直接メソッド呼び出しのユースケースについて説明します。毎回、最初にES5でのユースケースを説明し、次にES6での変更点(直接メソッド呼び出しがほとんど必要なくなる場合)について説明します。
一部の関数は複数の値を受け入れますが、パラメータごとに1つの値のみを受け入れます。Arrayを介して値を渡したい場合はどうすればよいでしょうか。
たとえば、push()を使用すると、複数の値を配列に破壊的に追加できます。
ただし、配列全体を破壊的に追加することはできません。apply()を使用することで、その制限を回避できます。
同様に、Math.max()とMath.min()は単一の値に対してのみ機能します。
apply()を使用すると、配列に対して使用できます。
...)は、ほとんどの場合apply()に取って代わる 配列を引数に変えるためだけにapply()を介して直接メソッド呼び出しを行うのは面倒です。そのため、ECMAScript 6には、このためのスプレッド演算子(...)があります。これは、ディスパッチされたメソッド呼び出しでもこの機能を提供します。
別の例
おまけに、スプレッドはnew演算子でも機能します。
apply()はnewでは使用できないことに注意してください。上記の偉業は、ECMAScript 5で複雑な回避策によってのみ実現できます。
JavaScriptの一部のオブジェクトは*配列のような*オブジェクトであり、ほとんど配列ですが、配列メソッドはありません。2つの例を見てみましょう。
最初に、関数の特別な変数argumentsは配列のようです。これには、lengthと、要素へのインデックス付きアクセスがあります。
> var args = function () { return arguments }('a', 'b');
> args.length
2
> args[0]
'a'
ただし、argumentsはArrayのインスタンスではなく、map()メソッドがありません。
> args instanceof Array
false
> args.map
undefined
次に、DOMメソッドdocument.querySelectorAll()はNodeListのインスタンスを返します。
したがって、多くの複雑な操作では、最初に配列のようなオブジェクトを配列に変換する必要があります。これは、Array.prototype.slice()を使用して実現されます。このメソッドは、レシーバーの要素を新しい配列にコピーします。
slice()を直接呼び出すと、NodeListを配列に変換できます。
var domLinks = document.querySelectorAll('a[href]');
var links = Array.prototype.slice.call(domLinks);
links.map(function (link) {
    return link.href;
});
また、argumentsを配列に変換できます。
function format(pattern) {
    // params start at arguments[1], skipping `pattern`
    var params = Array.prototype.slice.call(arguments, 1);
    return params;
}
console.log(format('a', 'b', 'c')); // ['b', 'c']
一方、ECMAScript 6には、配列のようなオブジェクトを配列に変換するためのより簡単な方法であるArray.from()があります。
const domLinks = document.querySelectorAll('a[href]');
const links = Array.from(domLinks);
links.map(link => link.href);
一方、ECMAScript 6には*レストパラメータ*(3つのドットで宣言)があるため、配列のようなargumentsは必要ありません。
function format(pattern, ...params) {
    return params;
}
console.log(format('a', 'b', 'c')); // ['b', 'c']
hasOwnProperty()を安全に使用する obj.hasOwnProperty('prop')は、objに*独自の*(継承されていない)プロパティpropがあるかどうかを通知します。
> var obj = { prop: 123 };
> obj.hasOwnProperty('prop')
true
> 'toString' in obj // inherited
true
> obj.hasOwnProperty('toString') // own
false
ただし、Object.prototype.hasOwnPropertyがオーバーライドされると、ディスパッチを介してhasOwnPropertyを呼び出すと正常に動作しなくなる可能性があります。
> var obj1 = { hasOwnProperty: 123 };
> obj1.hasOwnProperty('toString')
TypeError: Property 'hasOwnProperty' is not a function
Object.prototypeがオブジェクトのプロトタイプチェーンに含まれていない場合、ディスパッチを介してhasOwnPropertyを使用できない場合もあります。
どちらの場合も、解決策はhasOwnPropertyを直接呼び出すことです。
> var obj1 = { hasOwnProperty: 123 };
> Object.prototype.hasOwnProperty.call(obj1, 'hasOwnProperty')
true
> var obj2 = Object.create(null);
> Object.prototype.hasOwnProperty.call(obj2, 'toString')
false
hasOwnProperty()の必要性が少ない hasOwnProperty()は、主にオブジェクトを介してマップを実装するために使用されます。ありがたいことに、ECMAScript 6には組み込みのMapデータ構造があるため、hasOwnProperty()の必要性が少なくなります。
Object.prototypeとArray.prototypeの省略形 空のオブジェクトリテラル(プロトタイプがObject.prototypeである)を介して、Object.prototypeのメソッドにアクセスできます。たとえば、次の2つの直接メソッド呼び出しは同等です。
Object.prototype.hasOwnProperty.call(obj, 'propKey')
{}.hasOwnProperty.call(obj, 'propKey')
同じトリックは、Array.prototypeにも適用できます。
Array.prototype.slice.call(arguments)
[].slice.call(arguments)
このパターンは非常に人気があります。長いバージョンほど明確に作成者の意図を反映していませんが、はるかに冗長ではありません。速度に関しては、2つのバージョン間に大きな違いはありません。
nameプロパティ 関数のnameプロパティには、関数の名前が含まれています。
このプロパティは、デバッグ(その値はスタックトレースに表示されます)および一部のメタプログラミングタスク(名前で関数を選択するなど)に役立ちます。
ECMAScript 6より前は、このプロパティはほとんどのエンジンですでにサポートされていました。ES6では、言語標準の一部になり、自動的に頻繁に設定されるようになります。
次のセクションでは、さまざまなプログラミング構造でnameが自動的に設定される方法について説明します。
関数は、変数宣言を介して作成された場合、名前を取得します。
let func1 = function () {};
console.log(func1.name); // func1
const func2 = function () {};
console.log(func2.name); // func2
var func3 = function () {};
console.log(func3.name); // func3
しかし、通常の代入でも、nameは適切に設定されます。
let func4;
func4 = function () {};
console.log(func4.name); // func4
var func5;
func5 = function () {};
console.log(func5.name); // func5
名前に関して、アロー関数は匿名関数式のようなものです。
const func = () => {};
console.log(func.name); // func
今後は、匿名関数式が表示される場合はいつでも、アロー関数が同じように機能すると想定できます。
関数がデフォルト値の場合、変数またはパラメータから名前を取得します。
let [func1 = function () {}] = [];
console.log(func1.name); // func1
let { f2: func2 = function () {} } = {};
console.log(func2.name); // func2
function g(func3 = function () {}) {
    return func3.name;
}
console.log(g()); // func3
関数宣言と関数式は*関数定義*です。このシナリオは長い間サポートされています。名前付きの関数定義は、名前をnameプロパティに渡します。
たとえば、関数宣言。
function foo() {}
console.log(foo.name); // foo
名前付き関数式の名前もnameプロパティを設定します。
const bar = function baz() {};
console.log(bar.name); // baz
最初に来るため、関数式の名前bazは他の名前(たとえば、変数宣言を介して提供される名前bar)よりも優先されます。
ただし、ES5と同様に、関数式の名前は関数式内の変数にすぎません。
const bar = function baz() {
    console.log(baz.name); // baz
};
bar();
console.log(baz); // ReferenceError
関数がプロパティの値である場合、そのプロパティから名前を取得します。それがメソッド定義(A行)、従来のプロパティ定義(B行)、計算されたプロパティキーを持つプロパティ定義(C行)、またはプロパティ値の省略形(D行)のいずれで行われるかは関係ありません。
function func() {}
let obj = {
    m1() {}, // (A)
    m2: function () {}, // (B)
    ['m' + '3']: function () {}, // (C)
    func, // (D)
};
console.log(obj.m1.name); // m1
console.log(obj.m2.name); // m2
console.log(obj.m3.name); // m3
console.log(obj.func.name); // func
ゲッターの名前には'get'が接頭辞として付けられ、セッターの名前には'set'が接頭辞として付けられます。
let obj = {
    get foo() {},
    set bar(value) {},
};
let getter = Object.getOwnPropertyDescriptor(obj, 'foo').get;
console.log(getter.name); // 'get foo'
let setter = Object.getOwnPropertyDescriptor(obj, 'bar').set;
console.log(setter.name); // 'set bar'
クラス定義でのメソッドの名前付けは、オブジェクトリテラルに似ています。
class C {
    m1() {}
    ['m' + '2']() {} // computed property key
    static classMethod() {}
}
console.log(C.prototype.m1.name); // m1
console.log(new C().m1.name); // m1
console.log(C.prototype.m2.name); // m2
console.log(C.classMethod.name); // classMethod
ゲッターとセッターには、それぞれ'get'と'set'の名前の接頭辞が再び付けられます。
class C {
    get foo() {}
    set bar(value) {}
}
let getter = Object.getOwnPropertyDescriptor(C.prototype, 'foo').get;
console.log(getter.name); // 'get foo'
let setter = Object.getOwnPropertyDescriptor(C.prototype, 'bar').set;
console.log(setter.name); // 'set bar'
ES6では、メソッドのキーをシンボルにすることができます。このようなメソッドのnameプロパティは、依然として文字列です。
'')になります。const key1 = Symbol('description');
const key2 = Symbol();
let obj = {
    [key1]() {},
    [key2]() {},
};
console.log(obj[key1].name); // '[description]'
console.log(obj[key2].name); // ''
クラス定義は関数を作成することを忘れないでください。これらの関数も、プロパティnameが正しく設定されています。
class Foo {}
console.log(Foo.name); // Foo
const Bar = class {};
console.log(Bar.name); // Bar
次のすべてのステートメントで、nameが'default'に設定されます。
export default function () {}
export default (function () {});
export default class {}
export default (class {});
export default () => {};
new Function()は、nameが'anonymous'である関数を生成します。WebKitのバグは、Webでそれが必要な理由を説明しています。func.bind()は、nameが'bound '+func.nameである関数を生成します。function foo(x) {
      return x
  }
  const bound = foo.bind(undefined, 123);
  console.log(bound.name); // 'bound foo'
関数名は、常に作成時に割り当てられ、後で変更されることはありません。つまり、JavaScriptエンジンは前述のパターンを検出し、正しい名前で始まる関数を作成します。次のコードは、functionFactory()によって作成された関数の名前が、B行の宣言によってではなく、A行で割り当てられることを示しています。
function functionFactory() {
    return function () {}; // (A)
}
const foo = functionFactory(); // (B)
console.log(foo.name.length); // 0 (anonymous)
理論的には、各代入において、右辺が関数に評価されるかどうか、また、その関数がまだ名前を持っていないかどうかをチェックすることができます。しかし、それは著しいパフォーマンスの低下を招くでしょう。
関数名はミニフィケーションの対象となり、通常、ミニファイされたコードでは変更されます。やりたいことによっては、文字列(ミニファイされない)を介して関数名を管理する必要がある場合や、ミニファイアにどの名前をミニファイしないかを指示する必要がある場合があります。
これらはプロパティnameの属性です。
プロパティが書き込み可能でないということは、代入によってその値を変更できないことを意味します。
しかし、プロパティは構成可能であり、再定義することで変更できることを意味します。
> Object.defineProperty(func, 'name', {value: 'foo', configurable: true});
> func.name
'foo'
プロパティnameが既に存在する場合、記述子プロパティconfigurableを省略できます。これは、欠落している記述子プロパティが、対応する属性が変更されないことを意味するためです。
プロパティnameがまだ存在しない場合、記述子プロパティconfigurableは、nameが構成可能なままになることを保証します(デフォルトの属性値はすべてfalseまたはundefinedです)。
name SetFunctionName()は、プロパティnameを設定します。仕様でその名前を検索して、それがどこで発生するかを確認してください。'get'と'set')Function.prototype.bind()(プレフィックス'bound')nameを持たない無名関数式は、それらのランタイムセマンティクスを見ることで確認できます。SetFunctionName()を介して設定されます。この操作は、無名関数式に対しては呼び出されません。SetFunctionName()は呼び出されません)。newを介して呼び出されたかどうかを判断するにはどうすればよいですか? ES6には、クラスに関する章で説明されているサブクラス化のための新しいプロトコルがあります。そのプロトコルの一部は、コンストラクター呼び出しのチェーンの最初の要素を参照するメタプロパティnew.targetです(スーパーメソッド呼び出しのチェーンにおけるthisに似ています)。コンストラクター呼び出しがない場合はundefinedです。これを使用して、関数がnewを介して呼び出す必要があるか、またはそれを介して呼び出す必要がないことを強制できます。これは後者の例です。
function realFunction() {
    if (new.target !== undefined) {
        throw new Error('Can’t be invoked via `new`');
    }
    ···
}
ES5では、これは通常このようにチェックされていました。
function realFunction() {
    "use strict";
    if (this !== undefined) {
        throw new Error('Can’t be invoked via `new`');
    }
    ···
}