javascript筆記のアプリ、call、bindの使い方

11921 ワード

原文の住所:https://www.cnblogs.com/coco1s/p/4833199.html
 
appy、call 
javascriptでは、callとapplyはいずれも関数の実行時のコンテキストを変えるために存在し、換言すれば、関数体内部のthisの指向を変えるためである.
JavaScriptの大きな特徴は、関数には「定義時文脈」と「実行時文脈」と「文脈は変えられます」という概念があります.
まず栗をください.
function fruits() {}
 
fruits.prototype = {
    color: "red",
    say: function() {
        console.log("My color is " + this.color);
    }
}
 
var apple = new fruits;
apple.say();    //My color is red
しかし、もし私たちが恋人のbana=「{yellow}があったら.」 ,say方法を再定義したくないので、callまたはappyでappleのsay方法を使ってもいいです.
banana = {
    color: "yellow"
}
apple.say.call(banana);     //My color is yellow
apple.say.apply(banana);    //My color is yellow
したがって、callとappyは動的にthisを変えるために現れたものと見られますが、一つのobjectには方法がありません.しかし、他にはcallやappyを使って他の対象の方法で操作することができます.
 
アプリとコールの違い
applyとcallの両方にとって、役割は全く同じです.ただパラメータを受け取る方式は違います.例えば、次のような関数の定義があります.
var func = function(arg1, arg2) {
     
};
以下のように呼び出すことができます.
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
その中のthisはあなたが指定したい文脈です.彼はどのJavaScriptオブジェクトでもいいです.コールはパラメータを順番に渡す必要があります.  
JavaScriptでは、ある関数のパラメータ数が固定されていませんので、適用条件といえば、あなたのパラメータが明確に数を知っているときにコールを使います.
不確定な時はappyでパラメータpshを配列に送ります.パラメータの数が不確定な場合は,関数内部もargmentsという疑似配列によってすべてのパラメータを巡回することができる.
 
記憶を深めるために、次のような使い方を挙げます.
配列間の追加
var array1 = [12 , "foo" , {name "Joe"} , -2458]; 
var array2 = ["Doe" , 555 , 100]; 
Array.prototype.push.apply(array1, array2); 
/* array1     [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
行列の最大値と最小値を取得します.
var  numbers = [5, 458 , 120 , -215 ]; 
var maxInNumbers = Math.max.apply(Math, numbers),   //458
    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
number自体にはmax方法はありませんが、Mathがあります.私たちはcallまたはappyを使ってその方法を使うことができます.
配列かどうかを検証する(前提はtoString()メソッドは書き換えられていない)
functionisArray(obj){ 
    return Object.prototype.toString.call(obj) === '[object Array]' ;
}
クラス(疑似)配列は、配列方法を使用します.
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
Javascriptには擬似配列というオブジェクト構造があります.特別なのは argmentsオブジェクトは、呼び出しのようなものがあります. getElements ByTagName , Dcument.childNodes などのように、NodeListオブジェクトに戻るのは、擬似配列に属します.Aray下のプッシュ、ポップなどの方法は適用できません.
しかし、私たちはAray.prototype.slice.calを通じて、本物の配列のlength属性を持つオブジェクトに変換することができます.このように、domNodesはArayの下のすべての方法を適用することができます.
 
アプリの使い方を深く理解し、call
次は面接の問題を借りて、もっと深く理解してください.
log方法を定義して、consolie.log方法をプロキシすることができます.よくある解決方法は:
function log(msg) {
  console.log(msg);
}
log(1);    //1
log(1,2);    //1
上記の方法は最も基本的な需要を解決することができますが、パラメータの個数が不確定な場合、上記の方法は無効になります.この場合、appyまたはcallを使用して、ここにどれだけのパラメータが入ってくるかは不確定なので、appyを使うのが一番良い方法は以下の通りです.
function log(){
  console.log.apply(console, arguments);
};
log(1);    //1
log(1,2);    //1 2
次の要求は、各logsメッセージに「(app)」を追加する前に中断することです.例えば、
log("hello world");    //(app)hello world
どうやって優雅にすればいいですか?この時、argmentsパラメータは擬似配列であり、Aray.prototype.slite.calを通じて標準配列に変換し、配列方法unshiftを使用する必要があります.
function log(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('(app)');
 
  console.log.apply(console, args);
};
bind詳細
appyとcallを言い終わったら、またbindについて話してください.bind()方法はappyやcallに似ています.関数の内側にあるthisの方向を変えることもできます.
MDNの説明は、bind()方法は、バインディング関数と呼ばれる新しい関数を作成します.このバインディング関数を呼び出すと、バインディング関数はそれを作成するために導入されます. bind()メソッドの最初のパラメータを サース、着信 ビッド() 方法の2番目と以降のパラメータにバインディング関数を付けて動作する場合、自身のパラメータは順番に元の関数のパラメータとして呼び出されます.
具体的にどうやって使うかを直接見てみます.よくある単体モードでは、通常、_を使います.this、that、selfなどはthisを保存します.このように文脈を変えた後も引用し続けます. このように:
var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this;
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(_this.bar);     //1
        });
    }
}
Javascript特有の機構のため、コンテキスト環境はeventBind:function(){}に移行します. $('.someClass').on('click',function(event) }) 変更が発生しました.上記の変数でthisを保存します. これらの方法は有用です.問題もありません.もちろんbind()を使ってもっと優雅にこの問題を解決できます.
var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(this.bar);      //1
        }.bind(this));
    }
}
 
上記のコードの中で、bind()は、このclickイベントが呼び出されたときに、そのthisキーワードは着信された値(ここではbit(を呼び出すときに入ってくるパラメータ)に設定されます.したがって、ここには、所望のコンテキストthis(つまりfoo)が入ってきます.関数にあります.そして、コールバック関数が実行されると、thisが指します. foo 対象です.簡単な栗をもう一つお願いします.
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
bar(); // undefined
var func = bar.bind(foo);
func(); // 3
ここでは、新しい関数funcを作成しました.bind()を使ってバインディング関数を作成した後、実行されると、そのthisは私たちがbar()を呼び出す時のグローバルスコープではなくfooに設定されます.
面白い問題があります.もし連続して二回、或いは連続して三回で出力する値は何ですか?このように:
var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func(); //?
 
var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //?
答えは、期待の4と5ではなく、2回も出力します.なぜなら、Javascriptでは、複数回のbindは無効です.より深いレベルの原因は、bind()の実現は、関数を使って内部に1つのcall/applyを包んだことに相当します.2回目のbind()は、もう一回目のbind()に相当します.だから、2回目以降のbindは有効になりません.
  
apply、call、bind比較
では、apply、call、bindの3つを比べてみます.この間には何の違いがありますか?いつappy、callを使いますか?いつbindを使いますか?簡単な栗です.
var obj = {
    x: 81,
};
 
var foo = {
    getX: function() {
        return this.x;
    }
}
 
console.log(foo.getX.bind(obj)());  //81
console.log(foo.getX.call(obj));    //81
console.log(foo.getX.apply(obj));   //81
三つの出力は81ですが、bind()の使い方を注意してみてください.彼の後ろには括弧が多くなりました.
つまり、コンテキスト環境を変えたいなら、すぐに実行するのではなく、折り返し実行する場合は、bind()メソッドを使用しますが、appy/callはすぐに関数を実行します.
 
もう少しまとめてみます
apply、call、bindの3つは、関数を変更するためのthisオブジェクトの指向です.apply、call、bindの3つの最初のパラメータは、いずれもthisが指すオブジェクト、すなわち指定したいコンテキストである.
apply、call、bindの3つは、後続のパラメータを使用して参照することができます.ビッド は、対応する関数を返します.後で起動しやすくなります.appy、callはすぐ呼び出します.