JavaScript における call、apply、bind の実装原則の詳細な説明

JavaScript における call、apply、bind の実装原則の詳細な説明

序文

ご存知のとおり、call、apply、bind の機能はスコープを「変更」することですが、この「変更」についてはインターネット上では曖昧で、詳しい説明がありません。「変更」とはスコープを直接置き換えることを意味するのでしょうか?誰が誰の代わりになるのでしょうか?どのように効果を生み出すのでしょうか?これらの質問を明確に理解していないと、手書きの実装を見ても、おそらく長くは覚えられないでしょう。

したがって、この記事では、call、apply、bind の使用方法と、それぞれの実装原則について説明します。

電話

call() メソッドは、指定された this 値と、個別に指定された 1 つ以上の引数を使用して関数を呼び出します。

つまり、現在の関数の this ポインターを変更して、現在の関数を実行できます。

使用法

関数fun() {
  console.log(this.name, 引数)
}
obj = { name: 'clying' } とします。
fun.call(obj, 'deng', 'deng')
// clying [引数] { '0': 'deng', '1': 'deng' }

成し遂げる

call と apply の実装では、関数をリテラル obj のプロパティに配置して、関数内の this がリテラル オブジェクト obj を指すようにします。

シンプルな実装バージョン:

Function.prototype.mycall = 関数 (コンテキスト) {
  context = (context == null || context == undefined) ? window : 新しいオブジェクト(context)
  context.fn = これ
  コンテキスト.fn()
  コンテキストを削除する.fn
}

関数プロトタイプに mycall メソッドを追加し、コンテキスト オブジェクト context を作成します。渡されたオブジェクトが存在しない場合は、グローバル ウィンドウを指します。 context に fn 属性を追加することで、context の fn はメソッドを呼び出して fun を実行する関数 fun を参照します。実行が完了したら属性 fn を削除します。

最初に渡されたパラメータを取得する必要があり、その後、文字列配列になります。

実行メソッドでは、文字列を計算し、その中のコードを実行して計算結果を返す eval 関数を使用します。

アップグレード版:

呼び出しにパラメータを渡します。

Function.prototype.mycall = 関数 (コンテキスト) {
  context = (context == null || context == undefined) ? window : 新しいオブジェクト(context)
  context.fn = これ
  arr = [] とします
  (i = 1 とします; i < 引数の長さ; i++) {
    arr.push('argument[' + i + ']') // ["arguments[1]", "arguments[2]"]
  }
  let r = eval('context.fn(' + arr + ')') // 関数 fun を実行し、パラメータを渡す delete context.fn
  rを返す
}

さらに、 call は分解構文を通じて実装することもできます。

Function.prototype.mycall = function (コンテキスト、...引数) {
  context = (context == null || context == undefined) ? window : 新しいオブジェクト(context)
  context.fn = これ
  context.fn(...引数)
  コンテキストを削除する.fn
}

call メソッドを複数回呼び出せるようにしたい場合は、context.fn(...args) を変数に保存し、最後にそれを返すことができます。

Function.prototype.mycall = function (コンテキスト、...引数) {
  context = (context == null || context == undefined) ? window : 新しいオブジェクト(context)
  context.fn = これ
  r = context.fn(...args) とします。
  コンテキストを削除する.fn
  rを返す
}

適用する

call メソッドと同様に、call メソッドはパラメータ リストを受け取りますが、apply メソッドは複数のパラメータを含む配列を受け取ります。

使用法

関数内でこれを設定して、渡される最初のパラメータを指すようにします。2 番目のパラメータは配列です。

関数fun() {
  console.log(this.name, 引数);
}
obj = {
  名前: 'clying'
}
関数を適用する(obj, [22, 1])
// 引数(2) [22, 1]

成し遂げる

自分で適用メソッド myapply を実装します。実装方法は call と似ていますが、パラメータを受け取るときに、渡される 2 番目のパラメータとして args を使用できます。 2 番目のパラメータが渡されないかどうかを直接判断し、関数を直接実行します。それ以外の場合は、eval を使用して関数を実行します。

Function.prototype.myapply = 関数 (コンテキスト、引数) {
 context = (context == null || context == undefined) ? window : 新しいオブジェクト(context)
  context.fn = これ
  if(!args) は context.fn() を返します
  r = eval('context.fn('+args+')') とします。
  コンテキストを削除する.fn
  rを返す
}

バインド

bind() メソッドは新しい関数を作成し、自動的には実行されません。bind() を手動で呼び出す必要があります。新しい関数の this は bind() の最初のパラメータとして割り当てられ、残りのパラメータは新しい関数が呼び出されたときにそのパラメータとして使用されます。

使用法

obj を fun 関数の this にバインドします。fun 関数は、obj 内のプロパティと渡された変数を使用できます。

関数fun() {
  console.log(this.name, 引数);
}
obj = {
  名前: 'clying'
}
b = fun.bind(obj,2) とします。
b(3)
// 引数(2) [2, 3]

また、bind メソッドでバインドされた関数も新しいインスタンスを作成できますが、これはこの時点で変更されます。

アップグレード版 - プロトタイププロパティの使用法:

関数fun() {
  console.log(this.name, 引数);
}
obj = {
  名前: 'clying'
}
楽しいプロトタイプ年齢 = 23
b = fun.bind(obj, 3) とします。
インスタンス = new b(4) とする
コンソールにログ出力します。
//未定義の引数(2) [3, 4]
// 23

成し遂げる

基本バージョン:

bind の実装は、call と apply に基づいて行うことができます。

bind はすぐに実行されないため、ユーザーが手動で実行できるようにする関数を返すことができます。返された関数では、call または apply を使用して、指定された this オブジェクトとパラメータを渡します。

実装のバインドを適用する

Function.prototype.mybind = 関数 (コンテキスト) {
  それを = これとする
  bindargs = Array.prototype.slice.call(引数, 1) とします。
  関数を返す(){
    args = Array.prototype.slice.call(引数) とします。
    that.apply(context, bindargs.concat(args)) を返します
  }
}

apply メソッドは主に、bind によって渡されたパラメータと、関数の実行時にユーザーによって渡されたパラメータを取得して処理するために使用されます。必要なパラメータをインターセプトするには、Array プロトタイプ メソッドの slice メソッドを使用します。

bind によって渡されたパラメータを取得するときは、2 番目のパラメータからインターセプトを開始する必要があるため、開始位置は 1 になります。

呼び出しはbindを実装する

Function.prototype.mybind = function (コンテキスト、...args1) {
  それを = これとする
  関数を返す (...args2) {
    that.call(context, ...args1, ...args2) を返します。
  }
}

call を実装するには、パラメータを call メソッドの末尾に連結するだけです。

アップグレード版:

bind では、これのポインターを変更するだけでなく、bind 後にユーザーがパラメーターを渡すことも、コマンドの実行時にユーザーがパラメーターを渡すこともできます。実行関数に新しい操作を実行させることもできます。

バインドされた関数を使用して値を構築する場合、元々提供されていた this は無視されます。ただし、コンストラクターが呼び出されると、提供されたパラメーター リストはパラメーター リストの前に挿入されます。

適用する

Function.prototype.mybind = 関数 (コンテキスト) {
  それを = これとする
  bindargs = Array.prototype.slice.call(引数, 1) とします。
  関数fBind() {
    args = Array.prototype.slice.call(引数) とします。
    // new が使用される場合、これは fBind インスタンスを指します。これが現在のインスタンスでない場合は、コンテキスト オブジェクトを使用します。 return that.apply(this instanceof fBind ? this : context, bindargs.concat(args))
  }
  fBindを返す
}

new 演算子を使用する場合は、this のポインターを変更する必要があることに注意してください。new の場合、this はインスタンスを指します。new が使用されていない場合は、bind によって現在渡されている最初のパラメーターを指します。

さらに、元の関数が独自のメソッド属性を追加できることも含まれます。 fun 独自のプロトタイプ メソッドを使用できるようにするには、プロトタイプの共有を実現するために fBind.prototype = this.prototype も使用する必要があります。ただし、参照型のプロパティ値の共有の場合、他のインスタンスを変更せずに変更することはできません (プロトタイプ メソッドまたはプロパティを変更すると、すべての参照が変更されます)。

Function.prototype.mybind = 関数 (コンテキスト) {
  それを = これとする
  args = Array.prototype.slice.call(arguments, 1) とします。
  function fBind() { // バインド関数を実行する let bindargs = Array.prototype.slice.call(arguments)
    that.apply(this instanceof fBind ? this : context, args.concat(bindargs)) を返します。
  }
  function Fn(){} // 2つのクラスのプロトタイプは共有されませんが、プロトタイプメソッドはプロトタイプチェーンを通じて見つかります Fn.prototype = this.prototype
  fBind.prototype = 新しいFn()
  fBindを返す
}

上記の状況では、関数ミドルウェアを使用してプロトタイプ チェーンを使用し、元の関数プロトタイプ メソッドまたはプロパティを見つけることができます。

電話

call と apply の違いは処理パラメータの違いのみであり、その他はすべて同様です。

Function.prototype.mybind = function (コンテキスト、...args1) {
  それを = これとする
  関数 fBind(...args2) {
    that.call(this instanceof fBind ? this : context, ...args1, ...args2) を返します。
  }
  関数Fn() { }
  Fn.prototype = this.prototype
  fBind.prototype = 新しいFn()
  fBindを返す
}

要約する

これで、JavaScript の call、apply、bind の実装原則に関するこの記事は終了です。call、apply、bind の原則の詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JS を使用して、apply、call、bind メソッドを簡単に実装するためのサンプル コード
  • ネイティブ js で呼び出し、適用、バインドを実装する方法
  • 50 行の JavaScript コードを使用して、呼び出し、適用、バインドのシンプルなバージョンを実装する方法
  • Javascript で call、bind、apply を実装するためのコードの詳細な説明

<<:  MySQL バッチ挿入とユニークインデックスの問題に対する解決策

>>:  Nginx プロキシ転送構成を通じてクロスドメイン API プロキシ転送を実装する方法

推薦する

CentOS 7 で MySQL 8 の複数のインスタンスを設定する詳細なチュートリアル (必要な数だけ設定できます)

原因最近、プロジェクトのリファクタリングを始めたのですが、マスタースレーブと読み取り書き込み分離を使...

ネイティブJavaScriptでカルーセルを実装する

この記事では、JavaScriptでカルーセルを実装するための具体的なコードを参考までに紹介します。...

Tencent Cloud Server Tomcat ポートにアクセスできない場合の解決策

最近、Tencent Cloudを使用してサーバーを設定しました。使用中に、tomcatポートにアク...

Docker を使用して OpenLDAP+phpLDAPadmin 統合ユーザー認証を構築する方法

1. 背景LDAP を使用して、操作および保守に関連するユーザー名とパスワードを集中管理します。 1...

MySQL IFNULL判定問題の解決方法

問題: mybatis によって返される null 型のデータが消え、フロントエンドの表示にエラーが...

Nexus サーバーを設定するための詳細な手順

1. ネクサスサービス構築の意義イントラネットの統合プロキシとして、チームで共同開発する場合、全員が...

Vue3はサイドナビゲーションテキストスケルトン効果コンポーネントをカプセル化します

Vue3プロジェクトのカプセル化サイドナビゲーションテキストスケルトン効果コンポーネント-グローバル...

VMware Workstation 15 Pro に Ubuntu 1804 をインストールするチュートリアル (画像とテキスト付き)

このメモはインストール チュートリアルです。実用的な意味はありません。記録のためだけに書いています。...

カタツムリ映画システムのDocker展開の詳細なプロセス分析

環境に関する声明ホストOS: Cetnos7.9 最小インストールdocker バージョン: 20....

CSSはグラデーションを巧みに利用して高度な背景光アニメーションを実現します

成し遂げるこの効果は CSS を使用して完全に再現することは困難です。 CSS でシミュレートされた...

Nginx レイヤー 4 負荷分散構成ガイド

1. レイヤー4負荷分散の概要レイヤー 4 ロード バランシングとは何ですか?いわゆる 4 層負荷分...

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

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

HTML フレームセットのサンプルコード

この記事では、Frameset が作成した、できるだけシンプルなフレームワークを紹介します。さて、ま...

Linux システムでの CPU 使用率が高い場合のトラブルシューティングのアイデアと解決策

序文Linux 運用保守エンジニアとして、日々の業務の中で Linux サーバーの CPU 負荷が ...

Reactはページング効果を実装する

この記事では、Reactでページング効果を実現するための具体的なコードを参考までに紹介します。具体的...