call、apply、bind関数
46623 ワード
🙋 Call
.call(受信した値、[optional]が呼び出す関数のパラメータのパラメータ値として、...)
function showThisName() {
// 여기서 this는 window를 가리킨다.
// window.name은 "" 빈문자열
console.log(this.name);
}
showThisName(); // "" 혹은 undefined
const mike = { name: "Mike" };
showThisName.call(mike); // Mike
JavaScript環境では、windowはグローバル変数、ノードであることに注意してください.js環境ではglobalはグローバル変数となる.次のコードを見てみましょう!
let kim = { name: "kim", first: 100, second: 20 };
let lee = { name: "kim", first: 10, second: 20 };
function sum() {
return this.first + this.second;
}
// sum();
sum.call();
// 둘은 같다. 그런데, 왜 구지 .call()을 사용해야할까?
// 모든 함수는 .call()이라는 메소드가 내장되있다.
sum.call(kim);
// sum() 안의 this 값은 kim의 this를 가리킨다.
console.log(sum.call(kim)); // 출력값 : 120
// sum()은 kim 이라는 객체의 member가 아니었는데,
// call()이라는 특이한 메소드를 통해서, sum()이라는 함수가 kim의 member 메소드가 된 것이다.
console.log(sum.call(lee)); // 출력값 : 30
// .call() 이라고 하는 것은 인자를 몇 개 더 받을 수 있는데
function sum2(prefix, another = "") {
return prefix + another + (this.first + this.second);
}
// prefix의 값이 없기 때문에, NaN
console.log(sum2.call(kim));
console.log(sum2.call(lee));
// [필수인자] call()이라는 함수의 첫번째 인자로는
// call()을 붙여줄 함수의 내부적으로 this를 뭘로 해줄 것인가가 오고,
// [optional] 2번째 인자부터는 우리가 호출하려고 하는 함수의 파라미터로 들어갈 인자값들이 들어간다.
console.log(sum2.call(kim, "=> ")); // 출력값 : => 120
console.log(sum2.call(lee, "두번째 인자 ", "세번째 인자 "));
// 출력값 : 두번째 인자 세번째 인자 30
// call()
// 상속과 super 등을 사용하지 않고 함수를 독립적으로 뺴서 객체에게 사용할때 call을 사용한다.
// call의 유사품으로는 apply가 있는데, 사용법은 거의 비슷하다.
🙋 apply
apply(オブジェクト、配列)
applyに入る前に、辞書の知識を整理します.
関数もオブジェクトです.
を選択します.コールと同じです.applyという名前の組み込みメソッドがあります.
spread演算子、restパラメータが誕生する前にapplyをよく使用します.
function sum(arg1, arg2) {
return arg1 + arg2;
}
console.log(sum.apply(null, [1, 2])); // 3
console.log(sum(1, 2)); // 3
しかし、なぜapplyを最初のパラメータnullとして使用するのでしょうか.なぜapplyを使うのですか?
function sum() {
let _sum = 0;
for (const name in this) {
// for문이 this를 열거하는 과정에서 sum이라는 함수도 더하기를 시도하기 때문에
// 에러 방지를 위해서 if 절이 필요하다.
if( typeof this[name] !== 'function' )
_sum += this[name];
}
return _sum;
}
let o1 = { val1: 1, val2: 2, val3: 3, sum:sum }
上記のコードから分かるように、o1
オブジェクトにはsum
の方法がある.すなわち、オブジェクトでsumを使用し、sumを使用する場合は、各オブジェクトにsumをインストールする必要があります.これはかなり面倒です.だから、現れたのは
apply()
です.// apply를 사용하는 구체적인 이유
let o1 = { val1: 1, val2: 2, val3: 3 };
let o2 = { v1: 10, v2: 50, v3: 100, v4: 25 };
function sum() {
let _sum = 0;
// for in 문으로 this라고 하는 객체의 값들을 하나씩 꺼네서, 더한다.
// 이 맥락에서 this는 현재 정해져있지 않다.
// this는 호출 할 때, 정해진다.
for (const name in this) {
_sum += this[name];
}
return _sum;
}
// 첫번째 인자로 null 대신에 객체를 짚어넣었다.
// sum()에서 this = o1 이다.
console.log(sum.apply(o1)); // 6
console.log(sum.apply(o2)); // 185
apply(オブジェクト、配列)の使い方
let obj = { num:2 };
let obj2 = { num:5 };
const addToThis = function (a,b,c) {
return this.num + a + b + c;
};
let arr = [1,2,3, 4];
console.log( addToThis.apply(obj, arr) ); // 8
// => addToThis() 는 a,b,c라는 3개의 인자만의 받는다.
// 그래서, arr 배열 안에서 첫 3개의 요소만이 obj의 num값과 더해진 것이다.
console.log( addToThis.apply(obj2, arr) ); // 11
applyの使用例
以下にapplyを用いて配列因子を放出する例を示す.
例1
// null을 this로 지정합니다. Math는 생성자가 아니므로 this를 지정할 필요가 없다.
Math.max.apply(null, [5,4,1,6,2]) // 6
// spread operator의 도입으로 굳이 apply를 이용할 필요가 없어졌다.
Math.max(...[5,4,1,6,2]) // 6
次の例では、Prototypeを使用して実行します.例2
// '피,땀,눈물'을 this로 지정한다.
''.split.call('피,땀,눈물', ',')
// 다음과 정확히 동일한 결과를 리턴한다.
'피,땀,눈물'.split(',')
より実用的な例
let allDivs = document.querySelectorAll('div'); // NodeList라는 유사 배열이다.
// allDivs를 this로 지정한다.
[].map.call(allDivs, function(el) {
return el.className
})
// allDivs는 유사 배열이므로 map 메소드가 존재하지 않는다.
// 그러나, Array prototype으로부터 map 메소드를 빌려와 this를 넘겨 map을 실행할 수 있다.
これでapplyを用いて,オブジェクトはパラメータを受け入れない独立関数を利用できるようになった.コール()の使い方や用途も似ています.ただし、call()との違いは、2番目のパラメータとして配列を得ることができるため、オブジェクトでなくても、関数がパラメータを受け入れない場合にその関数の配列形式のパラメータを指定することができることである.
すなわち,関数の再利用性を最大化するためにapply()が出現した.
ここまでcallとapplyについてまとめて、以下のようにします.
this는 누군가에의해 호출되기 전까진 window 를 가리킨다
그 누군가를 명확히 하기위해 call과 apply라는 메소드를 사용한다
차이점은 apply는 연산에 필요한 인자값을 배열로 받는반면
call은 단일 인수로 받는다
bob.call(bill, 2,’goodboy’)
Bob.apply(bill,[1,”-/:;”])
ではbindは何ですか?🙋 bind
.bind(これは受信する値、[optional]は私たちが呼び出す関数のパラメータのパラメータ値です...)
callでは、実行関数のこの値を必要なオブジェクトに置き換えて実行できます.
bindは、関数を実行するこの値を必要なオブジェクトに固定する新しい関数を作成します.
call:外部の機能を内部に引っ張って使用できます.
bind:コピーした親関数に影響を及ぼさずに、新しい関数をクローンして()のオブジェクトに置き換えます.
let kim = { name: "kim", first: 100, second: 20 };
let lee = { name: "kim", first: 10, second: 20 };
function sum(prefix, another = "") {
return prefix + another + (this.first + this.second);
}
let kimSum = sum.bind( kim, '-> ' );
console.log( kimSum() );
callとbindは違うようで、callは実行時に関数のこの値を変更し、bindはある関数の内部で値が永久に変更された新しい関数を返します.
call vs bind
上の説明だけでは足りないので、下のコードを見て理解してください.
筆者にとって,callとbindの違いは以下のコードで明確に理解できる.
let obj = { num: 2 };
const addToThis = function (a, b, c) {
return this.num + a + b + c;
};
console.log(addToThis.bind(obj, 1, 2, 3)); // function
// bind는 함수를 return한다. => 이 함수는 this값이 obj의 num으로 지정되있다.
// bind로 call과 같은 출력값을 내려면,
console.log(addToThis.bind(obj, 1, 2, 3)()); // 8
console.log(addToThis.bind(obj)(1, 2, 3)); // 8
console.log(addToThis.call(obj, 1, 2, 3)); // 8
// call은 출력값(결과값)을 return한다.
// bind는 원본에 영향을 주지 않는 독립된 새로운 함수를 return하기 때문에,
// 변수 담아뒀다가 필요할 때마다 재사용하는 방식을 많이 쓴다.
let bound = addToThis.bind(obj);
console.log(bound(1, 2, 3)); // 8
後でreactでは.bind(this)
形をよく使いますが、後でreactを整理するときに詳しく調べてみましょう.bindを使用するいくつかの有用な例
bindはcall,applyに比べて多くの比較的有用な使用例がある.まず2つの例を見てみましょう.
Case 1:イベントハンドラ
bindは、イベントハンドラがイベントオブジェクトではなく別の値を渡す場合に便利です.次のように仮定します.
複数のボタンを動的に作成し、イベントハンドラごとに異なる値をバインドする必要がある場合は、を考慮します.
次の例では、各ボタンをクリックするとalertにユーザ情報が表示されることが望ましい.
https://codesandbox.io/s/bind-method-hagseub-nlmzi?from-embed
前述したように、コードを記述する場合は、動的に生成された各ボタンをクリックすると、ユーザーに必要な値が正しく渡されません.(console.logでuserが実際に何を撮ったか確認)
この場合bindを使用してパラメータを指定できますが、すぐに実行することはできません.この場合、この値は重要ではないのでnullのように何も渡さないことができます.
Solution 1:
users.forEach(user => {
let btn = document.createElement('button');
btn.textContent = user.name;
btn.onclick = handleClick.bind(null, user);
target.appendChild(btn);
});
Solution 2:
bindではなく匿名関数を使用して問題を解決することもできます.
users.forEach(user => {
let btn = document.createElement('button')
btn.textContent = user.name
btn.onclick = () => {
handleClick(user)
}
target.appendChild(btn)
});
case 2: setTimeout
settimeoutは、遅延が発生した後に関数を非同期で実行する関数です.この関数は、Windowsオブジェクトを常に明示的にこのオブジェクトにバインドする特徴があります.このため、次の問題が発生する可能性があります.
例
class Rectangle {
constructor(width, height) {
this.width = width
this.height = height
}
getArea() {
return this.width * this.height
}
printArea() {
console.log('사각형의 넓이는 ' + this.getArea() + ' 입니다')
}
printSync() {
// 즉시 사각형의 넓이를 콘솔에 표시한다
this.printArea()
}
printAsync() {
// 1초 후 사각형의 넓이를 콘솔에 표시한다
setTimeout(this.printArea, 2000)
}
}
let box = new Rectangle(40, 20)
box.printSync() // '사각형의 넓이는 800 입니다'
box.printAsync() // 에러 발생!
これがRectangleのインスタンスではないことをエラーで確認できます.Uncaught TypeError: this.getArea is not a function
at printArea (<anonymous>:12:36)
n.問題
console.log(this)
を追加し、直接検証します.Solution 1:
printAsync() {
// 1초 후 사각형의 넓이를 콘솔에 표시한다
setTimeout(this.printArea.bind(this), 2000)
}
矢印関数を入力します.次に、正常に動作している例を示します.矢印関数のthisに何か違いがあるか知っていますか.Solution 2:
printAsync() {
// 1초 후 사각형의 넓이를 콘솔에 표시한다
setTimeout(() => {
this.printArea()
}, 2000)
}
🙋 call apply bindクリーンアップ
- call()
=>
call
は、1番目のパラメータとしてthis
の値を指定するオブジェクトを受信し、2番目のパラメータから追加するパラメータを追加できます.=>
call
は、関数を直ちに実行し、対応する結果値を返します.- apply()
=>
apply
はすべての点でcall
と同じであるが、添加された因子は配列形態に加えられるべきである.=>
apply
は、関数を直ちに実行し、対応する結果値を返します.- bind()
=>
bind
は、この値を指定したオブジェクトを最初のパラメータとして受信する.2番目の因子はcall
のように入れることができます.=>ただし、
bind
は、関数のreturn値ではなく、最初のパラメータのオブジェクト値がthis
であることを指定する独立した新しい関数を返します.=>通常、この関数は再使用のために変数に割り当てられ、必要に応じて使用されます.
資料の出所と参考資料
JavaScriptミドルチュートリアル#13 call、apply、bind byエンコーディング
javaScript call apply and bind
JavaScriptオブジェクト向けプログラミング-14.2。生活コード
JavaScript関数の呼び出し(1/2):生活コード別アプリケーションの概要
JavaScript-呼び出し関数(2/2):適用された生活符号化
JavaScriptオブジェクト向けプログラミング-14.3。bindby生活コード
React - 16.4. 生活符号化によるイベントbind関数の理解
Reference
この問題について(call、apply、bind関数), 我々は、より多くの情報をここで見つけました https://velog.io/@gil0127/call-apply-bindテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol