[Hard] ๐ Closure
1. What is Closure ?โ
Closure๋ ๋ฌด์์ธ๊ฐ์?
ํด๋ก์ ๋ฅผ ์ดํดํ๋ ค๋ฉด, ๋จผ์ JavaScript์ ๋ณ์ ์ค์ฝํ์ ํจ์๊ฐ ์ธ๋ถ ๋ณ์์ ์ด๋ป๊ฒ ์ ๊ทผํ๋์ง๋ฅผ ์ดํดํด์ผ ํฉ๋๋ค.
Variable Scope(๋ณ์ ์ค์ฝํ)โ
JavaScript์์ ๋ณ์์ ์ค์ฝํ๋ global scope์ function scope ๋ ๊ฐ์ง๋ก ๋๋ฉ๋๋ค.
// global scope
let a = 1;
function parentFunction() {
// function scope
let b = 2;
function childFunction() {
let c = 3;
console.log(a, b, c); // print 1 2 3, can access global scope & function scope
}
childFunction();
}
parentFunction();
console.log(a); // print 1, can access global scope
console.log(b, c); // ์๋ฌ ๋ฐ์, function scope ๋ด์ ๋ณ์์ ์ ๊ทผํ ์ ์์
Closure exampleโ
Closure์ ๋ฐ๋ ์กฐ๊ฑด์, ์์ ํจ์๊ฐ ๋ถ๋ชจ ํจ์ ๋ด๋ถ์ ์ ์๋๊ณ return์ ํตํด ๋ฐํ๋์ด, ์์ ํจ์ ๋ด์ ํ๊ฒฝ ๋ณ์๋ฅผ ๋ณด์กดํ๋ ๊ฒ์
๋๋ค(์ฆ Garbage Collection(๊ฐ๋น์ง ์ปฌ๋ ์
)์ ํํผํฉ๋๋ค).
function parentFunction() {
let count = 0;
return function childFunction() {
count += 1;
console.log(`ํ์ฌ ์นด์ดํธ๏ผ${count}`);
};
}
const counter = parentFunction();
counter(); // print ํ์ฌ ์นด์ดํธ๏ผ1
counter(); // print ํ์ฌ ์นด์ดํธ๏ผ2
// count ๋ณ์๋ ํ์๋์ง ์์. childFunction์ด ์ฌ์ ํ ์กด์ฌํ๊ณ ํธ์ถํ ๋๋ง๋ค count ๊ฐ์ด ์
๋ฐ์ดํธ๋๊ธฐ ๋๋ฌธ
ํ์ง๋ง ์ฃผ์ํด์ผ ํ ์ ์, ํด๋ก์ ๊ฐ ๋ณ์๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ๋ณด์กดํ๊ธฐ ๋๋ฌธ์ ๋ณ์๊ฐ ๋๋ฌด ๋ง์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๊ณผ๋ํด์ง ์ ์์ผ๋ฉฐ(ํด๋ก์ ๋ฅผ ๋จ์ฉํ๋ฉด ์ ๋จ), ๊ฒฐ๊ณผ์ ์ผ๋ก ์ฑ๋ฅ์ ์ํฅ์ ์ค ์ ์์ต๋๋ค.
2. Create a function that meets the following conditionsโ
์๋ ์กฐ๊ฑด์ ์ถฉ์กฑํ๋ function์ ๋ง๋์ธ์ (ํด๋ก์ ๊ฐ๋ ์ ์ฌ์ฉํ์ฌ ์ฒ๋ฆฌ)
plus(2, 5); // output 7
plus(2)(5); // output 7
First Solution : two functionsโ
๋ ๊ฐ์ function์ผ๋ก ๋ถ๋ฆฌํ์ฌ ์ฒ๋ฆฌํฉ๋๋ค
function plus(value, subValue) {
return value + subValue;
}
console.log(plus(2, 5));
// use closure save variable
function plus(value) {
return function (subValue) {
return value + subValue;
};
}
console.log(plus(2)(5));
Second Solution : single functionโ
๋ฌผ๋ก ์ฒซ ๋ฒ์งธ ํด๋ฒ์ reject๋ ๊ฐ๋ฅ์ฑ์ด ๋์ผ๋ฏ๋ก, ํ๋์ function์ผ๋ก ํฉ์ณ์ผ ํฉ๋๋ค.
function plus(value, subValue) {
// ๋งค๋ฒ ์ ๋ฌ๋๋ ๋งค๊ฐ๋ณ์์ ์๋ก ํ๋จ
if (arguments.length > 1) {
return value + subValue;
} else {
return function (item) {
return value + item;
};
}
}
console.log(plus(2, 5));
console.log(plus(2)(5));
3. Please take advantage of the closure feature to increase the numberโ
ํด๋ก์ ์ ํน์ฑ์ ์ด์ฉํ์ฌ ์ซ์๋ฅผ ์ฆ๊ฐ์ํค์ธ์
function plus() {
// code
}
var obj = plus();
obj.add(); // print 1
obj.add(); // print 2
First Solution : return variableโ
์ฌ๊ธฐ์๋ Arrow Function์ ์ฌ์ฉํ์ง ์๊ณ , ์ผ๋ฐ function ํํ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
function plus() {
let cash = 0;
let counter = {
add() {
cash += 1;
console.log(cash);
},
};
return counter;
}
var obj = plus();
obj.add();
obj.add();
Second Solution : return objectโ
์ด์ ํด๋ฒ์์, object๋ฅผ ์ง์ return ์์ ํฌํจํ ์๋ ์์ต๋๋ค
function plus() {
let cash = 0;
return {
add: function () {
cash += 1;
console.log(cash);
},
};
}
var obj = plus();
obj.add();
obj.add();
4. What will be printed in this nested function call?โ
์ด ์ค์ฒฉ๋ ํจ์ ํธ์ถ์ ๋ฌด์์ ์ถ๋ ฅํ ๊น์?
function a(aa) {
aa();
}
function b(bb) {
bb();
}
function c() {
console.log('hello');
}
a(b(c));
๋ถ์โ
์คํ ๊ฒฐ๊ณผ๏ผ
hello
TypeError: aa is not a function
์์ธ ์คํ ํ๋ฆโ
// a(b(c)) ์คํ
// JavaScript๋ ์์ชฝ์์ ๋ฐ๊นฅ์ชฝ์ผ๋ก ํจ์๋ฅผ ์คํ
// ๋จ๊ณ 1๏ผ๊ฐ์ฅ ์์ชฝ์ b(c) ์คํ
b(c)
โ
// c ํจ์๊ฐ ๋งค๊ฐ๋ณ์๋ก b์ ์ ๋ฌ๋จ
// b ํจ์ ๋ด๋ถ์์ bb(), ์ฆ c()๊ฐ ์คํ๋จ
c() // 'hello' ์ถ๋ ฅ
โ
// b ํจ์์๋ return ๋ฌธ์ด ์์
// ๋ฐ๋ผ์ undefined๋ฅผ ๋ฐํ
return undefined
// ๋จ๊ณ 2๏ผa(undefined) ์คํ
a(undefined)
โ
// undefined๊ฐ ๋งค๊ฐ๋ณ์๋ก a์ ์ ๋ฌ๋จ
// a ํจ์ ๋ด๋ถ์์ aa()๋ฅผ ์คํํ๋ ค๊ณ ์๋
// ์ฆ undefined()
undefined() // โ ์๋ฌ: TypeError: aa is not a function
์ ์ด๋ ๊ฒ ๋๋ ๊ฑธ๊น์?โ
1. ํจ์ ์คํ ์์ (์์ชฝ์์ ๋ฐ๊นฅ์ชฝ์ผ๋ก)โ
// ์์
console.log(add(multiply(2, 3)));
โ โ
| โโ 2. ๋จผ์ multiply(2, 3) ์คํ โ 6
โโโโโโโ 3. ๊ทธ ๋ค์ add(6) ์คํ
// ๊ฐ์ ๊ฐ๋
a(b(c))
โ โ
| โโ 1. ๋จผ์ b(c) ์คํ
โโโโ 2. ๊ทธ ๋ค์ a(b(c)์ ๊ฒฐ๊ณผ) ์คํ
2. ํจ์์ return์ด ์์ผ๋ฉด undefined๋ฅผ ๋ฐํโ
function b(bb) {
bb(); // ์คํํ์ง๋ง return์ด ์์
} // ์๋ฌต์ ์ผ๋ก return undefined
// ๋ค์๊ณผ ๋์ผ
function b(bb) {
bb();
return undefined; // JavaScript๊ฐ ์๋์ผ๋ก ์ถ๊ฐ
}
3. ํจ์๊ฐ ์๋ ๊ฒ์ ํธ์ถํ๋ฉด ์๋ฌ ๋ฐ์โ
const notAFunction = undefined;
notAFunction(); // TypeError: notAFunction is not a function
// ์๋ฌ๊ฐ ๋ฐ์ํ๋ ๋ค๋ฅธ ๊ฒฝ์ฐ
null(); // TypeError
123(); // TypeError
'string'(); // TypeError
์์ ๋ฐฉ๋ฒโ
๋ฐฉ๋ฒ 1๏ผb ํจ์๊ฐ ํจ์๋ฅผ ๋ฐํํ๋๋ก ํ๊ธฐโ
function a(aa) {
aa();
}
function b(bb) {
bb();
return function () {
console.log('b executed');
};
}
function c() {
console.log('hello');
}
a(b(c));
// ์ถ๋ ฅ:
// hello
// b executed
๋ฐฉ๋ฒ 2๏ผํจ์๋ฅผ ์ง์ ์ ๋ฌํ๊ณ , ๋จผ์ ์คํํ์ง ์๊ธฐโ
function a(aa) {
aa();
}
function b(bb) {
return function () {
bb();
};
}
function c() {
console.log('hello');
}
a(b(c)); // 'hello'๋ง ์ถ๋ ฅ
// ๋๋ ์ด๋ ๊ฒ ์์ฑ
a(() => b(c)); // 'hello' ์ถ๋ ฅ
๋ฐฉ๋ฒ 3๏ผ์คํ ๋ก์ง ๋ณ๊ฒฝโ
function a(aa) {
aa();
}
function b(bb) {
bb();
}
function c() {
console.log('hello');
}
// ๋ถ๋ฆฌํ์ฌ ์คํ
b(c); // 'hello' ์ถ๋ ฅ
a(() => console.log('a executed')); // 'a executed' ์ถ๋ ฅ
๊ด๋ จ ๋ฌธ์ โ
๋ฌธ์ 1๏ผ์ด๋ ๊ฒ ๋ฐ๊พธ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?โ
function a(aa) {
return aa();
}
function b(bb) {
return bb();
}
function c() {
console.log('hello');
return 'world';
}
console.log(a(b(c)));
ํด๋ฆญํ์ฌ ๋ต ๋ณด๊ธฐ
hello
TypeError: aa is not a function
๋ถ์๏ผ
b(c)โc()๋ฅผ ์คํํ๊ณ ,'hello'๋ฅผ ์ถ๋ ฅ,'world'๋ฅผ ๋ฐํa('world')โ'world'()๋ฅผ ์คํ... ์ ๊น, ์ด๊ฒ๋ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค!
์ ๋ต๏ผ
hello
TypeError: aa is not a function
b(c)๋ 'world'(๋ฌธ์์ด)๋ฅผ ๋ฐํํ๊ณ , a('world')๋ 'world'()๋ฅผ ์คํํ๋ ค ํ์ง๋ง, ๋ฌธ์์ด์ ํจ์๊ฐ ์๋๋ฏ๋ก ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
๋ฌธ์ 2๏ผ์ ๋ถ return์ด ์๋ค๋ฉด?โ
function a(aa) {
return aa;
}
function b(bb) {
return bb;
}
function c() {
return 'hello';
}
const result = a(b(c));
console.log(result);
console.log(result());
ํด๋ฆญํ์ฌ ๋ต ๋ณด๊ธฐ
[Function: c]
hello
๋ถ์๏ผ
b(c)โcํจ์ ์์ฒด๋ฅผ ๋ฐํ (์คํํ์ง ์์)a(c)โcํจ์ ์์ฒด๋ฅผ ๋ฐํresult๋cํจ์result()โc()๋ฅผ ์คํํ๊ณ ,'hello'๋ฅผ ๋ฐํ
ํต์ฌ ์ ๋ฆฌโ
// ํจ์ ํธ์ถ ์ฐ์ ์์
a(b(c))
โ
// 1. ๋จผ์ ๊ฐ์ฅ ์์ชฝ์ ์คํ
b(c) // b์ return์ด ์์ผ๋ฉด undefined
โ
// 2. ๊ทธ ๋ค์ ๋ฐ๊นฅ์ชฝ์ ์คํ
a(undefined) // undefined()๋ฅผ ์คํํ๋ ค๊ณ ํ๋ฉด ์๋ฌ
// ํด๊ฒฐ ๋ฐฉ๋ฒ
// โ
1. ์ค๊ฐ ํจ์๊ฐ ํจ์๋ฅผ ๋ฐํํ๋๋ก ๋ณด์ฅ
// โ
2. ๋๋ ํ์ดํ ํจ์๋ก ๊ฐ์ธ๊ธฐ
a(() => b(c))