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

推薦する

react-beautiful-dnd を使用してリスト間のドラッグ アンド ドロップを実装する

目次react-beautiful-dndを選ぶ理由基本的な使い方基本概念使い方使用中に発生した問題...

Docker Swarm サービス オーケストレーション コマンドの詳細な説明

1. はじめにDocker には、タスクを構成する複数の Docker コンテナをオーケストレーショ...

Win10 構成 Tomcat 環境変数チュートリアル図

設定する前に、次の操作を行う必要があります。 1. まずjdk bloggerをインストールします。...

JavaScript でグレイウルフのポットビーティングゲームを実装

1. プロジェクト文書 2. ページレイアウトにHTMLとCSSを使用するHTML部分 <di...

CentOS 7.x dockerはoverlay2ストレージ方式を使用する

/etc/docker/daemon.json を編集し、以下を追加します。 { "ストレ...

Windows で MySQL インストーラーを使用して MySQL サービスをインストールするチュートリアル図

MYSQL は、MYSQL サービスやその他のコンポーネントをインストールするためのインストーラ方式...

ウェブサイトのアクセス速度を向上させるための徹底的な最適化に関するヒント

<br />ウェブサイトのアクセス速度はウェブサイトのトラフィックに直接影響を及ぼし、ウ...

MySQL json 形式のデータクエリ操作

デフォルトのテーブル名はbase_dataで、json列名はjson_valueです。 json_v...

JavaScriptの詳細な分析と方向の変更方法

目次これ方法オブジェクト内これを隠した厳密モードこれを変更してこれいつものように、まずはコードを見て...

VMware Workstation Pro でサーバー仮想マシンを構築する (グラフィック チュートリアル)

私が使用している VMware Workstation Pro のバージョンは次のとおりです。 1....

uniapp パッケージ化されたアプレット レーダー チャート コンポーネントの完全なコード

効果画像:実装コードは以下のとおりですビュー <canvas id="radar-c...

Linux デスクトップ用に Openbox を設定する方法 (推奨)

この記事は、「24 Days of Linux Desktop」の特別シリーズの一部です。 Open...

CentOS 7.6 Telnetサービス構築プロセス(Opensshアップグレードバトル第一弾のバックアップトランスポートライン構築)

不明な点があるときはいつでも、Blog Park にアクセスして、いつでも答えやインスピレーションを...

MySQLにログインする際のエラー「ERROR 1045 (28000)」を解決する方法

今日はサーバーにログインして、データベース内のいくつかのものを変更する準備をしました。しかし、パスワ...

タイプライター効果を実現する純粋な js

この記事の例では、タイプライター効果を実現するためのjsの具体的なコードを参考までに共有しています。...