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 プロキシ転送を実装する方法

推薦する

MySQL複合クエリの詳細な説明

UNIONの使用ほとんどの SQL クエリは、1 つ以上のテーブルからデータを返す単一の SELEC...

MySQLインデックスの作成について知っておくべきこと

目次序文: 1. インデックスメソッドを作成する2. インデックスを作成するために必要な権限序文: ...

webpackの遅延読み込みとプリロードの詳細な説明

目次通常の読み込み遅延読み込みプリロードプリロードを使用しないプリロードの使用要約する通常の読み込み...

読み込み進捗バーのネイティブ JS 実装

この記事では、ネイティブ JS によって実装された動的読み込みプログレス バーの特殊効果を紹介します...

MySQLのストレージエンジンの詳細な説明

MySQL ストレージ エンジンの概要ストレージ エンジンとは何ですか? MySQL のデータは、さ...

Vue はグラフィック検証コードログインを実装します

この記事では、グラフィック認証コードログインを実装するためのVueの具体的なコードを参考までに紹介し...

MySQL 上級学習ノート (パート 3): MySQL 論理アーキテクチャの紹介、MySQL ストレージ エンジンの詳細な説明

MySQL 論理アーキテクチャの概要他のデータベースと比較すると、MySQL は、そのアーキテクチャ...

DIV、テーブル、XHTML のウェブサイト構築の違いの分析と説明

簡単に言えば、ウェブサイト構築とは、「この人はどんな外見をしているのか」と「この人はどんな内面を持っ...

Dockerfile テキストファイルの使用例の分析

Dockerfile は、イメージをビルドするために使用されるテキスト ファイルです。テキスト コン...

HTML のセルパディングとセルスペース属性を図で説明します

セル - 表の内容 セルの余白 (表の余白) (cellpadding) - セルの外側の距離を表し...

jsは、州、市、地区の3レベルのリンクの非選択ドロップダウンボックスバージョンを実現します。

インターネットで3レベルリンクを検索したところ、すべてオプションで書かれていました。突然、別の方法で...

CSS3 の transition、transform、translate の違いと機能の簡単な分析

変換して翻訳するTransform は、変換と変形を意味します。他の幅属性や高さ属性と同様に、CSS...

複数の .sql ファイルを MySQL に効率的にインポートする方法の詳細な説明

MySQL には、複数の .sql ファイル (SQL ステートメントを含む) をインポートする方法...

Reactはルーティングを使用してログインインターフェースにリダイレクトします

前回の記事では、webpack と react 環境を設定した後、ログイン インターフェースとその後...

Vue でコミュニケーションを実装する 8 つの方法

目次1. コンポーネント通信1. Props 親コンポーネント ---> 子コンポーネント通信...