ES5とES6の違いを分析する

ES5とES6の違いを分析する

概要

ご存知のとおり、ES6 では、グローバルで組み込みの非構築可能な Reflect オブジェクトが追加され、一連のインターセプト可能な操作メソッドが提供されます。その 1 つが Reflect.apply() です。従来の ES5 Function.prototype.apply() との類似点と相違点を調べてみましょう。

関数シグネチャ

MDN 上の 2 つの関数シグネチャは次のとおりです。

Reflect.apply(ターゲット、thisArgument、argumentsList)
function.apply(thisArg, [argsArray])

TypeScript で定義されている関数シグネチャは次のとおりです。

名前空間Reflectを宣言する{
    関数を適用します(ターゲット: Function、thisArgument: any、argumentsList: ArrayLike<any>): any;
}
インターフェース関数{
    適用(this: Function、thisArg: any、argArray?: any): any;
}

これらはすべて、呼び出された関数に提供される this パラメータとパラメータの配列 (または配列のようなオブジェクト) を受け入れます。

オプションパラメータ

最も直感的にわかるのは、function.apply() によって関数に渡される 2 番目のパラメータ「パラメータ配列」がオプションであることです。呼び出された関数にパラメータを渡す必要がない場合は、パラメータを渡さなかったり、null 値や未定義値を渡したりすることはできません。 function.apply() には 2 つのパラメータしかないため、実際には最初のパラメータも省略でき、原理的には実装で未定義の値を取得できます。

(関数() { console.log('test1') }).apply()
//テスト1
(関数 () { console.log('test2') }).apply(未定義、[])
//テスト2
(関数 () { console.log('test3') }).apply(未定義、{})
//テスト3
(関数 (テキスト) { console.log(テキスト) }).apply(未定義、['test4'])
//テスト4

Reflect.apply() では、すべてのパラメータを渡す必要があります。呼び出された関数にパラメータを渡したくない場合は、空の配列または空の配列のようなオブジェクトを入力する必要があります (純粋な JavaScript では空のオブジェクトも許容されますが、TypeScript の場合は、型チェックに合格するために長さが 0 のキーと値のペアを使用する必要があります)。

Reflect.apply(関数() { console.log('test1') }, 未定義)
// スロー:
// TypeError: CreateListFromArrayLike が非オブジェクトに対して呼び出されました
Reflect.apply(関数() { console.log('test2') }, 未定義、[])
//テスト2
Reflect.apply(関数() { console.log('test3') }, 未定義, {})
//テスト3
Reflect.apply(関数(テキスト) { console.log(テキスト) }, 未定義、['test4'])
//テスト4

非厳密モード

ドキュメントによると、function.apply() の thisArg パラメータは非厳密モードでは異なります。その値が null または undefined の場合、グローバル オブジェクト (ブラウザーのウィンドウ) に自動的に置き換えられ、基本データ型の値が自動的にラップされます (たとえば、リテラル 1 のラップされた値は Number(1) に相当します)。

(関数() { console.log(this) }).apply(null)
// ウィンドウ {...}
(関数() { console.log(this) }).apply(1)
// 数値 { [[PrimitiveValue]]: 1 }
(関数() { console.log(this) }).apply(true)
// ブール値 { [[PrimitiveValue]]: true }
'厳密な使用';
(関数() { console.log(this) }).apply(null)
// ヌル
(関数() { console.log(this) }).apply(1)
// 1
(関数() { console.log(this) }).apply(true)
// 真実

しかし、テストの結果、非厳密モードでの上記の動作は Reflect.apply() でも有効であることがわかりましたが、MDN ドキュメントにはこれについて記載されていません。

例外処理

Reflect.apply は Function.prototype.apply のカプセル化とみなすことができ、一部の例外判定は同じです。渡されたターゲット関数ターゲットが実際には呼び出し可能ではない、関数ではないなどの場合は、例外がトリガーされます。しかし、異常な症状は異なる場合があります。

ターゲット パラメータに関数ではなくオブジェクトを渡すと、例外が発生します。

Function.prototype.apply() によってスローされる例外のセマンティクスは不明瞭です。文字通りの翻訳は、.call は関数ではないということです。ただし、正しい呼び出し可能な関数オブジェクトを渡すと、エラーは報告されません。これにより、Function.prototype.apply の下に call 属性があるかどうかについて混乱が生じます。

関数プロトタイプを適用します。呼び出し()
// スロー:
// TypeError: Function.prototype.apply.call は関数ではありません
Function.prototype.apply.call(コンソール)
// スロー:
// TypeError: Function.prototype.apply.call は関数ではありません
関数プロトタイプを適用して呼び出します(console.log)
///- 予想通り、出力は空です

Function.prototype.apply() によってスローされる例外はあいまいです。また、ターゲット パラメータに呼び出し不可能なオブジェクトが渡されます。2 番目と 3 番目のパラメータが入力されている場合、スローされる例外の説明は上記とはまったく異なります。

ただし、呼び出し不可能なオブジェクトのみを渡す Reflect.apply() の例外は、すべてのパラメータを指定した Function.prototype.apply() の例外と同じです。

Reflect.apply(コンソール)
// スロー:
// TypeError: Function.prototype.apply が #<Object> に対して呼び出されましたが、これはオブジェクトであり関数ではありません

正しい呼び出し可能な関数が渡された場合、3 番目のパラメータ配列のパラメータがチェックされます。これは、Reflect.apply() のパラメータ チェックが順次行われることも示しています。

反映.適用(コンソール.ログ)
// スロー:
// TypeError: CreateListFromArrayLike が非オブジェクトに対して呼び出されました

実用

プロキシ以外での使用例はまだ多くありませんが、互換性の問題が徐々に軽減されれば、使用率は徐々に増加すると考えています。

ES6Reflect.apply() の形式は、従来の ES5 の使用法よりも直感的で読みやすく、コード行でどの関数を使用するかがわかりやすくなり、期待される動作を実行できるようになります。

// ES5
Function.prototype.apply.call(<Function>, undefined, [...])
<関数>.apply(undefined, [...])
// ES6
Reflect.apply(<関数>, undefined, [...])

比較のために、よく使われる Object.prototype.toString を選択してみましょう。

Object.prototype.toString.apply(/ /)
// '[オブジェクト正規表現]'
Reflect.apply(Object.prototype.toString, //, [])
// '[オブジェクト正規表現]'

反対する人もいるかもしれませんが、これは書くのが長くて面倒ではないでしょうか?この点については意見が分かれています。単一の関数を繰り返し呼び出すと、より多くのコードが必要になります。柔軟な使用が必要なシナリオでは、関数型スタイルの方が適しています。関数オブジェクトを指定してパラメータを渡すだけで、期待どおりの結果が得られます。

しかし、この場合、小さな問題が発生する可能性があります。呼び出しごとに新しい空の配列を作成する必要があるのです。現在、ほとんどのデバイスのパフォーマンスは十分に優れているため、プログラマーはこの損失を考慮する必要はありませんが、高パフォーマンスで最適化されていないエンジンのシナリオでは、最初に再利用可能な空の配列を作成する方がよい場合があります。

定数空の引数 = []

関数 getType(obj) {
    Reflect.apply() を返す
        オブジェクト.prototype.toString、
        オブジェクト、
        空の引数
    )
}

String.fromCharCode() が呼び出される別のシナリオでは、コード内の文字列を混乱させる可能性があります。

反映.適用(
    文字列.fromCharCode、
    未定義、
    [104、101、108、108、
     111、32、119、111、
     [114、108、100、33]
)
// 'こんにちは世界!'

これは、Math.max() などの複数のパラメータを渡すことができる関数の場合に、より便利な場合があります。

定数arr = [1, 1, 2, 3, 5, 8]
Reflect.apply(Math.max, 未定義, arr) を適用します。
// 8
Function.prototype.apply.call(Math.max, 未定義, arr) 関数の呼び出し
// 8
Math.max.apply(未定義、arr)
// 8

ただし、言語標準ではパラメータの最大数は指定されていないため、大きすぎる配列が渡されると、スタック サイズが制限を超えたというエラーが報告される可能性があります。このサイズはプラットフォームやエンジンによって異なります。たとえば、PC 上の node.js は非常に大きなサイズに達する可能性がありますが、携帯電話上の jsC は 65536 に制限される場合があります。

const arr = 新しい配列(Math.floor(2**18)).fill(0)
// [
// 0, 0, 0, 0,
// ... 262140 件以上
// ]
Reflect.apply(Math.max, null, arr)
// スロー:
// RangeError: 最大コールスタックサイズを超えました

要約する

新しい ES6 標準で提供される Reflect.apply() は、より規則的で使いやすくなっています。次の機能があります。

1. 直感的で読みやすく、呼び出される関数はパラメータ内に配置され、関数型スタイルに近いです。

2. 例外処理は一貫しており、明確である。

3. すべてのパラメータを渡す必要があり、コンパイル時のエラーチェックと型推論がより使いやすくなります。

現在、vue.js 3 もレスポンシブ システムで Proxy と Reflect を多用しています。近い将来、フロントエンドの世界で Reflect が活躍することを期待しています。

上記は、ES5とES6のapplyの違いの詳細な分析です。ES5とES6の違いの詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • ネイティブ js で呼び出し、適用、バインドを実装する方法
  • JavaScript の call、apply、bind の違いの詳細な説明
  • Javascript 呼び出しと適用のアプリケーション シナリオと例
  • JS call() および apply() メソッドの使用例のまとめ
  • JS 適用の使用状況の概要と使用シナリオ例の分析
  • JavaScript における this/call/apply/bind の使い方と違い
  • JavaScript 関数の呼び出し、原則例の分析の適用
  • JavaScript での call、apply、callee、caller の使用例の分析
  • JS の call() と apply() の機能と使用例の分析
  • JS での apply() の適用例の分析

<<:  Mysql5.7.17 winx64.zip 解凍バージョンのインストールと設定のグラフィックチュートリアル

>>:  Nginx で https をアップグレードする方法

推薦する

Windows に異なる (2 つの) バージョンの MySQL データベースをインストールする詳細なチュートリアル

1. 原因: SQL ファイルをインポートする必要があるのですが、インポートできません。この文を実行...

Docker 接続 MongoDB 実装プロセスとコード例

コンテナが起動した後まず管理者にログインして新しいユーザーを作成してください $ docker ex...

MySQLのカバーインデックスに関する知識ポイントのまとめ

インデックスにクエリする必要があるすべてのフィールドの値が含まれている(またはカバーしている)場合、...

CentOS 8.1 で LEMP (Linux+Nginx+MySQL+PHP) 環境を構築する (チュートリアルの詳細)

目次ステップ1: CentOS 8でパッケージを更新するステップ2: CentOS 8にNginx ...

uni-appがNFC読み取り機能を実装

この記事では、参考までに、NFC読み取り機能を実装するためのuni-appの具体的なコードを紹介しま...

VMware Workstation 14 Pro は CentOS 7.0 をインストールします

VMware Workstation 14 ProにCentOS 7.0をインストールする具体的な方...

Vue のプロダクション環境と開発環境を切り替えてフィルターを使用する方法

目次1. 本番環境と開発環境を切り替える最初の方法: .envファイルを設定する2番目の方法2. フ...

左右の幅を固定し、中央の幅を適応させたHTMLレイアウトのソリューションの詳細な説明

この記事では、次のように、誰にでも共有できる左右幅固定のミドルアダプティブ HTML レイアウトソリ...

Docker Compose を使用して ELK を迅速にデプロイする (テスト済みで効果的)

目次1. 概要1.1 定義1.2 機能説明2. ELKを展開する2.1 ディレクトリとファイルを作成...

ubuntu15.10 での hadoop2.7.2 の詳細なインストールと設定

Linux での Hadoop インストール チュートリアルはインターネットや書籍に多数ありますが、...

Windows での MySQL 5.7.18 のインストールと設定のチュートリアル

この記事では、WindowsでのMySQL 5.7.18のインストールと設定のチュートリアルを参考ま...

CSS3で実装された天気アイコンのアニメーション効果

成果を達成する 実装コードhtml <div class="wrapper"...

JavaScript でカウントダウン効果を実装する

カウントダウン効果を実現するにはJavascriptを使用します。参考までに、具体的な内容は次のとお...

mysql はインデックスを無効にしますか?

mysql の IN はインデックスを無効にしますか?しませんよ! 結果をご覧ください: mysq...

IDEA の Maven プロジェクトで MySQL 8.0 に接続して使用する方法に関するチュートリアル

まず、私の基本的な開発環境を見てみましょう。オペレーティングシステム: MacOS 10.13.5 ...