ãªãã¸ã§ã¯ãã¡ã½ããã§ setTimeout ã使ã£ããããªãã¸ã§ã¯ãã¡ã½ãããæ¸¡ããããªå ´åãâthis ã失ãâ ã¨ããæ¢ç¥ã®åé¡ãããã¾ãã
çªç¶ãthis ãæ£ããåä½ããã®ãããã¾ãããã®ç¶æ³ã¯åå¿è
ã®éçºè
ã«ã¯å
¸åçã§ãããçµé¨è
ã§ãåæ§ã«èµ·ãããã¾ãã
âthisâ ã失ã
ç§ãã¡ã¯ãã§ã«ãJavaScriptã§ã¯ this ã失ããã¨ã容æã§ãããã¨ãç¥ã£ã¦ãã¾ãã ããã¡ã½ããããªãã¸ã§ã¯ãããå¥ã®å ´æã«æ¸¡ãããã¨ãthis ã¯å¤±ããã¾ãã
ããã§ setTimeout ãå©ç¨ãã¦ã©ã®ããã«èµ·ããã®ãã示ãã¾ã:
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
setTimeout(user.sayHi, 1000); // Hello, undefined!
ä¸ã§åããéãåºåã¯ã this.firstName 㯠âJohnâ ã§ã¯ãªãã undefined ã§ã!
ããã¯ã setTimeout ã¯ãªãã¸ã§ã¯ãã¨ã¯å¥ã«é¢æ° user.sayHi ãæã£ã¦ããããã§ããæå¾ã®è¡ã¯ãã®ããã«æ¸ãç´ããã¨ãã§ãã¾ã:
let f = user.sayHi;
setTimeout(f, 1000); // user ã³ã³ããã¹ãã失ãã¾ã
ãã©ã¦ã¶ã«ããã¦ãã¡ã½ãã setTimeout ã¯å°ãç¹å¥ã§ã: 颿°å¼ã³åºãã§ã¯ this=window ãè¨å®ãã¾ã(Node.js ã§ã¯ãthis ã¯ã¿ã¤ãã¼ãªãã¸ã§ã¯ãã«ãªãã¾ãããããã§ã¯ã»ã¨ãã©é¢ä¿ããã¾ãã)ãå¾ã£ã¦ãthis.firstName ã¯ãåå¨ããªã window.firstName ãåå¾ãããã¨ãã¾ããä»ã®åæ§ã®ã±ã¼ã¹ã§ã¯ãé常 this 㯠undefined ã«ãªãã¾ãã
ãã®ã¿ã¹ã¯ã¯é常ã«å ¸åçã§ã â ãªãã¸ã§ã¯ãã¡ã½ãããã©ããå¥ã®å ´æï¼ããã§ã¯ã¹ã±ã¸ã¥ã¼ã©ã«æ¸¡ãã¦ï¼ããå¼ã³åºãããå ´åã§ãããããé©åãªã³ã³ããã¹ãã§å¼ã³åºããããã¨ã¯ã©ã®ããã«ç¢ºèªããã°ããã§ãããï¼
解決ç 1: å²ã
æãã·ã³ãã«ãªè§£æ±ºçã¯ã©ããããã颿°ã使ããã¨ã§ã:
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
setTimeout(function() {
user.sayHi(); // Hello, John!
}, 1000);
ããã¯ä¸æãåãã¾ãããªããªããå¤é¨ã®ã¬ãã·ã«ã«ç°å¢ãã user ãåãåããã¡ã½ãããæ®éã«å¼ã³åºãããã§ãã
ãããåãã§ãããããçãè¨æ³ã§ã:
setTimeout(() => user.sayHi(), 1000); // Hello, John!
è¯ãè¦ãã¾ãããã³ã¼ãæ§é ã«å ããªèå¼±æ§ãããã¾ãã
仮㫠setTimeout ãåãåã«(ä¸ã®ä¾ã§ã¯1ç§ã®é
å»¶ãããã¾ã!)ãuser ãå¤ã夿´ãã¦ãããï¼ããã¨çªç¶ãééã£ããªãã¸ã§ã¯ããå¼ã³åºãã§ããã!
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
setTimeout(() => user.sayHi(), 1000);
// ...1ç§ä»¥å
ã«æ¬¡ãè¡ãããã¨
user = {
sayHi() { alert("Another user in setTimeout!"); }
};
// Another user in setTimeout?!?
次ã®è§£æ±ºçã¯ãã®ãããªãã¨ãèµ·ããªããã¨ãä¿è¨¼ãã¾ãã
解決ç 2: bind
颿°ã¯ãthis ãåºå®ã§ããçµã¿è¾¼ã¿ã¡ã½ãã bind ãæä¾ãã¾ãã
åºæ¬ã®æ§æã¯æ¬¡ã®éãã§ã:
// ããè¤éãªæ§æã¯ããå°ãå¾ã§
let boundFunc = func.bind(context);
func.bind(context) ã®çµæã¯ç¹å¥ãªé¢æ°ã©ã¤ã¯ãª âã¨ãã¾ããã¯ãªãã¸ã§ã¯ã(exotic object)â ã§ããããã¯é¢æ°ã¨ãã¦å¼ã¶ãã¨ãã§ããfunc ã« this=context ãééçã«æ¸¡ãã¾ãã
è¨ãæããã¨ãboundFunc ã®å¼ã³åºãã¯ãåºå®ããã this ã§ã® func å¼ã³åºãã§ãã
ä¾ãã°ãfuncUser 㯠this=user ã§ã® func å¼ã³åºããæ¸¡ãã¾ã:
let user = {
firstName: "John"
};
function func() {
alert(this.firstName);
}
let funcUser = func.bind(user);
funcUser(); // John
ããã§ãfunc.bind(user) 㯠this=user ã§åºå®ããã func ã® âãã¤ã³ããããããªã¢ã³ãâ ã¨ãªãã¾ãã
ãã¹ã¦ã®å¼æ°ã¯ãªãªã¸ãã«ã® func ã« âãã®ã¾ã¾â 渡ããã¾ããä¾:
let user = {
firstName: "John"
};
function func(phrase) {
alert(phrase + ', ' + this.firstName);
}
// this ã user ã«ãã¤ã³ããã
let funcUser = func.bind(user);
funcUser("Hello"); // Hello, John (弿° "Hello" ãæ¸¡ãã, this=user)
ãã¦ããªãã¸ã§ã¯ãã¡ã½ããã§è©¦ãã¦ã¿ã¾ãããã:
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
let sayHi = user.sayHi.bind(user); // (*)
// ãªãã¸ã§ã¯ããªãã§å®è¡å¯è½
sayHi(); // Hello, John!
setTimeout(sayHi, 1000); // Hello, John!
// 1ç§ä»¥å
ã« user ã®å¤ãå¤ãã£ãã¨ãã¦ã
// sayHi ã¯å¤ã user ãªãã¸ã§ã¯ããåç
§ãã¦ãããã¤ã³ãåã®å¤ã使ç¨ãã¾ã
user = {
sayHi() { alert("Another user in setTimeout!"); }
};
(*) ã®è¡ã§ãã¡ã½ãã user.sayHi ã user ã«ãã¤ã³ããã¦ãã¾ããsayHi 㯠âæç¸(ãã¤ã³ã)ãããâ 颿°ã§ãããåç¬ããã㯠setTimeout ã«æ¸¡ãã¦å¼ã³åºããã¨ãã§ãã¾ãã
弿°ã âãã®ã¾ã¾â 渡ãããthis ã ãã bind ã«ãã£ã¦åºå®ããã¦ãããã¨ããããã¾ã:
let user = {
firstName: "John",
say(phrase) {
alert(`${phrase}, ${this.firstName}!`);
}
};
let say = user.say.bind(user);
say("Hello"); // Hello, John ("Hello" 弿°ã¯ say ã«æ¸¡ããã¾ã)
say("Bye"); // Bye, John ("Bye" 㯠say ã«æ¸¡ããã¾ã)
bindAllãããªãã¸ã§ã¯ããå¤ãã®ã¡ã½ãããæã¡ããããããã¤ã³ãããå¿ è¦ãããå ´åããã¹ã¦ã«ã¼ãã§ãã¤ã³ãã§ãã¾ãã:
for (let key in user) {
if (typeof user[key] == 'function') {
user[key] = user[key].bind(user);
}
}
JavaScriptã©ã¤ãã©ãªã¯ã便å©ãªå¤æ°ã®ãã¤ã³ããè¡ãããã®æ©è½ãæä¾ãã¦ãã¾ããe.g. _.bindAll(obj) in lodash.
é¨å颿°
ããã¾ã§ã¯ãthis ã®ãã¤ã³ãã«ã¤ãã¦ã®ã¿èª¬æãã¦ãã¾ãããæ¬¡ã®ã¹ãããã«ããã¾ãããã
this ã ãã§ãªãã弿°ããã¤ã³ããããã¨ãå¯è½ã§ããããã¯ãã£ãã«ããã¾ãããã便å©ãªå ´åãããã¾ãã
bind ã®å®å
¨ãªæ§æã¯æ¬¡ã®éãã§ã:
let bound = func.bind(context, [arg1], [arg2], ...);
context ã thisãã¨ãã颿°ã®éå§å¼æ°ããã¤ã³ããããã¨ãã§ãã¾ãã
ä¾ãã°ãä¹ç®é¢æ° mul(a, b) ãããã¨ãã¾ã:
function mul(a, b) {
return a * b;
}
ããããã¼ã¹ã«ãbind ã使ç¨ãã¦ãdouble 颿°ã使ãã¾ãããã:
function mul(a, b) {
return a * b;
}
let double = mul.bind(null, 2);
alert( double(3) ); // = mul(2, 3) = 6
alert( double(4) ); // = mul(2, 4) = 8
alert( double(5) ); // = mul(2, 5) = 10
mul.bind(null, 2) ã®å¼ã³åºãã§æ°ãã颿° double ã使ããããã¯ã³ã³ããã¹ãã nullãæåã®å¼æ°ã 2 ã§åºå®ãã mul ãå¼ã³åºãã¾ãããã以éã®å¼æ°ã¯ âãã®ã¾ã¾â 渡ããã¾ãã
ãã㯠é¨å颿°ã¢ããªã±ã¼ã·ã§ã³ ã¨å¼ã°ããæ¢åã®ãã©ã¡ã¼ã¿ã®ããã¤ããåºå®ã«ãããã¨ã§æ°ãã颿°ã使ãã¾ãã
å®éã«ã¯ããã§ã¯ this ã¯ä½¿ç¨ããªããã¨ã«çæãã¦ãã ãããã§ãããbind ã§æå®ãå¿
è¦ãªã®ã§ null ãªã©ä½ããããç½®ãå¿
è¦ãããã¾ãã
以ä¸ã®ã³ã¼ãã®é¢æ° triple ã¯å¤ã3åãã¾ãã:
function mul(a, b) {
return a * b;
}
let triple = mul.bind(null, 3);
alert( triple(3) ); // = mul(3, 3) = 9
alert( triple(4) ); // = mul(3, 4) = 12
alert( triple(5) ); // = mul(3, 5) = 15
ãªãé¨å颿°ãä½ãã®ã§ããããï¼
ã¡ãªããã¯ãèªã¿ãããååï¼double, tripleï¼ã§ç¬ç«ãã颿°ãä½ããã¨ãã§ãããã¨ã§ããbind ã§åºå®ããã¦ãããããæ¯åæåã®å¼æ°ãæå®ããå¿
è¦ãããã¾ããã
ä»ã®ã±ã¼ã¹ã§ã¯ãé常ã«ä¸è¬çãªé¢æ°ãããç¶æ ã§ã便å©ãã®ããã«ç¹å®ç¨éã®ãã¿ã¼ã³ã欲ããå ´åã«é¨å颿°ã¯å½¹ç«ã¡ã¾ãã
ä¾ãã°ã颿° send(from, to, text) ãããã¨ãã¾ããuser ãªãã¸ã§ã¯ãã®ä¸ã§ããã®é¨åãã¿ã¼ã³ã使ç¨ãããå ´åãç¾å¨ã®ã¦ã¼ã¶ããéä¿¡ããã颿° sendTo(to, text) ã
Going partial without context
ä»®ã«å¼æ°ã®ããã¤ããåºå®ãããããã³ã³ããã¹ã this ã¯åºå®ããããªãå ´åã¯ã©ããã¾ããï¼ä¾ãã°ããªãã¸ã§ã¯ãã¡ã½ããã§ãã
ãã¤ãã£ãã® bind ã¯ããã¯è¨±å¯ãã¾ãããã³ã³ããã¹ããçç¥ãã¦å¼æ°ã ãæå®ãããã¨ã¯ã§ãã¾ããã
幸ããªãã¨ã«ã弿°ã ãããã¤ã³ãããããã®é¢æ° partial ã¯ç°¡åã«å®è£
ã§ãã¾ãã
次ã®ããã«ãªãã¾ã:
function partial(func, ...argsBound) {
return function(...args) { // (*)
return func.call(this, ...argsBound, ...args);
}
}
// Usage:
let user = {
firstName: "John",
say(time, phrase) {
alert(`[${time}] ${this.firstName}: ${phrase}!`);
}
};
// åºå®æéã§é¨åã¡ã½ããã追å
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());
user.sayNow("Hello");
// Something like:
// [10:00] John: Hello!
partial(func[, arg1, arg2...])å¼ã³åºãã®çµæã¯ä»¥ä¸ãã㤠func ãå¼ã³åºãã©ããã¼ (*)ã§ãã
- åå¾ãããã®ã¨åã
thisï¼user.sayNowå¼ã³åºãã®å ´åãuserï¼ - 次ã«
...argsBoundãæå®ãã¾ããpartialå¼ã³åºãããã®å¼æ° ("10:00") - 次ã«
...argsãã©ããã¼ã«ä¸ãããã弿°("Hello")
ãªã®ã§ãã¹ãã¬ããæ§æã§ç°¡åã«è¡ããã¨ãã§ãã¾ãã
ã¾ããlodash ã©ã¤ãã©ãªã§ã¯ã_.partial å®è£ ãç¨æããã¦ãã¾ãã
ãµããª
ã¡ã½ãã func.bind(context, ...args) ã¯ã³ã³ããã¹ã this ãåºå®ãã颿° func ã® âæç¸ãããããªã¢ã³ãâ ãè¿ãã¾ãã
é常ã¯ããªãã¸ã§ã¯ãã¡ã½ããã§ this ãåºå®ããããã« bind ãé©ç¨ããã©ãã (ãã¨ãã° setTimeout) ã«æ¸¡ããã¨ãã§ããããã«ãã¾ãã
æ¢åã®é¢æ°ã«ããã¦ã弿°ã®ããã¤ããåºå®ããæ±ç¨æ§ã®ä½ã颿°ã¯ãé¨å颿°ã¨å¼ã°ãã¾ãã
é¨å颿°ã¯åã弿°ãä½åº¦ãç¹°ãè¿ããããªãã¨ãã«ä¾¿å©ã§ããä¾ãã°ãsend(from, to) ã¨ãã颿°ããã£ã¦ãfromã¯ã©ã®ã¿ã¹ã¯ã§ã常ã«åãã«ãªãã¯ããªããé¨å颿°ãå©ç¨ãã¦ãããå¦çã§ãã¾ãã
ã³ã¡ã³ã
<code>ã¿ã°ã使ã£ã¦ãã ãããè¤æ°è¡ã®å ´åã¯<pre>ãã10è¡ãè¶ ããå ´åã«ã¯ãµã³ãããã¯ã¹ã使ã£ã¦ãã ãã(plnkr, JSBin, codepenâ¦)ã