๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

[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

๋ถ„์„๏ผš

  1. b(c) โ†’ c()๋ฅผ ์‹คํ–‰ํ•˜๊ณ , 'hello'๋ฅผ ์ถœ๋ ฅ, 'world'๋ฅผ ๋ฐ˜ํ™˜
  2. 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

๋ถ„์„๏ผš

  1. b(c) โ†’ c ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ (์‹คํ–‰ํ•˜์ง€ ์•Š์Œ)
  2. a(c) โ†’ c ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜
  3. result๋Š” c ํ•จ์ˆ˜
  4. result() โ†’ c()๋ฅผ ์‹คํ–‰ํ•˜๊ณ , 'hello'๋ฅผ ๋ฐ˜ํ™˜

ํ•ต์‹ฌ ์ •๋ฆฌโ€‹

// ํ•จ์ˆ˜ ํ˜ธ์ถœ ์šฐ์„ ์ˆœ์œ„
a(b(c))
โ†“
// 1. ๋จผ์ € ๊ฐ€์žฅ ์•ˆ์ชฝ์„ ์‹คํ–‰
b(c) // b์— return์ด ์—†์œผ๋ฉด undefined
โ†“
// 2. ๊ทธ ๋‹ค์Œ ๋ฐ”๊นฅ์ชฝ์„ ์‹คํ–‰
a(undefined) // undefined()๋ฅผ ์‹คํ–‰ํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์—๋Ÿฌ

// ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
// โœ… 1. ์ค‘๊ฐ„ ํ•จ์ˆ˜๊ฐ€ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ณด์žฅ
// โœ… 2. ๋˜๋Š” ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ๊ธฐ
a(() => b(c))

Referenceโ€‹