JSの中のコール、appy、bindの方法は詳しく説明します.
26300 ワード
bindは対応関数を返します.後で起動しやすくなります.apply、callはすぐ呼び出します.
appy、call
javascriptでは、callとapplyはいずれも関数の実行時のコンテキストを変えるために存在し、換言すれば、関数体内部のthisの指向を変えるためである.JavaScriptの大きな特徴は、関数には「定義時文脈」と「実行時文脈」と「文脈は変えられます」という概念があります.
アプリとコールの違い
applyとcallの両方にとって、役割は全く同じです.ただパラメータを受け取る方式は違います.例えば、次のような関数の定義があります.
apply、callの例
配列間の追加
配列かどうかを検証する(前提はtoString()メソッドは書き換えられていない)
面接問題
log方法を定義して、consolie.log方法をプロキシすることができます.よくある解決方法は:
ビッド()の方法を議論する前にまず一つのテーマを見に行きます.
MDNの説明では、bind()方法は、バインディング関数と呼ばれる新しい関数を作成します.このバインディング関数を呼び出すと、バインディング関数が作成されたときにbind()方法の最初のパラメータをthisとして入力します.
具体的にどうやって使うかを直接見てみます.よくある単体モードでは、通常、_を使います.this、that、selfなどはthisを保存します.このように文脈を変えた後も引用し続けます.このように:
偏関数(Partial Functions)
Partial FunctionsはPartial Appplicationsとも呼ばれています.ここでは偏関数に関する定義を切り取ります.
Partal appication can be described as tang a function that accepts some number of argments、binding values to to more of those argments、and returning a new function that only accepts the remaning、ungum-born.
コンストラクタとして結合関数
バインディング関数は、newオペレータを使用してターゲット関数を構成する例にも適用される.結合関数を使用してインスタンスを作成する場合、thisは無視されますが、着信パラメータはまだ利用可能です.
bind()は特定のthis値を必要とする関数に近道を作ることもできる.
たとえば、クラスの配列オブジェクトを本物の配列に変換する場合、可能な例は以下の通りです.
上のいくつかの小節はビッド()の多くの使用シーンが見られますが、bind()関数はECMA-2262の第5版に入っています.すべてのブラウザで実行できないかもしれません.これは私たち自身がビッド関数を実現する必要があります.
まず、目的関数に作用領域を指定することにより、簡単にbind()の方法を実現することができる.
引き続き、Javascriptの関数はコンストラクションとしても使えます.結合した関数はこのような方式で呼び出されると、状況が微妙になります.プロトタイプチェーンの伝達に関わる必要があります.
ブラウザでbind()関数をサポートするためには、上記の関数を少し修正するだけでいいです.
apply、call、bind比較
では、apply、call、bindの3つを比べたら、何か違いがありますか?いつappy、callを使いますか?いつbindを使いますか?簡単な栗です.
つまり、文脈環境を変えたいなら、すぐに実行するのではなく、折り返し実行する場合に、bind()を使用するのが違いです.アプリ/callはすぐに関数を実行します.
もう少しまとめてみます appy、call、bindの3つは、関数を変更するためのthisオブジェクトの指向です. appy、call、bindの3つの最初のパラメータはいずれもthisが指すオブジェクト、すなわち指定したいコンテキストです. appy、call、bindの3つは、後続のパラメータを使用して参照することができます. bitは対応関数を返します.後で起動しやすくなります.apply、callはすぐ呼び出します. bind詳細参照アドレス:《MDN:Function.prototype.bind()》
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={color=0000 ff}を持っていたら、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オブジェクトでもいいです.コールはパラメータを順番に渡す必要があります.記憶を深めるために、次のような使い方を挙げます.apply、callの例
配列間の追加
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.child Nodesなどがありますが、NodeListオブジェクトに戻るのはすべて擬似配列です.Aray下のプッシュ、ポップなどの方法は適用できません.しかし、私たちはAray.prototype.slice.calを通じて、本物の配列のlength属性を持つオブジェクトに変換することができます.このように、domNodesはArayの下のすべての方法を適用することができます.面接問題
log方法を定義して、consolie.log方法をプロキシすることができます.よくある解決方法は:
function log(msg) {
console.log(msg);
}
log(1); //1
log(1,2); //1
上記の方法は最も基本的な需要を解決することができますが、パラメータの個数が不確定な場合、上記の方法は無効になります.この場合、appyまたはcallを使用することを考慮して、ここ
に注意してください.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);
};
ビッドビッド()の方法を議論する前にまず一つのテーマを見に行きます.
var altwrite = document.write;
altwrite("hello");
結果:Uncaught TypeError: Illegal invocation
altwrite()関数がthisの指向globalまたはwindowオブジェクトを変更したため、実行時に不正な呼び出しを促す異常が発生しました.正しい方式はbind()を使用する方法です.altwrite.bind(document)("hello")
もちろんコール方法も使えます.altwrite.call(document, "hello")
結合関数bind()
の最も簡単な使い方は、どのように呼び出しても同じthis値がある関数を作成することである.よくあるエラーは上記の例のように、方法を対象から取り出して呼び出し、かつ元のオブジェクトに対してthisを指してください.特殊な処理をしないと、元の相手を失うことが多いです.bind()を使ってこの問題を綺麗に解決できます.this.num = 9;
var mymodule = {
num: 81,
getNum: function() {
console.log(this.num);
}
};
mymodule.getNum(); // 81
var getNum = mymodule.getNum;
getNum(); // 9, ,"this"
var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81
bind()方法はappyやcallに似ています.関数の内側にあるthisの方向を変えることもできます.MDNの説明では、bind()方法は、バインディング関数と呼ばれる新しい関数を作成します.このバインディング関数を呼び出すと、バインディング関数が作成されたときにbind()方法の最初のパラメータをthisとして入力します.
具体的にどうやって使うかを直接見てみます.よくある単体モードでは、通常、_を使います.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キーワードは、入力された値(ここではbind()を呼び出すときに入ってくるパラメータ)に設定されます.したがって、ここに私たちは希望の文脈this(つまりfoo)を伝えて、bind()関数に入ります.そして、コールバック関数が実行されると、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に設定されます.偏関数(Partial Functions)
Partial FunctionsはPartial Appplicationsとも呼ばれています.ここでは偏関数に関する定義を切り取ります.
Partal appication can be described as tang a function that accepts some number of argments、binding values to to more of those argments、and returning a new function that only accepts the remaning、ungum-born.
bind()
の他の最も簡単な使い方は、関数が予め設定された初期パラメータを持つようにすることである.これらのパラメータ(もしあれば)がbind()
のパラメータとしてthis
の後に書かれている限り.バインディング関数が起動されると、これらのパラメータはターゲット関数のパラメータリストの開始位置に挿入され、バインディング関数に渡すパラメータはそれらの後に付いてきます.function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// 37
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
セットTimeoutと一緒に使用します.function Bloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// 1 declare
Bloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 100);
};
Bloomer.prototype.declare = function() {
console.log(' ' + this.petalCount + ' !');
};
var bloo = new Bloomer();
bloo.bloom(); // 5 !
注意:イベントハンドリング関数とset Interval方法についても、上記の方法が使えます.コンストラクタとして結合関数
バインディング関数は、newオペレータを使用してターゲット関数を構成する例にも適用される.結合関数を使用してインスタンスを作成する場合、thisは無視されますが、着信パラメータはまだ利用可能です.
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
console.log(this.x + ',' + this.y);
};
var p = new Point(1, 2);
p.toString(); // '1,2'
var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
// ,
// bind :
var YAxisPoint = Point.bind(null, 0/*x*/);
var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'
axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true
近道bind()は特定のthis値を必要とする関数に近道を作ることもできる.
たとえば、クラスの配列オブジェクトを本物の配列に変換する場合、可能な例は以下の通りです.
var slice = Array.prototype.slice;
// ...
slice.call(arguments);
bind()
を使用すれば、状況はより簡単になる.var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
// ...
slice(arguments);
実現する上のいくつかの小節はビッド()の多くの使用シーンが見られますが、bind()関数はECMA-2262の第5版に入っています.すべてのブラウザで実行できないかもしれません.これは私たち自身がビッド関数を実現する必要があります.
まず、目的関数に作用領域を指定することにより、簡単にbind()の方法を実現することができる.
Function.prototype.bind = function(context){
self = this; // this, bind
return function(){
return self.apply(context,arguments);
};
};
関数コリック化を考慮して、よりロバストなビッドを構築できます.Function.prototype.bind = function(context){
var args = Array.prototype.slice.call(arguments, 1),
self = this;
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply(context,finalArgs);
};
};
今回のbind()
方法は、オブジェクトをバインディングすることができ、バインディング時の参照をサポートする.引き続き、Javascriptの関数はコンストラクションとしても使えます.結合した関数はこのような方式で呼び出されると、状況が微妙になります.プロトタイプチェーンの伝達に関わる必要があります.
Function.prototype.bind = function(context){
var args = Array.prototype.slice(arguments, 1),
F = function(){},
self = this,
bound = function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply((this instanceof F ? this : context), finalArgs);
};
F.prototype = self.prototype;
bound.prototype = new F();
return bound;
};
これは「JavaScript Web Appliation」の本の中の対bind()の実現です.一つの中継構造関数Fを設定することによって、バインディング後の関数と呼び出しbind()の関数が同じプロトタイプチェーン上にあり、newオペレータでバインディング後の関数を呼び出して、戻る対象も常にinstance ofを使用することができます.ブラウザでbind()関数をサポートするためには、上記の関数を少し修正するだけでいいです.
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// this instanceof fBound === true , fBound new
return fToBind.apply(this instanceof fBound
? this
: oThis,
// (fBound) .bind
aArgs.concat(Array.prototype.slice.call(arguments)));
};
//
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
// fBound.prototype fNOP ,
// fBound new ,new this fBound, __proto__ fNOP
fBound.prototype = new fNOP();
return fBound;
};
}
面白い問題がありますが、もし連続して二回、或いは連続して三回なら出力の値は何ですか?このように: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回とも3を出力します.なぜなら、Javascriptでは、複数回bind()が無効になっているからです.より深いレベルの原因は、bind()の実装は、使用関数が内部で1つのcall/applyを包んだことに相当し、2番目のbind()は、もう1回の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()を使用するのが違いです.アプリ/callはすぐに関数を実行します.
もう少しまとめてみます