๐ ๋งํฌ์ฐธ๊ณ
ํจ์ ํธ์ถ ๋ฐฉ์์ ์ํด ๊ฒฐ์ ๋๋ this ์์ ํ ์ดํดํ๊ณ ์์ผ ํจ. (๐tistory ๋์ฒด ๋งํฌ)
1. this
ย ย function ํค์๋๋ก ์์ฑํ ์ผ๋ฐ ํจ์์ ํ์ดํ ํจ์์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ this์ ๋๋ค.
์ผ๋ฐ ํจ์์ this
ย ย ์๋ฐ์คํฌ๋ฆฝํธ์ ๊ฒฝ์ฐ ํจ์ ํธ์ถ ๋ฐฉ์์ ์ํด this์ ๋ฐ์ธ๋ฉํ ์ด๋ค ๊ฐ์ฒด๊ฐ ๋์ ์ผ๋ก ๊ฒฐ์ ๋ฉ๋๋ค.
๋ค์ ๋งํด, ํจ์๋ฅผ ์ ์ธํ ๋ ๋ฐ์ธ๋ฉํ ๊ฐ์ฒด๊ฐ ๊ฒฐ์ ๋๋ ๊ฒ์ด ์๋๊ณ , ํจ์๋ฅผ ํธ์ถํ ๋ ํจ์๊ฐ ์ด๋ป๊ฒ ํธ์ถ๋์๋์ง์ ๋ฐ๋ผ this์ ๋ฐ์ธ๋ฉํ ๊ฐ์ฒด๊ฐ ๋์ ์ผ๋ก ํ ๋น๋๋ ๊ฒ์
๋๋ค.
์ฝ๋ฐฑ ํจ์ ๋ด๋ถ์ this๋ browserํ๊ฒฝ์์๋ ์ ์ญ ๊ฐ์ฒด window๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
function Prefixer(prefix) {
ย this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
ย // (A)
ย return arr.map(function (x) {
ย ย return this.prefix + ' ' + x; // (B)
ย });
};
var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim'])); // [ 'undefined Lee', 'undefined Kim' ]
window ์ ์ญ ๊ฐ์ฒด๊ฐ ์๋๋ผ ์์ฑ์ ํจ์์ ์ธ์คํด์ค๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ํ๋ ค๋ฉด ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ์๋๋งํฌ ์ฐธ๊ณ
๐ํฐ์คํ ๋ฆฌ ๋งํฌthis
๐์ต์๋์ธ ๋งํฌthis
ํ์ดํ ํจ์์ this
ย ย ํ์ดํ ํจ์๋ ํจ์๋ฅผ ์ ์ธํ ๋ this์ ๋ฐ์ธ๋ฉํ ๊ฐ์ฒด๊ฐ ์ ์ ์ผ๋ก ๊ฒฐ์ ๋ฉ๋๋ค. ๋์ ์ผ๋ก ๊ฒฐ์ ๋๋ ์ผ๋ฐ ํจ์์๋ ๋ฌ๋ฆฌ ํ์ดํ ํจ์์ this๋ ์ธ์ ๋ ์์ ์ค์ฝํ์ this๋ฅผ ๊ฐ๋ฆฌํต๋๋ค. ์ด๋ฅผ Lexical this๋ผ๊ณ ํฉ๋๋ค.
ํ์ดํ ํจ์์ this ๋ฐ์ธ๋ฉ ๊ฐ์ฒด ๊ฒฐ์ ๋ฐฉ์์ ํจ์์ ์์ ์ค์ฝํ๋ฅผ ๊ฒฐ์ ํ๋ ๋ฐฉ์์ธ ๋ ์์ปฌ ์ค์ฝํ์ ์ ์ฌํฉ๋๋ค. โญ
function Prefixer(prefix) {
ย this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
ย // this๋ ์์ ์ค์ฝํ์ธ prefixArray ๋ฉ์๋ ๋ด์ this๋ฅผ ๊ฐ๋ฆฌํจ๋ค.
ย return arr.map(x => `${this.prefix} ย ${x}`);
};
const pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));
ํนํ ๋ฐ๋ก ์๋ ์์๊ฐ ํท๊ฐ๋ ธ์ ๋ ๋์์ด ๋ง์ด ๋์ต๋๋ค.
const another = {
name: 'another',
// @ts-ignore
getAnother(callbackFn?: Function) {
if (callbackFn) {
callbackFn();
return;
} else {
const log = () => console.log(this);
log();
}
},
};
another.getAnother(); // {name: 'another', getAnother: ฦ}
another.getAnother(() => console.log(this)); // Window { }
// browser ์ฝ์์์ ์ณ์ผ ํ๋ค.
var name = 'test';
/**
* ์ผ๋ฐ ํจ์
*/
const person1 = {
name: 'Kane',
tools: {
name: 'Sara',
sayHi() {
console.log(`Hi ${this.name}`); // Hi Sara
(function InnerHI() {
console.log(`Hi ${this.name}`); // Hi test
})();
const InnerHi2 = () => {
console.log(`Hi ${this.name}`); // Hi Sara
};
InnerHi2();
},
},
};
person1.tools.sayHi();
/**
* ๋ฉ์๋๋ก์์ ํ์ดํ ํจ์
*/
const person2 = {
name: 'Lee',
tools: {
name: 'Kim',
sayHi: () => console.log(`Hi ${this.name}`), // Hi test
},
};
person2.tools.sayHi();
/**
* ๋ฉ์๋ ํจ์ ๋ด๋ถ์ ํ์ดํ ํจ์ ํธ์ถ
*/
const person3 = {
name: 'John',
tools: {
name: 'Mike',
InnerFunc() {
const sayHi = () => console.log(`Hi ${this.name}`); // Hi Mike
sayHi();
const InnerHi = () => {
console.log(`Hi ${this.name}`); // Hi Mike
const InnerHi2 = () => {
console.log(`Hi ${this.name}`); // Hi Mike
};
InnerHi2();
(function InnerArrowFunc() {
console.log(`Hi ${this.name}`); // Hi test
})();
};
InnerHi();
},
},
};
person3.tools.InnerFunc();
๐จ ํ์ดํ ํจ์๋ call, apply, bind ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ this๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ๐จ
window.x = 1;
const normal = function () { return this.x; };
const arrow = () => this.x;
console.log(normal.call({ x: 10 })); // 10
console.log(arrow.call({ x: 10 })); ย // 1
ย ย ์กฐ๊ธ ๋ ๋ณต์กํ ์์ ์ฝ๋๋ฅผ ๋ณด๊ฒ ์ต๋๋ค.
addEventListener ๋ฉ์๋์ ์ฝ๋ฐฑ ํจ์ ๋ด๋ถ์ this๋ event.currentTarget์ ๋ฐ์ธ๋ฉ๋๋ค๊ณ ํ์์ต๋๋ค.(์๊ณ ์์ด์ผ ํจ. ์ค์! ๐Event์ฐธ๊ณ )
๊ทธ๋ฌ๋ ์๋ debounce ํจ์์ setTimeout์์ ๋ด๋ถ์ ์ผ๋ก ๋ฐ์์จ callbackFnํจ์๋ฅผ apply ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ธ๋ฉํ๋ ค ํ์ผ๋ ํ์ดํ ํจ์๋ ๋ฐ์ธ๋ฉ ๋์ง ์๊ณ ์ ์ญ ๊ฐ์ฒด window๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ชจ์ต์ ๋ณผ ์ ์์ต๋๋ค.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="text/javascript" defer src="code-snippet/debounce.js"></script>
</head>
<body>
<button id="special">๋๋ฐ์ด์ค</button>
<script>
function debounce(callbackFn: Function, limit: number = 100) {
let timer: ReturnType<typeof setTimeout> | null = null;
return function(...args: any[]) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(
// @ts-ignore
() => callbackFn.apply(this, args), // โ
limit,
);
};
}
const debounceEventHandler = debounce(() => console.log(this), 2000); // --> ์ฌ๊ธฐ์ ์ด๋ฏธ this๋ window {}์ ๋ฐ์ธ๋ฉ ๋จ
// const debounceEventHandler = debounce(function () {
// console.log(this);
// }, 2000); // <button id="special">๋๋ฐ์ด์ค</button>
special.addEventListener('click', debounceEventHandler);
</script>
</body>
</html>
๊ทธ ์ด์ ๋ const debounceEventHandler = debounce(() => console.log(this), 2000);์์ ์ด๋ฏธ ํ์ดํ ํจ์๋ ์ ์ ์ผ๋ก ์์ ์ค์ฝํ์ธ window๊ฐ์ฒด์ ๋ฐ์ธ๋ฉ์ด ๋์๊ธฐ ๋๋ฌธ์
๋๋ค. addEventListener์ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ํจ์๋ก debounceEventHandler ์ฆ, debounceํจ์๊ฐ ํธ์ถ๋์ด return๋๋ ์ต๋ช
ํจ์์ this๊ฐ currentTarget(=special)์ ๊ฐ๋ฆฌํค๊ณ ์์์๋ ๋ถ๊ตฌํ๊ณ ์ต๋ช
ํจ์ ๋ด๋ถ ์ค์ฝํ์ this๋ฅผ ๋ฐ๋ผ๊ฐ์ง ์์์ต๋๋ค.
๋ฐ๋ฉด ์ต๋ช
ํจ์ ์ ์ธ์์ apply ๋ฐ์ธ๋ฉ์ด ์ ๋ผ์ button node๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.(addEventListener ์์ ์ฃผ์ ์ฒ๋ฆฌํด๋์ ๋ถ๋ถ)
์ ๋ง ์ต๋ช
ํจ์ ๋ด๋ถ์ this๊ฐ btn (event.currentTarget)์ ์๋์ผ๋ก ๋ฐ์ธ๋ฉ ๋์ด ์๋์ง ํ์ธํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ ํด์ ํ์ธํด๋ณด๋ฉด ๋๊ฒ ์ต๋๋ค.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="text/javascript" defer src="code-snippet/debounce.js"></script>
</head>
<body>
<button id="special">๋๋ฐ์ด์ค</button>
<script>
function debounce(callbackFn: Function, limit: number = 100) {
let timer: ReturnType<typeof setTimeout> | null = null;
return function(...args: any[]) {
if (timer) {
clearTimeout(timer);
}
// @ts-ignore
console.log(this); // ์ต๋ช
ํจ์๋ event.curretTarget์ ๋ฐ์ธ๋ฉ ๋์ด์์.
timer = setTimeout(
// @ts-ignore
() => callbackFn.apply(this, args), //callbackFn์ ํํ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง
limit,
);
};
}
const debounceEventHandler = debounce(() => console.log(this), 2000); // window {}
// const debounceEventHandler = debounce(function () {
// console.log(this);
// }, 2000); // <button id="special">๋๋ฐ์ด์ค</button>
special.addEventListener('click', debounceEventHandler);
</script>
</body>
</html>
๋ฒํผ์ ๋๋ฌ๋ณด๋ฉด ์๋์ ๊ฐ์ด ์ฝ์์ด ์ฐํ๋ ๋ชจ์ต์ ๋ณผ ์ ์์ต๋๋ค.
<button id="special">๋๋ฐ์ด์ค</button>
Windowย {window: Window, self: Window, document: document, name: '', location: Location,ย โฆ}
class super ํค์๋์ ํ์ดํ ํจ์ โญ
class Animal {
constructor() {}
stop() {
console.log('๋ฉ์ถฐ!');
}
}
class Rabbit extends Animal {
stop() {
setTimeout(() => super.stop(), 1000); // ๋ฉ์ถฐ!
// setTimeout(function () {
// super.stop(); // ! 'super' can only be referenced in members of derived classes or object literal expressions.ts(2660)
// }, 1000);
}
}
const ๋ธ๋ ์ดํฌ = new Rabbit();
๋ธ๋ ์ดํฌ.stop();
์ ์์์์ ํ์ดํ ํจ์๋ ๋๊ณ ํจ์ ํํ์์ ์๋ฌ๋ฅผ ๋ฐ์์ํต๋๋ค.
๊ทธ ์ด์ ๋ฅผ ์ฐ๋ฆฌ๋ [javascript] ํจ์ ํธ์ถ ๋ฐฉ์์ ์ํด ๊ฒฐ์ ๋๋ this๋ฅผ ์๋ค๋ฉด ์ฝ๊ฒ ์ดํดํ ์ ์์ต๋๋ค.
sto ์์ ํจ์ ํํ์์ .(๋ท ๋
ธํ
์ด์
)์ด ์๊ธฐ ๋๋ฌธ์ ๋
์์ ์ธ this scope๋ฅผ ๊ฐ์ต๋๋ค. ์ฆ, ์ด this๋ ์๋ฌด๊ฒ๋ ์ฐธ์กฐํ์ง ๋ชปํฉ๋๋ค. ๊ทธ๋์ ์์ํ ์์ ์์ฑ์ ํจ์๋ฅผ ๊ฐ๋ฆฌํค๋ superํค์๋๋ ์ฌ์ฉํ์ง ๋ชปํฉ๋๋ค.
๋ฐ๋ฉด, ํ์ดํ ํจ์์ this๋ ์์์ Lexical this๋ผ๊ณ ์ธ๊ธํ์์ต๋๋ค. ๋๋ฌธ์ outer(์ ์ธ ๋์ ์์ ์ธ๋ถ ํ๊ฒฝ stop() ๋ฉ์๋)๋ฅผ ํตํด ์๋ก ๊ฑฐ์ฌ๋ฌ ์ฌ๋ผ๊ฐ ์ ์์ต๋๋ค. ๊ทธ๋์ super ํค์๋๋ฅผ ํตํด ์์ํ ์์ ์์ฑ์ ํจ์๋ก ์ ๊ทผํ ์ ์์ต๋๋ค.
์ดํด๊ฐ ์๋๋ค๋ฉด this ๋ฅผ ์ง์ ์ฐ์ด๋ณด์
class Rabbit extends Animal {
stop() {
setTimeout(() => console.log(this), 1000); // Rabbit {}
}
}
class Rabbit extends Animal {
stop() {
setTimeout(function(){ console.log(this); }, 1000); // window {}
}
}
2. ๐จ ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํด์๋ ์ ๋๋ ๊ฒฝ์ฐ ๐จ
ํ์ดํ ํจ์๋ Lexical this๋ฅผ ์ง์ํ๋ฏ๋ก ์ฝ๋ฐฑ ํจ์๋ก ์ฌ์ฉํ๊ธฐ ํธ๋ฆฌํฉ๋๋ค. ํ์ง๋ง ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์คํ๋ ค ํผ๋์ ๋ถ๋ฌ์ค๋ ๊ฒฝ์ฐ๋ ์์ผ๋ฏ๋ก ์ฃผ์ํ์ฌ์ผ ํฉ๋๋ค. (ํ์ดํ ํจ์๋ ๋ฌธ๋ฒ์ ์คํ synthetic sugar์ผ๋ก๋ ์ฌ๊ฒจ์ง๋๋ค.)
๋ฉ์๋
ย ย ํ์ดํ ํจ์๋ก ๋ฉ์๋๋ฅผ ์ ์ํ๋ ๊ฒ์ ํผํด์ผ ํฉ๋๋ค. ํ์ดํ ํจ์๋ก ๋ฉ์๋๋ฅผ ์ ์ํด๋ด ์๋ค.
// Bad
const person = {
ย name: 'Lee',
ย sayHi: () => console.log(`Hi ${this.name}`)
};
person.sayHi(); // Hi undefined
์ ์์ ์ ๊ฒฝ์ฐ ๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํค์ง ์๊ณ ์์ ์ปจํ ์คํธ์ธ window ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ์์์ ๋ฐ์ธ๋ฉ์ด ์ ์ฉ๋์ง ์๋ ๊ฒ์ด์ฃ . ๋ฐ๋ผ์ ํ์ดํ ํจ์๋ก ๋ฉ์๋๋ฅผ ์ ์ํ๋ ๊ฒ์ ๋ฐ๋์งํ์ง ์์ต๋๋ค.
์ด์ ๊ฐ์ ๊ฒฝ์ฐ์๋ ๋ฉ์๋๋ฅผ ์ํ ๋จ์ถ ํ๊ธฐ๋ฒ์ธ ES6์ ์ถ์ฝ ๋ฉ์๋ ํํ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
// Good
const person = {
ย name: 'Lee',
ย sayHi() { // === sayHi: function() {
ย ย console.log(`Hi ${this.name}`);
ย }
};
person.sayHi(); // Hi Lee
prototype
ย ย ํ์ดํ ํจ์๋ก ์ ์๋ ๋ฉ์๋๋ฅผ prototype์ ํ ๋นํ๋ ๊ฒฝ์ฐ๋ ๋์ผํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ํ์ดํ ํจ์๋ก ์ ์๋ ๋ฉ์๋๋ฅผ prototype์ ํ ๋นํ์ฌ ๋ณด๊ฒ ์ต๋๋ค.
// Bad
const person = {
ย name: 'Lee',
};
Object.prototype.sayHi = () => console.log(`Hi ${this.name}`);
person.sayHi(); // Hi undefined
ํ์ดํ ํจ์๋ก ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ์ ์ํ์์ ๋์ ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค. ๋ฐ๋ผ์ prototype์ ๋ฉ์๋๋ฅผ ํ ๋นํ๋ ๊ฒฝ์ฐ, ์ผ๋ฐ ํจ์๋ฅผ ํ ๋นํด์ผ ํฉ๋๋ค.
// Good
const person = {
ย name: 'Lee',
};
Object.prototype.sayHi = function() {
ย console.log(`Hi ${this.name}`);
};
person.sayHi(); // Hi Lee
์์ฑ์ ํจ์
ย ย ํ์ดํ ํจ์๋ ์์ฑ์ ํจ์๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ์์ฑ์ ํจ์๋ prototype ํ๋กํผํฐ๋ฅผ ๊ฐ์ง๋ฉฐ prototype ํ๋กํผํฐ๊ฐ ๊ฐ๋ฆฌํค๋ ํ๋กํ ํ์ ๊ฐ์ฒด์ constructor๋ฅผ ์ฌ์ฉํฉ๋๋ค. ํ์ง๋ง ํ์ดํ ํจ์๋ prototype ํ๋กํผํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ง ์์ต๋๋ค.
const Foo = () => {};
// ํ์ดํ ํจ์๋ prototype ํ๋กํผํฐ๊ฐ ์๋ค
console.log(Foo.hasOwnProperty('prototype')); // false
const foo = new Foo(); // TypeError: Foo is not a constructor
addEventListener ํจ์์ ์ฝ๋ฐฑ ํจ์
ย ย addEventListener ํจ์์ ์ฝ๋ฐฑ ํจ์๋ฅผ ํ์ดํ ํจ์๋ก ์ ์ํ๋ฉด this๊ฐ ์์ ์ปจํ์คํธ์ธ ์ ์ญ ๊ฐ์ฒด window๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
// Bad
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
ย console.log(this === window); // => true
ย this.innerHTML = 'Clicked button';
});
๋ฐ๋ผ์ addEventListener ํจ์์ ์ฝ๋ฐฑ ํจ์ ๋ด์์ this๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, function ํค์๋๋ก ์ ์ํ ์ผ๋ฐ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ์ผ ํฉ๋๋ค. ์ผ๋ฐ ํจ์๋ก ์ ์๋ addEventListener ํจ์์ ์ฝ๋ฐฑ ํจ์ ๋ด๋ถ์ย this๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋์ ๋ฐ์ธ๋ฉ๋ ์์(currentTarget)๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
// Good
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
ย console.log(this === button); // => true
ย this.innerHTML = 'Clicked button';
});
์ด ๊ธ์ ์ต์๋์ธ(Obsidian)์์ ์์ฑ๋์์ต๋๋ค. ํฐ์คํ ๋ฆฌ์์ ์๋๊ฒฝ๋ก ๋งํฌ๋ ์๋ํ์ง ์์ต๋๋ค. ํฐ ์ด๋ฏธ์ง ํ์ผ์ ์ ๋ก๋๋์ง ์์ ์ ์์ต๋๋ค.