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 をアップグレードする方法

推薦する

html 内の絶対パス URL と相対パス URL、サブディレクトリ、親ディレクトリ、ルート ディレクトリ

絶対 URL は、インターネット上の特定のファイルに必要なすべてのコンテンツを表すために使用されます...

VueプロジェクトにPWAを導入する手順

目次1. 依存関係をインストールする2. vue.config.js ファイルで pwa を設定しま...

Ubuntu 18.04 のログインループ/ブートインターフェイスで停止/グラフィカルインターフェイスに入ることができない問題を解決する方法

原因: NVIDIA グラフィック カード ドライバーが破損している解決:コマンドラインモードで再起...

nginx と Tencent Cloud の無料証明書を使用して https を作成する方法

httpsを取得する方法を勉強しています。最近、Tencent Cloud が提供する無料の SSL...

Webデザインチュートリアル(2):模倣と盗作について

<br />前回の記事では、Webデザインの手順と方法を紹介しました。詳細については、前...

Vue シングルページ SEO の 4 つのソリューションについての簡単な説明

目次1.Nuxtサーバーサイドレンダリングアプリケーションの展開(SSRサーバーレンダリング)利点:...

JavaScriptで計算機機能を実現するプロセスの詳細な説明

目次1. 電卓機能の紹介2. 計算機ページのデザイン1. ナビゲーションバー2. データ部分3. i...

Vueのフロントエンドとバックエンドのポートの不一致の問題を解決する

Vue のフロントエンドとバックエンドのポートが一致していませんconfig index.jsファイ...

Windows 7 の mysql6.x で中国語の文字化けが発生する問題に対する完璧な解決策

1. コマンドラインでMySQLサービスを停止します: net stop mysql stop my...

Windows (x86、64 ビット) で MySQL 5.7.17 無料インストール バージョンをアップグレードするための詳細なチュートリアル

Laravel 5.4 のデフォルトの utf8mb64 文字エンコーディングをサポートするには、M...

vuex ベースのショッピングカート機能の実装

この記事の例では、ショッピングカート機能を実装するためのvuexの具体的なコードを参考までに共有して...

Mac OS10.12 に mysql5.7.18 をインストールするチュートリアル

ウェブ全体を検索して、さまざまな落とし穴を見つけましたが、問題は解決しませんでした。ついに自分でも分...