From 740904e9a465189d2ef060d67570cfb8c04a359e Mon Sep 17 00:00:00 2001 From: meanzzi Date: Fri, 15 May 2026 18:06:14 +0900 Subject: [PATCH 1/9] =?UTF-8?q?fix=20:=2007-new-function/article.md=20?= =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../07-new-function/article.md | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/1-js/06-advanced-functions/07-new-function/article.md b/1-js/06-advanced-functions/07-new-function/article.md index adb621d44..6b27342e9 100644 --- a/1-js/06-advanced-functions/07-new-function/article.md +++ b/1-js/06-advanced-functions/07-new-function/article.md @@ -1,4 +1,3 @@ - # new Function 문법 함수 표현식과 함수 선언문 이외에 함수를 만들 수도 있는 방법이 하나 더 있습니다. 잘 사용하는 방법은 아니지만, 이 방법 외에는 대안이 없을 때 사용합니다. @@ -8,7 +7,7 @@ `new Function` 문법을 사용하면 함수를 만들 수 있습니다. ```js -let func = new Function ([arg1, arg2, ...argN], functionBody); +let func = new Function([arg1, arg2, ...argN], functionBody); ``` 새로 만들어지는 함수는 인수 `arg1...argN`과 함수 본문 `functionBody`로 구성됩니다. @@ -16,9 +15,9 @@ let func = new Function ([arg1, arg2, ...argN], functionBody); 인수 두 개가 있는 함수를 직접 만들어 보면서 `new Function` 문법에 대해 이해해보도록 합시다. ```js run -let sum = new Function('a', 'b', 'return a + b'); +let sum = new Function("a", "b", "return a + b"); -alert( sum(1, 2) ); // 3 +alert(sum(1, 2)); // 3 ``` 인수가 없고 함수 본문만 있는 함수를 만들어보겠습니다. @@ -46,11 +45,11 @@ func(); ## 클로저 -함수는 특별한 프로퍼티 `[[Environment]]`에 저장된 정보를 이용해 자기 자신이 태어난 곳을 기억합니다. `[[Environment]]`는 함수가 만들어진 렉시컬 환경을 참조합니다(자세한 내용은 에서 다루었습니다). +함수는 특별한 프로퍼티 `[[Environment]]`에 저장된 정보를 이용해 자기 자신이 태어난 곳을 기억합니다. `[[Environment]]`는 함수가 만들어진 렉시컬 환경을 참조합니다(자세한 내용은 에서 다루었습니다). 그런데 `new Function`을 이용해 함수를 만들면 함수의 `[[Environment]]` 프로퍼티가 현재 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하게 됩니다. -따라서 `new Function`을 이용해 만든 함수는 외부 변수에 접근할 수 없고, 오직 전역 변수에만 접근할 수 있습니다. +따라서 `new Function`을 이용해 만든 함수는 외부 변수에 접근할 수 없고, 오직 전역 변수에만 접근할 수 있습니다. ```js run function getFunc() { @@ -90,13 +89,9 @@ getFunc()(); // getFunc의 렉시컬 환경에 있는 값 *!*"test"*/!*가 출 `new Function`으로 만든 새로운 함수 내부에서 외부 변수에 접근하려 할 때, 기존 함수 선언 방식으로 작성한 함수와 동일한 동작이 보장되어야 하죠. -그런데 스크립트가 프로덕션 서버에 반영되기 전, *압축기(minifier)* 에 의해 압축될 때 문제가 발생합니다. 압축기는 스크립트에서 주석이나 여분의 공백 등을 없애 코드 크기를 줄여주는 특수한 프로그램인데 압축기가 지역 변수 이름을 짧게 바꾸면서 문제가 발생하죠. +그런데 스크립트가 프로덕션 서버에 반영되기 전, _압축기(minifier)_ 에 의해 압축될 때 문제가 발생합니다. 압축기는 스크립트에서 주석이나 여분의 공백 등을 없애 코드 크기를 줄여주는 특수한 프로그램인데 압축기가 지역 변수 이름을 짧게 바꾸면서 문제가 발생하죠. -<<<<<<< HEAD -구체적으로 어떤 부분이 문제가 되는지 예시를 통해 알아봅시다. 함수 내부에 `let userName`라는 변수가 있으면 이 지역변수는 압축기에 의해 `let a` 등(짧은 이름)으로 대체되는데, 이때 `userName` 모두가 `a`로 교체됩니다. `userName`은 지역변수이고, 함수 외부에선 함수 내부에 있는 변수에 접근할 수 없기 때문에 이렇게 해도 전혀 문제가 없죠. 압축기는 단순히 변수를 찾아서 바꾸지 않고 코드 구조를 분석해 기존에 작성한 코드의 기능을 망가뜨리지 않으면서 영리하게 제 역할을 수행합니다. -======= -For instance, if a function has `let userName`, minifier replaces it with `let a` (or another letter if this one is occupied), and does it everywhere. That's usually a safe thing to do, because the variable is local, nothing outside the function can access it. And inside the function, minifier replaces every mention of it. Minifiers are smart, they analyze the code structure, so they don't break anything. They're not just a dumb find-and-replace. ->>>>>>> upstream/master +구체적으로 어떤 부분이 문제가 되는지 예시를 통해 알아봅시다. 함수 내부에 `let userName`라는 변수가 있으면 이 지역변수는 압축기에 의해 `let a` 등(해당 글자가 이미 사용 중이라면 다른 짧은 이름)으로 대체되는데, 이때 `userName` 모두가 `a`로 교체됩니다. `userName`은 지역변수이고, 함수 외부에선 함수 내부에 있는 변수에 접근할 수 없기 때문에 이렇게 해도 전혀 문제가 없죠. 압축기는 단순히 찾아바꾸기가 아니라 코드 구조를 분석해 기존 코드의 기능을 망가뜨리지 않으면서 영리하게 제 역할을 수행합니다. 이런 동작 방식 때문에 `new Function` 문법으로 만든 함수 내부에서 외부 변수에 접근하려고 하면 `userName`은 이미 이름이 변경되었기 때문에 찾을 수 없게 됩니다. @@ -111,7 +106,7 @@ For instance, if a function has `let userName`, minifier replaces it with `let a 문법: ```js -let func = new Function ([arg1, arg2, ...argN], functionBody); +let func = new Function([arg1, arg2, ...argN], functionBody); ``` 인수를 한꺼번에 모아(쉼표로 구분) 전달할 수도 있습니다. @@ -119,9 +114,9 @@ let func = new Function ([arg1, arg2, ...argN], functionBody); 아래 세 선언 방식은 동일하게 동작하죠. ```js -new Function('a', 'b', 'return a + b'); // 기본 문법 -new Function('a,b', 'return a + b'); // 쉼표로 구분 -new Function('a , b', 'return a + b'); // 쉼표와 공백으로 구분 +new Function("a", "b", "return a + b"); // 기본 문법 +new Function("a,b", "return a + b"); // 쉼표로 구분 +new Function("a , b", "return a + b"); // 쉼표와 공백으로 구분 ``` `new Function`을 이용해 만든 함수의 `[[Environment]]`는 외부 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하므로 외부 변수를 사용할 수 없습니다. 단점 같아 보이는 특징이긴 하지만 에러를 예방해 준다는 관점에선 장점이 되기도 합니다. 구조상으론 매개변수를 사용해 값을 받는 게 더 낫습니다. 압축기에 의한 에러도 방지할 수 있죠. From 95b3d226c25e71567f31ef95818796cf3c64bd23 Mon Sep 17 00:00:00 2001 From: meanzzi Date: Fri, 15 May 2026 20:17:31 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[=EC=B6=A9=EB=8F=8C=ED=95=B4=EA=B2=B0]=20Pa?= =?UTF-8?q?rt1=206.8=20=ED=98=B8=EC=B6=9C=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../08-settimeout-setinterval/article.md | 89 ++++++------------- 1 file changed, 25 insertions(+), 64 deletions(-) diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md index 4a89ab268..b311a5a49 100644 --- a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md @@ -27,11 +27,7 @@ let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...) : 실행 전 대기 시간으로, 단위는 밀리초(millisecond, 1000밀리초 = 1초)이며 기본값은 0입니다. `arg1`, `arg2`... -<<<<<<< HEAD -: 함수에 전달할 인수들로, IE9 이하에선 지원하지 않습니다. -======= -: Arguments for the function ->>>>>>> upstream/master +: 함수에 전달할 인수입니다. 예시를 통해 `setTimeout`을 어떻게 쓸 수 있는지 알아봅시다. 아래 코드를 실행하면 1초 후에 `sayHi()`가 호출됩니다. @@ -68,7 +64,7 @@ setTimeout("alert('안녕하세요.')", 1000); 그런데 이렇게 문자열을 사용하는 방법은 추천하지 않습니다. 되도록 다음 예시와 같이 익명 화살표 함수를 사용하세요. ```js run no-beautify -setTimeout(() => alert('안녕하세요.'), 1000); +setTimeout(() => alert("안녕하세요."), 1000); ``` ````smart header="함수를 실행하지 말고 넘기세요." @@ -106,11 +102,7 @@ alert(timerId); // 위 타이머 식별자와 동일함 (취소 후에도 식별 다시 한번 말씀드리자면, 스케줄링에 관한 명세는 따로 존재하지 않습니다. 명세가 없기 때문에 호스트 환경마다 약간의 차이가 있을 수밖에 없습니다. -<<<<<<< HEAD -참고로 브라우저는 HTML5의 [timers section](https://www.w3.org/TR/html5/webappapis.html#timers)을 준수하고 있습니다. -======= -For browsers, timers are described in the [timers section](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) of HTML Living Standard. ->>>>>>> upstream/master +참고로 브라우저는 HTML Living Standard의 [timers section](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers)을 준수하고 있습니다. ## setInterval @@ -128,21 +120,21 @@ let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...) ```js run // 2초 간격으로 메시지를 보여줌 -let timerId = setInterval(() => alert('째깍'), 2000); +let timerId = setInterval(() => alert("째깍"), 2000); // 5초 후에 정지 -setTimeout(() => { clearInterval(timerId); alert('정지'); }, 5000); +setTimeout(() => { + clearInterval(timerId); + alert("정지"); +}, 5000); ``` -```smart header="`alert` 창이 떠 있더라도 타이머는 멈추지 않습니다." -Chrome과 Firefox를 포함한 대부분의 브라우저는 `alert/confirm/prompt` 창이 떠 있는 동안에도 내부 타이머를 멈추지 않습니다. +```smart header="`alert`창이 떠 있더라도 타이머는 멈추지 않습니다." +Chrome과 Firefox를 포함한 대부분의 브라우저는`alert/confirm/prompt` 창이 떠 있는 동안에도 내부 타이머를 멈추지 않습니다. -<<<<<<< HEAD 위 예시를 실행하고 첫 번째 `alert` 창이 떴을 때 몇 초간 기다렸다가 창을 닫으면, 두 번째 `alert` 창이 바로 나타나는 것을 보고 이를 확인할 수 있습니다. 이런 이유로 얼럿 창은 명시한 지연 시간인 2초보다 더 짧은 간격으로 뜨게 됩니다. -======= -So if you run the code above and don't dismiss the `alert` window for some time, then the next `alert` will be shown immediately as you do it. The actual interval between alerts will be shorter than 2 seconds. ->>>>>>> upstream/master -``` + +```` ## 중첩 setTimeout @@ -161,7 +153,7 @@ let timerId = setTimeout(function tick() { timerId = setTimeout(tick, 2000); // (*) */!* }, 2000); -``` +```` 다섯 번째 줄의 `setTimeout`은 `(*)`로 표시한 줄의 실행이 종료되면 다음 호출을 스케줄링합니다. @@ -170,6 +162,7 @@ let timerId = setTimeout(function tick() { 5초 간격으로 서버에 요청을 보내 데이터를 얻는다고 가정해 봅시다. 서버가 과부하 상태라면 요청 간격을 10초, 20초, 40초 등으로 증가시켜주는 게 좋을 겁니다. 아래는 이를 구현한 의사 코드입니다. + ```js let delay = 5000; @@ -186,7 +179,6 @@ let timerId = setTimeout(function request() { }, delay); ``` - CPU 소모가 많은 작업을 주기적으로 실행하는 경우에도 `setTimeout`을 재귀 실행하는 방법이 유용합니다. 작업에 걸리는 시간에 따라 다음 작업을 유동적으로 계획할 수 있기 때문입니다. **중첩 `setTimeout`을 이용하는 방법은 지연 간격을 보장하지만 `setInterval`은 이를 보장하지 않습니다.** @@ -195,7 +187,7 @@ CPU 소모가 많은 작업을 주기적으로 실행하는 경우에도 `setTim ```js let i = 1; -setInterval(function() { +setInterval(function () { func(i++); }, 100); ``` @@ -222,7 +214,7 @@ setTimeout(function run() { 그렇다면 `func`을 실행하는 데 걸리는 시간이 명시한 지연 간격보다 길 때 어떤 일이 발생할까요? -이런 경우는 엔진이 `func`의 실행이 종료될 때까지 기다려줍니다. `func`의 실행이 종료되면 엔진은 스케줄러를 확인하고, 지연 시간이 지났으면 다음 호출을 *바로* 시작합니다. +이런 경우는 엔진이 `func`의 실행이 종료될 때까지 기다려줍니다. `func`의 실행이 종료되면 엔진은 스케줄러를 확인하고, 지연 시간이 지났으면 다음 호출을 _바로_ 시작합니다. 따라서 함수 호출에 걸리는 시간이 매번 `delay` 밀리초보다 길면, 모든 함수가 쉼 없이 계속 연속 호출됩니다. @@ -230,11 +222,7 @@ setTimeout(function run() { ![](settimeout-interval.svg) -<<<<<<< HEAD -**중첩 `setTimeout`을 사용하면 명시한 지연(여기서는 100ms)이 보장됩니다.** -======= -**The nested `setTimeout` ensures a minimum delay (100ms here) between the end of one call and the beginning of the subsequent one.** ->>>>>>> upstream/master +**중첩 `setTimeout`을 사용하면 이전 호출이 끝난 후 다음 호출이 시작되기까지 명시한 지연(여기서는 100ms)이 보장됩니다.** 이렇게 지연 간격이 보장되는 이유는 이전 함수의 실행이 종료된 이후에 다음 함수 호출에 대한 계획이 세워지기 때문입니다. @@ -248,11 +236,7 @@ setTimeout(function() {...}, 100); `setInterval`의 경우는, `clearInterval`이 호출되기 전까지 함수에 대한 참조가 메모리에 유지됩니다. -<<<<<<< HEAD 그런데 이런 동작 방식에는 부작용이 하나 있습니다. 외부 렉시컬 환경을 참조하는 함수가 있다고 가정해 봅시다. 이 함수가 메모리에 남아있는 동안엔 외부 변수 역시 메모리에 남아있기 마련입니다. 그런데 이렇게 되면 실제 함수가 차지했어야 하는 공간보다 더 많은 메모리 공간이 사용됩니다. 이런 부작용을 방지하고 싶다면 스케줄링할 필요가 없어진 함수는 아무리 작더라도 취소하도록 합시다. -======= -There's a side effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function anymore, it's better to cancel it, even if it's very small. ->>>>>>> upstream/master ```` ## 대기 시간이 0인 setTimeout @@ -275,13 +259,8 @@ alert("Hello"); 대기 시간이 0인 setTimeout을 활용한 브라우저 환경에서의 유스 케이스는 에서 자세히 다루도록 하겠습니다. -<<<<<<< HEAD -````smart header="브라우저 환경에서 실제 대기 시간은 0이 아닙니다." -브라우저는 [HTML5 표준](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers)에서 정한 중첩 타이머 실행 간격 관련 제약을 준수합니다. 해당 표준엔 "다섯 번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다."라는 제약이 명시되어있습니다. -======= -````smart header="Zero delay is in fact not zero (in a browser)" -In the browser, there's a limitation of how often nested timers can run. The [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) says: "after five nested timers, the interval is forced to be at least 4 milliseconds.". ->>>>>>> upstream/master +````smart header="실제로 지연시간이 0인 경우는 없습니다(브라우저 환경)." +브라우저는 [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers)에서 정한 중첩 타이머 실행 간격 관련 제약을 준수합니다. 해당 표준엔 "다섯 번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다."라는 제약이 명시되어 있습니다. 예시를 보며 이 제약 사항을 이해해봅시다. 예시 내 `setTimeout`은 지연 없이 함수 run을 다시 호출할 수 있게 스케줄링 되어 있습니다. 배열 `times`에는 실제 지연 간격에 대한 정보가 기록되도록 해놓았는데, 배열 times에 어떤 값이 저장되는지 알아봅시다. @@ -306,41 +285,23 @@ setTimeout(function run() { 이는 오래전부터 있던 제약인데, 구식 스크립트 중 일부는 아직 이 제약에 의존하는 경우가 있어서 명세서를 변경하지 못하고 있는 상황입니다. -<<<<<<< HEAD -한편, 서버 측엔 이런 제약이 없습니다. Node.js의 [process.nextTick](https://nodejs.org/api/process.html)과 [setImmediate](https://nodejs.org/api/timers.html)를 이용하면 비동기 작업을 지연 없이 실행할 수 있습니다. 위에서 언급된 제약은 브라우저에 한정됩니다. -======= -For server-side JavaScript, that limitation does not exist, and there exist other ways to schedule an immediate asynchronous job, like [setImmediate](https://nodejs.org/api/timers.html#timers_setimmediate_callback_args) for Node.js. So this note is browser-specific. ->>>>>>> upstream/master +한편, 서버 측엔 이런 제약이 없습니다. Node.js의 [setImmediate](https://nodejs.org/api/timers.html#timers_setimmediate_callback_args)와 같은 방법으로 비동기 작업을 즉시 실행할 수 있습니다. 위에서 언급된 제약은 브라우저에 한정합니다. ```` ## 요약 -<<<<<<< HEAD - `setInterval(func, delay, ...args)`과 `setTimeout(func, delay, ...args)`은 `delay`밀리초 후에 `func`을 규칙적으로, 또는 한번 실행하도록 해줍니다. -- `setInterval·setTimeout`을 호출하고 반환받은 값을 `clearInterval·clearTimeout`에 넘겨주면 스케줄링을 취소할 수 있습니다. -- 중첩 `setTimeout`을 사용하면 `setInterval`을 사용한 것보다 유연하게 코드를 작성할 수 있습니다. 여기에 더하여 *지연 간격* 보장이라는 장점도 있습니다. +- `setTimeout·setInterval`을 호출하고 반환받은 값을 `clearTimeout·clearInterval`에 넘겨주면 스케줄링을 취소할 수 있습니다. +- 중첩 `setTimeout`을 사용하면 `setInterval`을 사용한 것보다 유연하게 코드를 작성할 수 있습니다. 여기에 더하여 실행 _사이의_ 지연 간격을 더 정확하게 조절할 수 있게 해줍니다. - 대기 시간이 0인 setTimeout(`setTimeout(func, 0)` 혹은 `setTimeout(func)`)을 사용하면 '현재 스크립트의 실행이 완료된 후 가능한 한 빠르게' 원하는 함수를 호출할 수 있습니다. - 지연 없이 중첩 `setTimeout`을 5회 이상 호출하거나 지연 없는 `setInterval`에서 호출이 5회 이상 이뤄지면, 4밀리초 이상의 지연 간격이 강제로 더해집니다. 이는 브라우저에만 적용되는 사항이며, 하위 호환성을 위해 유지되고 있습니다. -======= -- Methods `setTimeout(func, delay, ...args)` and `setInterval(func, delay, ...args)` allow us to run the `func` once/regularly after `delay` milliseconds. -- To cancel the execution, we should call `clearTimeout/clearInterval` with the value returned by `setTimeout/setInterval`. -- Nested `setTimeout` calls are a more flexible alternative to `setInterval`, allowing us to set the time *between* executions more precisely. -- Zero delay scheduling with `setTimeout(func, 0)` (the same as `setTimeout(func)`) is used to schedule the call "as soon as possible, but after the current script is complete". -- The browser limits the minimal delay for five or more nested calls of `setTimeout` or for `setInterval` (after 5th call) to 4ms. That's for historical reasons. ->>>>>>> upstream/master 스케줄링 메서드를 사용할 땐 명시한 지연 간격이 *보장*되지 않을 수도 있다는 점에 유의해야 합니다. -<<<<<<< HEAD -아래와 같은 상황에서 브라우저 내 타이머가 느려지면 지연 간격이 보장되지 않습니다. +아래와 같은 여러 이유로 브라우저 내 타이머가 느려질 수 있습니다. + - CPU가 과부하 상태인 경우 - 브라우저 탭이 백그라운드 모드인 경우 -- 노트북이 배터리에 의존해서 구동 중인 경우 -======= -For example, the in-browser timer may slow down for a lot of reasons: -- The CPU is overloaded. -- The browser tab is in the background mode. -- The laptop is on battery saving mode. ->>>>>>> upstream/master +- 노트북이 배터리 절약 모드인 경우 이런 상황에서 타이머의 최소 지연 시간은 300밀리초에서 심하면 1,000밀리초까지 늘어납니다. 연장 시간은 브라우저나 구동 중인 운영 체제의 성능 설정에 따라 다릅니다. From dffd4870fa2860bd638e6edae73defc5e370aeb6 Mon Sep 17 00:00:00 2001 From: meanzzi Date: Fri, 15 May 2026 20:31:02 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[=EC=B6=A9=EB=8F=8C=ED=95=B4=EA=B2=B0]=20Pa?= =?UTF-8?q?rt1=206.9=20call/apply=EC=99=80=20=EB=8D=B0=EC=BD=94=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=ED=84=B0,=20=ED=8F=AC=EC=9B=8C=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../09-call-apply-decorators/article.md | 86 +++++++------------ 1 file changed, 32 insertions(+), 54 deletions(-) diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md index 8ed453772..2acc7b1f0 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md @@ -1,6 +1,6 @@ # call/apply와 데코레이터, 포워딩 -자바스크립트는 함수를 다룰 때 탁월한 유연성을 제공합니다. 함수는 이곳저곳 전달될 수 있고, 객체로도 사용될 수 있습니다. 이번 챕터에선 함수 간에 호출을 어떻게 *포워딩(forwarding)* 하는지, 함수를 어떻게 *데코레이팅(decorating)* 하는지에 대해 알아보겠습니다. +자바스크립트는 함수를 다룰 때 탁월한 유연성을 제공합니다. 함수는 이곳저곳 전달될 수 있고, 객체로도 사용될 수 있습니다. 이번 챕터에선 함수 간에 호출을 어떻게 _포워딩(forwarding)_ 하는지, 함수를 어떻게 _데코레이팅(decorating)_ 하는지에 대해 알아보겠습니다. ## 코드 변경 없이 캐싱 기능 추가하기 @@ -22,40 +22,33 @@ function slow(x) { function cachingDecorator(func) { let cache = new Map(); - return function(x) { - if (cache.has(x)) { // cache에 해당 키가 있으면 + return function (x) { + if (cache.has(x)) { + // cache에 해당 키가 있으면 return cache.get(x); // 대응하는 값을 cache에서 읽어옵니다. } - let result = func(x); // 그렇지 않은 경우엔 func를 호출하고, + let result = func(x); // 그렇지 않은 경우엔 func를 호출하고, - cache.set(x, result); // 그 결과를 캐싱(저장)합니다. + cache.set(x, result); // 그 결과를 캐싱(저장)합니다. return result; }; } slow = cachingDecorator(slow); -<<<<<<< HEAD -alert( slow(1) ); // slow(1)이 저장되었습니다. -alert( "다시 호출: " + slow(1) ); // 동일한 결과 +alert(slow(1)); // slow(1)를 캐시에 저장하고 결과를 반환합니다. +alert("다시 호출: " + slow(1)); // 캐시에서 slow(1)의 결과를 가져옵니다. -alert( slow(2) ); // slow(2)가 저장되었습니다. -alert( "다시 호출: " + slow(2) ); // 윗줄과 동일한 결과 -======= -alert( slow(1) ); // slow(1) is cached and the result returned -alert( "Again: " + slow(1) ); // slow(1) result returned from cache - -alert( slow(2) ); // slow(2) is cached and the result returned -alert( "Again: " + slow(2) ); // slow(2) result returned from cache ->>>>>>> upstream/master +alert(slow(2)); // slow(2)를 캐시에 저장하고 결과를 반환합니다. +alert("다시 호출: " + slow(2)); // 캐시에서 slow(2)의 결과를 가져옵니다. ``` -`cachingDecorator`같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 *데코레이터(decorator)* 라고 부릅니다. +`cachingDecorator`같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 _데코레이터(decorator)_ 라고 부릅니다. 모든 함수를 대상으로 `cachingDecorator`를 호출 할 수 있는데, 이때 반환되는 것은 캐싱 래퍼입니다. 함수에 `cachingDecorator`를 적용하기만 하면 캐싱이 가능한 함수를 원하는 만큼 구현할 수 있기 때문에 데코레이터 함수는 아주 유용하게 사용됩니다. -캐싱 관련 코드를 함수 코드와 분리할 수 있기 때문에 함수의 코드가 간결해진다는 장점도 있습니다. +캐싱 관련 코드를 함수 코드와 분리할 수 있기 때문에 함수의 코드가 간결해진다는 장점도 있습니다. 아래 그림에서 볼 수 있듯이 `cachingDecorator(func)`를 호출하면 '래퍼(wrapper)', `function(x)`이 반환됩니다. 래퍼 `function(x)`는 `func(x)`의 호출 결과를 캐싱 로직으로 감쌉니다(wrapping). @@ -66,7 +59,7 @@ alert( "Again: " + slow(2) ); // slow(2) result returned from cache `slow` 본문을 수정하는 것 보다 독립된 래퍼 함수 `cachingDecorator`를 사용할 때 생기는 이점을 정리하면 다음과 같습니다. - `cachingDecorator`를 재사용 할 수 있습니다. 원하는 함수 어디에든 `cachingDecorator`를 적용할 수 있습니다. -- 캐싱 로직이 분리되어 `slow` 자체의 복잡성이 증가하지 않습니다. +- 캐싱 로직이 분리되어 `slow` 자체의 복잡성이 증가하지 않습니다. - 필요하다면 여러 개의 데코레이터를 조합해서 사용할 수도 있습니다(추가 데코레이터는 `cachingDecorator` 뒤를 따릅니다). ## 'func.call'를 사용해 컨텍스트 지정하기 @@ -139,9 +132,10 @@ func.call(context, arg1, arg2, ...) 메서드를 호출하면 메서드의 첫 번째 인수가 `this`, 이어지는 인수가 `func`의 인수가 된 후, `func`이 호출됩니다. 아래 함수와 메서드를 호출하면 거의 동일한 일이 발생합니다. + ```js func(1, 2, 3); -func.call(obj, 1, 2, 3) +func.call(obj, 1, 2, 3); ``` 둘 다 인수로 `1`, `2`, `3`을 받죠. 유일한 차이점은 `func.call`에선 `this`가 `obj`로 고정된다는 점입니다. @@ -157,22 +151,21 @@ let user = { name: "John" }; let admin = { name: "Admin" }; // call을 사용해 원하는 객체가 'this'가 되도록 합니다. -sayHi.call( user ); // this = John -sayHi.call( admin ); // this = Admin +sayHi.call(user); // this = John +sayHi.call(admin); // this = Admin ``` 아래 예시에선 `call`을 사용해 컨텍스트와 `phrase`에 원하는 값을 지정해 보았습니다. - ```js run function say(phrase) { - alert(this.name + ': ' + phrase); + alert(this.name + ": " + phrase); } let user = { name: "John" }; // this엔 user가 고정되고, "Hello"는 메서드의 첫 번째 인수가 됩니다. -say.call( user, "Hello" ); // John: Hello +say.call(user, "Hello"); // John: Hello ``` 래퍼 안에서 `call`을 사용해 컨텍스트를 원본 함수로 전달하면 에러가 발생하지 않습니다. @@ -227,7 +220,7 @@ alert( worker.slow(2) ); // 제대로 동작합니다. 다만, 원본 함수가 let worker = { slow(min, max) { return min + max; // CPU를 아주 많이 쓰는 작업이라고 가정 - } + }, }; // 동일한 인수를 전달했을 때 호출 결과를 기억할 수 있어야 합니다. @@ -240,7 +233,7 @@ worker.slow = cachingDecorator(worker.slow); 1. 복수 키를 지원하는 맵과 유사한 자료 구조 구현하기(서드 파티 라이브러리 등을 사용해도 됨) 2. 중첩 맵을 사용하기. `(max, result)` 쌍 저장은 `cache.set(min)`으로, `result`는 `cache.get(min).get(max)`을 사용해 얻습니다. -3. 두 값을 하나로 합치기. `맵`의 키로 문자열 `"min,max"`를 사용합니다. 여러 값을 하나로 합치는 코드는 *해싱 함수(hashing function)* 에 구현해 유연성을 높입니다. +3. 두 값을 하나로 합치기. `맵`의 키로 문자열 `"min,max"`를 사용합니다. 여러 값을 하나로 합치는 코드는 _해싱 함수(hashing function)_ 에 구현해 유연성을 높입니다. 세 번째 방법만으로 충분하기 때문에 이 방법을 사용해 코드를 수정해 보겠습니다. @@ -299,7 +292,7 @@ alert( "다시 호출: " + worker.slow(3, 5) ); // 동일한 결과 출력(캐 내장 메서드 [func.apply](mdn:js/Function/apply)의 문법은 다음과 같습니다. ```js -func.apply(context, args) +func.apply(context, args); ``` `apply`는 `func`의 `this`를 `context`로 고정해주고, 유사 배열 객체인 `args`를 인수로 사용할 수 있게 해줍니다. @@ -309,39 +302,24 @@ func.apply(context, args) 따라서 아래 코드 두 줄은 거의 같은 역할을 합니다. ```js -<<<<<<< HEAD -func.call(context, ...args); // 전개 구문을 사용해 인수가 담긴 배열을 전달하는 것과 -func.apply(context, args); // call을 사용하는 것은 동일합니다. -``` - -그런데 약간의 차이가 있긴 합니다. -======= func.call(context, ...args); func.apply(context, args); ``` -They perform the same call of `func` with given context and arguments. +위 코드는 `func`을 동일한 컨텍스트와 인수로 호출합니다. +그런데 `args`에 관해 약간의 차이가 있긴 합니다. -There's only a subtle difference regarding `args`: ->>>>>>> upstream/master - -- 전개 구문 `...`은 *이터러블* `args`을 분해 해 `call`에 전달할 수 있도록 해줍니다. -- `apply`는 오직 *유사 배열* 형태의 `args`만 받습니다. - -<<<<<<< HEAD -이 차이만 빼면 두 메서드는 완전히 동일하게 동작합니다. 인수가 이터러블 형태라면 `call`을, 유사 배열 형태라면 `apply`를 사용하면 됩니다. +- 전개 구문 `...`은 _이터러블_ `args`을 분해 해 `call`에 전달할 수 있도록 해줍니다. +- `apply`는 오직 _유사 배열_ 형태의 `args`만 받습니다. 배열같이 이터러블이면서 유사 배열인 객체엔 둘 다를 사용할 수 있는데, 대부분의 자바스크립트 엔진은 내부에서 `apply`를 최적화 하기 때문에 `apply`를 사용하는 게 좀 더 빠르긴 합니다. -======= -...And for objects that are both iterable and array-like, such as a real array, we can use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better. ->>>>>>> upstream/master -이렇게 컨텍스트와 함께 인수 전체를 다른 함수에 전달하는 것을 *콜 포워딩(call forwarding)* 이라고 합니다. +이렇게 컨텍스트와 함께 인수 전체를 다른 함수에 전달하는 것을 _콜 포워딩(call forwarding)_ 이라고 합니다. 가장 간단한 형태의 콜 포워딩은 다음과 같습니다. ```js -let wrapper = function() { +let wrapper = function () { return func.apply(this, arguments); }; ``` @@ -354,11 +332,11 @@ let wrapper = function() { ```js function hash(args) { - return args[0] + ',' + args[1]; + return args[0] + "," + args[1]; } ``` -지금 상태에선 인수 두 개만 다룰 수 있습니다. `args`의 요소 개수에 상관없이 요소들을 합칠 수 있으면 더 좋겠네요. +지금 상태에선 인수 두 개만 다룰 수 있습니다. `args`의 요소 개수에 상관없이 요소들을 합칠 수 있으면 더 좋겠네요. 가장 자연스러운 해결책은 배열 메서드 [arr.join](mdn:js/Array/join)을 사용하는 것입니다. @@ -394,7 +372,7 @@ function hash() { hash(1, 2); ``` -The trick is called *method borrowing*. +The trick is called _method borrowing_. 일반 배열에서 `join` 메서드를 빌려오고(`[].join`), `[].join.call`를 사용해 `arguments`를 컨텍스트로 고정한 후 `join`메서드를 호출하는 것이죠. @@ -438,7 +416,7 @@ The trick is called *method borrowing*. *콜 포워딩*은 대개 `apply`를 사용해 구현합니다. ```js -let wrapper = function() { +let wrapper = function () { return original.apply(this, arguments); }; ``` From 69a8e3e4f19707816d8e59d3ee772ff3681080d8 Mon Sep 17 00:00:00 2001 From: meanzzi Date: Sun, 17 May 2026 00:34:43 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[=EC=B6=A9=EB=8F=8C=ED=95=B4=EA=B2=B0]=20Pa?= =?UTF-8?q?rt1=206.10=20=ED=95=A8=EC=88=98=20=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../10-bind/5-question-use-bind/solution.md | 13 ++-- 1-js/06-advanced-functions/10-bind/article.md | 66 ++++++++----------- 2 files changed, 33 insertions(+), 46 deletions(-) diff --git a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md index c5e53cb14..05ea816a4 100644 --- a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md +++ b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md @@ -1,9 +1,4 @@ - -<<<<<<< HEAD -에러는 `ask`가 함수 `loginOk`, `loginFail`을 객체 없이 가지고 오기 때문에 발생합니다. -======= -The error occurs because `askPassword` gets functions `loginOk/loginFail` without the object. ->>>>>>> upstream/master +에러는 `askPassword`가 함수 `loginOk`, `loginFail`을 객체 없이 가지고 오기 때문에 발생합니다. ask는 `loginOk`, `loginFail`을 호출할 때 `this=undefined`라고 자연스레 가정합니다. @@ -37,9 +32,13 @@ askPassword(user.loginOk.bind(user), user.loginFail.bind(user)); 이제 잘 동작합니다. 이 외에도 다른 답이 있는데, 아래에서 확인 가능합니다. + ```js //... -askPassword(() => user.loginOk(), () => user.loginFail()); +askPassword( + () => user.loginOk(), + () => user.loginFail(), +); ``` 이렇게 화살표 함수를 사용하는 방법 또한 대개 잘 동작하며 가독성도 좋습니다. diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index 9f56d635a..08d21525a 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -1,5 +1,6 @@ libs: - - lodash + +- lodash --- @@ -30,7 +31,7 @@ setTimeout(user.sayHi, 1000); // Hello, undefined! `this.firstName`이 "John"이 되어야 하는데, 얼럿창엔 `undefined`가 출력됩니다. -이렇게 된 이유는 `setTimeout`에 객체에서 분리된 함수인 `user.sayHi`가 전달되기 때문입니다. 위 예시의 마지막 줄은 다음 코드와 같습니다. +이렇게 된 이유는 `setTimeout`에 객체에서 분리된 함수인 `user.sayHi`가 전달되기 때문입니다. 위 예시의 마지막 줄은 다음 코드와 같습니다. ```js let f = user.sayHi; @@ -72,19 +73,22 @@ setTimeout(() => user.sayHi(), 1000); // Hello, John! `setTimeout`이 트리거 되기 전에(1초가 지나기 전에) `user`가 변경되면, 변경된 객체의 메서드를 호출하게 됩니다. - ```js run let user = { firstName: "John", sayHi() { alert(`Hello, ${this.firstName}!`); - } + }, }; setTimeout(() => user.sayHi(), 1000); // 1초가 지나기 전에 user의 값이 바뀜 -user = { sayHi() { alert("또 다른 사용자!"); } }; +user = { + sayHi() { + alert("또 다른 사용자!"); + }, +}; // setTimeout에 또 다른 사용자! ``` @@ -104,11 +108,11 @@ let boundFunc = func.bind(context); `func.bind(context)`는 함수처럼 호출 가능한 '특수 객체(exotic object)'를 반환합니다. 이 객체를 호출하면 `this`가 `context`로 고정된 함수 `func`가 반환됩니다. -따라서 `boundFunc`를 호출하면 `this`가 고정된 `func`를 호출하는 것과 동일한 효과를 봅니다. +따라서 `boundFunc`를 호출하면 `this`가 고정된 `func`를 호출하는 것과 동일한 효과를 봅니다. 아래 `funcUser`에는 `this`가 `user`로 고정된 `func`이 할당됩니다. -```js run +```js run let user = { firstName: "John" }; @@ -119,19 +123,15 @@ function func() { *!* let funcUser = func.bind(user); -funcUser(); // John +funcUser(); // John */!* ``` -<<<<<<< HEAD -여기서 `func.bind(user)`는 `func`의 `this`를 `user`로 '바인딩한 변형'이라고 생각하시면 됩니다. -======= -Here `func.bind(user)` is a "bound variant" of `func`, with fixed `this=user`. ->>>>>>> upstream/master +여기서 `func.bind(user)`는 `this=user`로 고정된 `func`의 '바인딩한 변형'이라고 생각하면 됩니다. 인수는 원본 함수 `func`에 '그대로' 전달됩니다. -```js run +```js run let user = { firstName: "John" }; @@ -150,7 +150,6 @@ funcUser("Hello"); // Hello, John (인수 "Hello"가 넘겨지고 this는 user 이제 객체 메서드에 `bind`를 적용해 봅시다. - ```js run let user = { firstName: "John", @@ -177,25 +176,20 @@ user = { `(*)`로 표시한 줄에서 메서드 `user.sayHi`를 가져오고, 메서드에 `user`를 바인딩합니다. `sayHi`는 이제 '묶인(bound)' 함수가 되어 단독으로 호출할 수 있고 `setTimeout`에 전달하여 호출할 수도 있습니다. 어떤 방식이든 컨택스트는 원하는 대로 고정됩니다. -아래 예시를 실행하면 인수는 '그대로' 전달되고 `bind`에 의해 `this`만 고정된 것을 확인할 수 있습니다. +아래 예시를 실행하면 인수는 '그대로' 전달되고 `bind`에 의해 `this`만 고정된 것을 확인할 수 있습니다. ```js run let user = { firstName: "John", say(phrase) { alert(`${phrase}, ${this.firstName}!`); - } + }, }; let say = user.say.bind(user); -<<<<<<< HEAD -say("Hello"); // Hello, John (인수 "Hello"가 say로 전달되었습니다.) -say("Bye"); // Bye, John ("Bye"가 say로 전달되었습니다.) -======= -say("Hello"); // Hello, John! ("Hello" argument is passed to say) -say("Bye"); // Bye, John! ("Bye" is passed to say) ->>>>>>> upstream/master +say("Hello"); // Hello, John! (인수 "Hello"를 say에 전달합니다.) +say("Bye"); // Bye, John! ("Bye"를 say에 전달합니다.) ``` ````smart header="`bindAll`로 메서드 전체 바인딩하기" @@ -203,24 +197,21 @@ say("Bye"); // Bye, John! ("Bye" is passed to say) ```js for (let key in user) { - if (typeof user[key] == 'function') { + if (typeof user[key] == "function") { user[key] = user[key].bind(user); } } ``` -<<<<<<< HEAD -자바스크립트 라이브러리를 사용해도 대규모 바인딩을 할 수 있습니다. lodash 라이브러리의 [_.bindAll(object, methodNames)](http://lodash.com/docs#bindAll)이 그 예입니다. -======= -JavaScript libraries also provide functions for convenient mass binding , e.g. [_.bindAll(object, methodNames)](https://lodash.com/docs#bindAll) in lodash. ->>>>>>> upstream/master +자바스크립트 라이브러리를 사용해도 대규모 바인딩을 할 수 있습니다. lodash 라이브러리의 [\_.bindAll(object, methodNames)](https://lodash.com/docs#bindAll)이 그 예입니다. + ```` ## 부분 적용 지금까진 `this` 바인딩에 대해서만 이야기해보았습니다. 한 단계 더 나아가 봅시다. -`this` 뿐만 아니라 인수도 바인딩이 가능합니다. 인수 바인딩은 잘 쓰이진 않지만 가끔 유용할 때가 있습니다. +`this` 뿐만 아니라 인수도 바인딩이 가능합니다. 인수 바인딩은 잘 쓰이진 않지만 가끔 유용할 때가 있습니다. `bind`의 전체 문법은 다음과 같습니다. @@ -258,11 +249,7 @@ alert( double(5) ); // = mul(2, 5) = 10 이런 방식을 [부분 적용(partial application)](https://en.wikipedia.org/wiki/Partial_application)이라고 부릅니다. 부분 적용을 사용하면 기존 함수의 매개변수를 고정하여 새로운 함수를 만들 수 있습니다. -<<<<<<< HEAD -위 예시에선 `this`를 사용하지 않았다는 점에 주목하시기 바랍니다. `bind`엔 컨텍스트를 항상 넘겨줘야 하므로 `null`을 사용했습니다. -======= -Please note that we actually don't use `this` here. But `bind` requires it, so we must put in something like `null`. ->>>>>>> upstream/master +위 예시에선 `this`를 사용하지 않았다는 점에 주목합니다. 그런데 `bind`는 컨텍스트가 필요하므로 `null` 같은 값을 넣어줘야 합니다. 부분 적용을 사용해 3을 곱해주는 함수 `triple`을 만들어보겠습니다. @@ -294,7 +281,7 @@ alert( triple(5) ); // = mul(3, 5) = 15 네이티브 `bind`만으로는 컨텍스트를 생략하고 인수로 바로 뛰어넘지 못합니다. -다행히도 인수만 바인딩해주는 헬퍼 함수 `partial`를 구현하는 건 쉽습니다. +다행히도 인수만 바인딩해주는 헬퍼 함수 `partial`를 구현하는 건 쉽습니다. 아래와 같이 말이죠. @@ -336,8 +323,9 @@ lodash 라이브러리의 [_.partial](https://lodash.com/docs#partial)을 사용 `func.bind(context, ...args)`는 `this`가 `context`로 고정되고 인수도 고정된 함수 `func`을 반환합니다. -`bind`는 보통 객체 메서드의 `this`를 고정해 어딘가에 넘기고자 할 때 사용합니다. `setTimeout`에 넘길 때 같이 말이죠. +`bind`는 보통 객체 메서드의 `this`를 고정해 어딘가에 넘기고자 할 때 사용합니다. `setTimeout`에 넘길 때 같이 말이죠. 기존 함수의 인수 몇 개를 고정한 함수를 *부분 적용(partially applied)* 함수 또는 *부분(partial)* 함수라고 부릅니다. -부분 적용은 같은 인수를 여러 번 반복하고 싶지 않을 때 유용합니다. `send(from, to)`라는 함수가 있는데 `from`을 고정하고 싶다면 `send(from, to)`의 부분 함수를 구현해 사용하면 됩니다. +부분 적용은 같은 인수를 여러 번 반복하고 싶지 않을 때 유용합니다. `send(from, to)`라는 함수가 있는데 `from`을 고정하고 싶다면 `send(from, to)`의 부분 함수를 구현해 사용하면 됩니다. +```` From 0eacf38525c9ead4fa8f3ba41cbcc635e3bf4bf7 Mon Sep 17 00:00:00 2001 From: meanzzi Date: Sun, 17 May 2026 00:36:42 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[=EC=B6=A9=EB=8F=8C=ED=95=B4=EA=B2=B0]=20Pa?= =?UTF-8?q?rt1=206.11=20=ED=99=94=EC=82=B4=ED=91=9C=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=20=EB=8B=A4=EC=8B=9C=20=EC=82=B4=ED=8E=B4=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../12-arrow-functions/article.md | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/1-js/06-advanced-functions/12-arrow-functions/article.md b/1-js/06-advanced-functions/12-arrow-functions/article.md index 5b1709cf0..48caa3f8b 100644 --- a/1-js/06-advanced-functions/12-arrow-functions/article.md +++ b/1-js/06-advanced-functions/12-arrow-functions/article.md @@ -51,13 +51,8 @@ let group = { showList() { *!* this.students.forEach(function(student) { -<<<<<<< HEAD - // TypeError: Cannot read property 'title' of undefined - alert(this.title + ': ' + student) -======= // Error: Cannot read property 'title' of undefined - alert(this.title + ': ' + student); ->>>>>>> upstream/master + alert(this.title + ': ' + student) }); */!* } @@ -72,14 +67,15 @@ group.showList(); ```warn header="화살표 함수는 `new`와 함께 실행할 수 없습니다." `this`가 없기 때문에 화살표 함수는 생성자 함수로 사용할 수 없다는 제약이 있습니다. 화살표 함수는 `new`와 함께 호출할 수 없습니다. -``` + +```` ```smart header="화살표 함수 vs. bind" 화살표 함수와 일반 함수를 `.bind(this)`를 사용해서 호출하는 것 사이에는 미묘한 차이가 있습니다. - `.bind(this)`는 함수의 '한정된 버전(bound version)'을 만듭니다. - 화살표 함수는 어떤 것도 바인딩시키지 않습니다. 화살표 함수엔 단지 `this`가 없을 뿐입니다. 화살표 함수에서 `this`를 사용하면 일반 변수 서칭과 마찬가지로 `this`의 값을 외부 렉시컬 환경에서 찾습니다. -``` +```` ## 화살표 함수엔 'arguments'가 없습니다 @@ -91,13 +87,13 @@ group.showList(); ```js run function defer(f, ms) { - return function() { + return function () { setTimeout(() => f.apply(this, arguments), ms); }; } function sayHi(who) { - alert('안녕, ' + who); + alert("안녕, " + who); } let sayHiDeferred = defer(sayHi, 2000); @@ -108,9 +104,9 @@ sayHiDeferred("철수"); // 2초 후 "안녕, 철수"가 출력됩니다. ```js function defer(f, ms) { - return function(...args) { + return function (...args) { let ctx = this; - setTimeout(function() { + setTimeout(function () { return f.apply(ctx, args); }, ms); }; From 9f33b9345e2efc07f7da8adf887cc2f896c88f10 Mon Sep 17 00:00:00 2001 From: meanzzi Date: Sun, 17 May 2026 02:00:50 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[=EC=B6=A9=EB=8F=8C=ED=95=B4=EA=B2=B0]=20Pa?= =?UTF-8?q?rt1=207.1=20=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=ED=94=8C?= =?UTF-8?q?=EB=9E=98=EA=B7=B8=EC=99=80=20=EC=84=A4=EB=AA=85=EC=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../01-property-descriptors/article.md | 168 +++++------------- 1 file changed, 45 insertions(+), 123 deletions(-) diff --git a/1-js/07-object-properties/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md index 11c94b67c..a7b304817 100644 --- a/1-js/07-object-properties/01-property-descriptors/article.md +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -1,15 +1,14 @@ - # 프로퍼티 플래그와 설명자 아시다시피 객체엔 프로퍼티가 저장됩니다. -지금까진 프로퍼티를 단순히 '키-값' 쌍의 관점에서만 다뤘습니다. 그런데 사실 프로퍼티는 우리가 생각했던 것보다 더 유연하고 강력한 자료구조입니다. +지금까진 프로퍼티를 단순히 '키-값' 쌍의 관점에서만 다뤘습니다. 그런데 사실 프로퍼티는 우리가 생각했던 것보다 더 유연하고 강력한 자료구조입니다. 이 챕터에선 객체 프로퍼티 추가 구성 옵션 몇 가지를 다루고, 이어지는 챕터에선 이 옵션들을 이용해 손쉽게 getter나 setter 함수를 만드는 법을 알아보겠습니다. ## 프로퍼티 플래그 -객체 프로퍼티는 **`값(value)`** 과 함께 플래그(flag)라 불리는 특별한 속성 세 가지를 갖습니다. +객체 프로퍼티는 **`값(value)`** 과 함께 플래그(flag)라 불리는 특별한 속성 세 가지를 갖습니다. - **`writable`** -- `true`이면 값을 수정할 수 있습니다. 그렇지 않다면 읽기만 가능합니다. - **`enumerable`** -- `true`이면 반복문을 사용해 나열할 수 있습니다. 그렇지 않다면 반복문을 사용해 나열할 수 없습니다. @@ -19,13 +18,10 @@ 자 이제 본격적으로 프로퍼티 플래그에 대해 다뤄봅시다. 먼저 플래그를 얻는 방법을 알아보겠습니다. -<<<<<<< HEAD -[Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor)메서드를 사용하면 특정 프로퍼티에 대한 정보를 *모두* 얻을 수 있습니다. -======= -The method [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) allows to query the *full* information about a property. ->>>>>>> upstream/master +[Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) 메서드를 사용하면 특정 프로퍼티에 대한 정보를 _모두_ 얻을 수 있습니다. 문법: + ```js let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); ``` @@ -42,12 +38,12 @@ let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); ```js run let user = { - name: "John" + name: "John", }; -let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); +let descriptor = Object.getOwnPropertyDescriptor(user, "name"); -alert( JSON.stringify(descriptor, null, 2 ) ); +alert(JSON.stringify(descriptor, null, 2)); /* property descriptor: { "value": "John", @@ -58,16 +54,12 @@ alert( JSON.stringify(descriptor, null, 2 ) ); */ ``` -<<<<<<< HEAD -메서드 [Object.defineProperty](mdn:js/Object/defineProperty)를 사용하면 플래그를 변경할 수 있습니다. -======= -To change the flags, we can use [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). ->>>>>>> upstream/master +메서드 [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)를 사용하면 플래그를 변경할 수 있습니다. 문법: ```js -Object.defineProperty(obj, propertyName, descriptor) +Object.defineProperty(obj, propertyName, descriptor); ``` `obj`, `propertyName` @@ -76,7 +68,7 @@ Object.defineProperty(obj, propertyName, descriptor) `descriptor` : 적용하고자 하는 프로퍼티 설명자 -`defineProperty`메서드는 객체에 해당 프로퍼티가 있으면 플래그를 원하는 대로 변경해줍니다. 프로퍼티가 없으면 인수로 넘겨받은 정보를 이용해 새로운 프로퍼티를 만듭니다. 이때 플래그 정보가 없으면 플래그 값은 자동으로 `false`가 됩니다. +`defineProperty`메서드는 객체에 해당 프로퍼티가 있으면 플래그를 원하는 대로 변경해줍니다. 프로퍼티가 없으면 인수로 넘겨받은 정보를 이용해 새로운 프로퍼티를 만듭니다. 이때 플래그 정보가 없으면 플래그 값은 자동으로 `false`가 됩니다. 아래 예시를 보면 프로퍼티 `name`이 새로 만들어지고, 모든 플래그 값이 `false`가 된 것을 확인할 수 있습니다. @@ -128,13 +120,8 @@ user.name = "Pete"; // Error: Cannot assign to read only property 'name' 이제 `defineProperty`를 사용해 `writable` 플래그를 `true`로 변경하지 않는 한 그 누구도 객체의 이름을 변경할 수 없게 되었습니다. -<<<<<<< HEAD ```smart header="에러는 엄격 모드에서만 발생합니다." -비 엄격 모드에선 읽기 전용 프로퍼티에 값을 쓰려고 해도 에러가 발생하지 않습니다. 다만 이때 값을 변경하는 것은 불가능합니다. 비 엄격 모드에선 이와 같이 플래그에서 정한 규칙을 위반하는 행위는 에러 없이 그냥 무시됩니다. -======= -```smart header="Errors appear only in strict mode" -In non-strict mode, no errors occur when writing to non-writable properties and such. But the operation still won't succeed. Flag-violating actions are just silently ignored in non-strict. ->>>>>>> upstream/master +비 엄격 모드에선 쓰기 불가 프로퍼티 등에 값을 써도 에러가 발생하지 않습니다. 다만 이때 값을 변경하는 것은 불가능합니다. 비 엄격 모드에선 이와 같이 플래그에서 정한 규칙을 위반하는 행위는 에러 없이 그냥 무시됩니다. ``` 아래 예시는 위 예시와 동일하게 동작합니다. 다만 아래 예시에선 `defineProperty` 메서드를 사용해 프로퍼티를 밑바닥부터 만들어 보았습니다. @@ -166,7 +153,7 @@ let user = { name: "John", toString() { return this.name; - } + }, }; //커스텀 toString은 for...in을 사용해 열거할 수 있습니다. @@ -205,18 +192,14 @@ alert(Object.keys(user)); // name 구성 가능하지 않음을 나타내는 플래그(non-configurable flag)인 `configurable:false`는 몇몇 내장 객체나 프로퍼티에 기본으로 설정되어있습니다. -<<<<<<< HEAD -어떤 프로퍼티의 `configurable` 플래그가 `false`로 설정되어 있다면 해당 프로퍼티는 객체에서 지울 수 없습니다. -======= -A non-configurable property can't be deleted, its attributes can't be modified. ->>>>>>> upstream/master +구성 가능하지 않은(non-configurable) 프로퍼티는 객체에서 삭제할 수 없고, 속성도 수정할 수 없습니다. 내장 객체 `Math`의 `PI` 프로퍼티가 대표적인 예입니다. 이 프로퍼티는 쓰기와 열거, 구성이 불가능합니다. ```js run -let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); +let descriptor = Object.getOwnPropertyDescriptor(Math, "PI"); -alert( JSON.stringify(descriptor, null, 2 ) ); +alert(JSON.stringify(descriptor, null, 2)); /* { "value": 3.141592653589793, @@ -226,6 +209,7 @@ alert( JSON.stringify(descriptor, null, 2 ) ); } */ ``` + 개발자가 코드를 사용해 `Math.PI` 값을 변경하거나 덮어쓰는 것도 불가능합니다. ```js run @@ -234,102 +218,70 @@ Math.PI = 3; // Error, because it has writable: false // 수정도 불가능하지만 지우는 것 역시 불가능합니다. ``` -<<<<<<< HEAD -`configurable` 플래그를 `false`로 설정하면 돌이킬 방법이 없습니다. `defineProperty`를 써도 값을 `true`로 되돌릴 수 없죠. - -`configurable:false`가 만들어내는 구체적인 제약사항은 아래와 같습니다. -1. `configurable` 플래그를 수정할 수 없음 -2. `enumerable` 플래그를 수정할 수 없음. -3. `writable: false`의 값을 `true`로 바꿀 수 없음(`true`를 `false`로 변경하는 것은 가능함). -4. 접근자 프로퍼티 `get/set`을 변경할 수 없음(새롭게 만드는 것은 가능함). - -이런 특징을 이용하면 아래와 같이 "영원히 변경할 수 없는" 프로퍼티(`user.name`)를 만들 수 있습니다. -======= -We also can't change `Math.PI` to be `writable` again: ->>>>>>> upstream/master +`Math.PI`를 다시 `writable`로 바꾸는 것도 불가능합니다. ```js run // Error, because of configurable: false Object.defineProperty(Math, "PI", { writable: true }); ``` -There's absolutely nothing we can do with `Math.PI`. +`Math.PI`로는 아무것도 할 수 없습니다. -Making a property non-configurable is a one-way road. We cannot change it back with `defineProperty`. +프로퍼티를 구성 가능하지 않은 상태로 만들면 되돌릴 수 없습니다. `defineProperty`를 사용해도 다시 구성 가능하게 만들 수 없습니다. -**Please note: `configurable: false` prevents changes of property flags and its deletion, while allowing to change its value.** +**참고: `configurable: false`는 프로퍼티 플래그 변경과 삭제를 막지만, 값 변경은 허용합니다.** -Here `user.name` is non-configurable, but we can still change it (as it's writable): +아래 예시에서 `user.name`은 구성 가능하지 않은 프로퍼티지만 writable이므로 값을 변경할 수 있습니다. ```js run let user = { - name: "John" + name: "John", }; Object.defineProperty(user, "name", { - configurable: false + configurable: false, }); -user.name = "Pete"; // works fine +user.name = "Pete"; // 동작합니다. delete user.name; // Error ``` -And here we make `user.name` a "forever sealed" constant, just like the built-in `Math.PI`: +아래 예시에서는 내장 객체 `Math.PI`처럼 `user.name`을 "변경할 수 없는" 상수로 만듭니다. ```js run let user = { - name: "John" + name: "John", }; Object.defineProperty(user, "name", { writable: false, - configurable: false + configurable: false, }); -<<<<<<< HEAD -*!* -// user.name 프로퍼티의 값이나 플래그를 변경할 수 없습니다. -// 아래와 같이 변경하려고 하면 에러가 발생합니다. -// user.name = "Pete" -// delete user.name -// Object.defineProperty(user, "name", { value: "Pete" }) -Object.defineProperty(user, "name", {writable: true}); // Error -*/!* -``` - -```smart header="\"non-configurable\"은 \"non-writable\"과 다릅니다." -`configurable` 플래그가 `false`이더라도 `writable` 플래그가 `true`이면 프로퍼티 값을 변경할 수 있습니다. - -`configurable: false`는 플래그 값 변경이나 프로퍼티 삭제를 막기 위해 만들어졌지, 프로퍼티 값 변경을 막기 위해 만들어진 게 아닙니다. -======= -// won't be able to change user.name or its flags -// all this won't work: +// user.name과 플래그를 변경할 수 없습니다. +// 아래는 모두 동작하지 않습니다. user.name = "Pete"; delete user.name; Object.defineProperty(user, "name", { value: "Pete" }); ``` -```smart header="The only attribute change possible: writable true -> false" -There's a minor exception about changing flags. +```smart header="가능한 플래그 변경: writable true -> false" +플래그 변경에는 한 가지 예외가 있습니다. + +구성 가능하지 않은 프로퍼티라도 `writable: true`를 `false`로 변경할 수 있습니다. 이렇게 하면 값 변경을 막을 수 있습니다. 반대로 false를 true로 변경하는 것은 불가능합니다. -We can change `writable: true` to `false` for a non-configurable property, thus preventing its value modification (to add another layer of protection). Not the other way around though. ->>>>>>> upstream/master ``` ## Object.defineProperties -<<<<<<< HEAD -[Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) 메서드를 사용하면 프로퍼티 여러 개를 한 번에 정의할 수 있습니다. -======= -There's a method [Object.defineProperties(obj, descriptors)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) that allows to define many properties at once. ->>>>>>> upstream/master +[Object.defineProperties(obj, descriptors)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) 메서드를 사용하면 프로퍼티 여러 개를 한 번에 정의할 수 있습니다. 문법: ```js Object.defineProperties(obj, { prop1: descriptor1, - prop2: descriptor2 + prop2: descriptor2, // ... }); ``` @@ -348,11 +300,7 @@ Object.defineProperties(user, { ## Object.getOwnPropertyDescriptors -<<<<<<< HEAD -[Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors) 메서드를 사용하면 프로퍼티 설명자를 전부 한꺼번에 가져올 수 있습니다. -======= -To get all property descriptors at once, we can use the method [Object.getOwnPropertyDescriptors(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors). ->>>>>>> upstream/master +[Object.getOwnPropertyDescriptors(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors) 메서드를 사용하면 프로퍼티 설명자를 한꺼번에 가져올 수 있습니다. 이 메서드를 `Object.defineProperties`와 함께 사용하면 객체 복사 시 플래그도 함께 복사할 수 있습니다. @@ -364,17 +312,13 @@ let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)); ```js for (let key in user) { - clone[key] = user[key] + clone[key] = user[key]; } ``` -그런데 이 방법은 플래그는 복사하지 않습니다. 플래그 정보도 복사하려면 `Object.defineProperties`를 사용하시기 바랍니다. +그런데 이 방법은 플래그를 복사하지 않습니다. 플래그 정보도 복사하려면 `Object.defineProperties`를 사용하기 바랍니다. -<<<<<<< HEAD -위 샘플 코드처럼 `for..in`을 사용해 객체를 복사하면 심볼형 프로퍼티도 놓치게 됩니다. 하지만 `Object.getOwnPropertyDescriptors`는 심볼형 프로퍼티를 포함한 프로퍼티 설명자 *전체*를 반환합니다. -======= -Another difference is that `for..in` ignores symbolic and non-enumerable properties, but `Object.getOwnPropertyDescriptors` returns *all* property descriptors including symbolic and non-enumerable ones. ->>>>>>> upstream/master +또 다른 점은 `for..in`은 심볼형 프로퍼티와 열거가 불가능한 프로퍼티를 무시합니다. 하지만 `Object.getOwnPropertyDescriptors`는 심볼형 프로퍼티와 열거가 불가능한 프로퍼티를 포함한 프로퍼티 설명자 *전체*를 반환합니다. ## 객체 수정을 막아주는 다양한 메서드 @@ -382,46 +326,24 @@ Another difference is that `for..in` ignores symbolic and non-enumerable propert 아래 메서드를 사용하면 한 객체 내 프로퍼티 *전체*를 대상으로 하는 제약사항을 만들 수 있습니다. -<<<<<<< HEAD -[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions) -: 객체에 새로운 프로퍼티를 추가할 수 없게 합니다. - -[Object.seal(obj)](mdn:js/Object/seal) -: 새로운 프로퍼티 추가나 기존 프로퍼티 삭제를 막아줍니다. 프로퍼티 전체에 `configurable: false`를 설정하는 것과 동일한 효과입니다. - -[Object.freeze(obj)](mdn:js/Object/freeze) -: 새로운 프로퍼티 추가나 기존 프로퍼티 삭제, 수정을 막아줍니다. 프로퍼티 전체에 `configurable: false, writable: false`를 설정하는 것과 동일한 효과입니다. -======= [Object.preventExtensions(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) -: Forbids the addition of new properties to the object. +: 객체에 새로운 프로퍼티를 추가할 수 없게 합니다. [Object.seal(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) -: Forbids adding/removing of properties. Sets `configurable: false` for all existing properties. +: 새로운 프로퍼티 추가나 기존 프로퍼티 삭제를 막아줍니다. 기존 프로퍼티 전체에 `configurable: false`를 설정합니다. [Object.freeze(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) -: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties. ->>>>>>> upstream/master +: 새로운 프로퍼티 추가나 기존 프로퍼티 삭제, 수정을 막아줍니다. 기존 프로퍼티 전체에 `configurable: false, writable: false`를 설정합니다. 아래 메서드는 위 세 가지 메서드를 사용해서 설정한 제약사항을 확인할 때 사용할 수 있습니다. -<<<<<<< HEAD -[Object.isExtensible(obj)](mdn:js/Object/isExtensible) -: 새로운 프로퍼티를 추가하는 게 불가능한 경우 `false`를, 그렇지 않은 경우 `true`를 반환합니다. - -[Object.isSealed(obj)](mdn:js/Object/isSealed) -: 프로퍼티 추가, 삭제가 불가능하고 모든 프로퍼티가 `configurable: false`이면 `true`를 반환합니다. - -[Object.isFrozen(obj)](mdn:js/Object/isFrozen) -: 프로퍼티 추가, 삭제, 변경이 불가능하고 모든 프로퍼티가 `configurable: false, writable: false`이면 `true`를 반환합니다. -======= [Object.isExtensible(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) -: Returns `false` if adding properties is forbidden, otherwise `true`. +: 새로운 프로퍼티를 추가하는 게 불가능한 경우 `false`를, 그렇지 않은 경우 `true`를 반환합니다. [Object.isSealed(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) -: Returns `true` if adding/removing properties is forbidden, and all existing properties have `configurable: false`. +: 프로퍼티 추가, 삭제가 불가능하고 모든 프로퍼티가 `configurable: false`이면 `true`를 반환합니다. [Object.isFrozen(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) -: Returns `true` if adding/removing/changing properties is forbidden, and all current properties are `configurable: false, writable: false`. ->>>>>>> upstream/master +: 프로퍼티 추가, 삭제, 변경이 불가능하고 모든 프로퍼티가 `configurable: false, writable: false`이면 `true`를 반환합니다. 위 메서드들은 실무에선 잘 사용되지 않습니다. From d40b568be94880fecf9a22b22078ab00b61d927a Mon Sep 17 00:00:00 2001 From: meanzzi Date: Sun, 17 May 2026 02:04:54 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[=EC=B6=A9=EB=8F=8C=ED=95=B4=EA=B2=B0]=20Pa?= =?UTF-8?q?rt1=207.2=20=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20getter?= =?UTF-8?q?=EC=99=80=20setter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../02-property-accessors/article.md | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md index 2899bea10..b56b11c39 100644 --- a/1-js/07-object-properties/02-property-accessors/article.md +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -1,15 +1,10 @@ - # 프로퍼티 getter와 setter 객체의 프로퍼티는 두 종류로 나뉩니다. -첫 번째 종류는 *데이터 프로퍼티(data property)* 입니다. 지금까지 사용한 모든 프로퍼티는 데이터 프로퍼티입니다. 데이터 프로퍼티 조작 방법에 대해선 모두 알고 계실 것이라 생각합니다. +첫 번째 종류는 _데이터 프로퍼티(data property)_ 입니다. 지금까지 사용한 모든 프로퍼티는 데이터 프로퍼티입니다. 데이터 프로퍼티 조작 방법에 대해선 모두 알고 계실 것이라 생각합니다. -<<<<<<< HEAD -두 번째는 *접근자 프로퍼티(accessor property)* 라 불리는 새로운 종류의 프로퍼티입니다. 접근자 프로퍼티의 본질은 함수인데, 이 함수는 값을 획득(get)하고 설정(set)하는 역할을 담당합니다. 그런데 외부 코드에서는 함수가 아닌 일반적인 프로퍼티처럼 보입니다. -======= -The second type of property is something new. It's an *accessor property*. They are essentially functions that execute on getting and setting a value, but look like regular properties to an external code. ->>>>>>> upstream/master +두 번째는 _접근자 프로퍼티(accessor property)_ 라 불리는 새로운 종류의 프로퍼티입니다. 접근자 프로퍼티의 본질은 함수인데, 이 함수는 값을 획득(get)하고 설정(set)하는 역할을 담당합니다. 그런데 외부 코드에서는 함수가 아닌 일반적인 프로퍼티처럼 보입니다. ## getter와 setter @@ -34,7 +29,7 @@ getter 메서드는 `obj.propName`을 사용해 프로퍼티를 읽으려고 할 ```js let user = { name: "John", - surname: "Smith" + surname: "Smith", }; ``` @@ -57,7 +52,7 @@ alert(user.fullName); // John Smith */!* ``` -바깥 코드에선 접근자 프로퍼티를 일반 프로퍼티처럼 사용할 수 있습니다. 접근자 프로퍼티는 이런 아이디어에서 출발했습니다. 접근자 프로퍼티를 사용하면 함수처럼 *호출* 하지 않고, 일반 프로퍼티에서 값에 접근하는 것처럼 평범하게 `user.fullName`을 사용해 프로퍼티 값을 *얻을 수 있습니다*. 나머지 작업은 getter 메서드가 뒷단에서 처리해줍니다. +바깥 코드에선 접근자 프로퍼티를 일반 프로퍼티처럼 사용할 수 있습니다. 접근자 프로퍼티는 이런 아이디어에서 출발했습니다. 접근자 프로퍼티를 사용하면 함수처럼 _호출_ 하지 않고, 일반 프로퍼티에서 값에 접근하는 것처럼 평범하게 `user.fullName`을 사용해 프로퍼티 값을 _얻을 수 있습니다_. 나머지 작업은 getter 메서드가 뒷단에서 처리해줍니다. 한편, 위 예시의 `fullName`은 getter 메서드만 가지고 있기 때문에 `user.fullName=`을 사용해 값을 할당하려고 하면 에러가 발생합니다. @@ -169,11 +164,13 @@ let user = { set name(value) { if (value.length < 4) { - alert("입력하신 값이 너무 짧습니다. 네 글자 이상으로 구성된 이름을 입력하세요."); + alert( + "입력하신 값이 너무 짧습니다. 네 글자 이상으로 구성된 이름을 입력하세요.", + ); return; } this._name = value; - } + }, }; user.name = "Pete"; @@ -182,14 +179,13 @@ alert(user.name); // Pete user.name = ""; // 너무 짧은 이름을 할당하려 함 ``` -`user`의 이름은 `_name`에 저장되고, 프로퍼티에 접근하는 것은 getter와 setter를 통해 이뤄집니다. +`user`의 이름은 `_name`에 저장되고, 프로퍼티에 접근하는 것은 getter와 setter를 통해 이뤄집니다. 기술적으론 외부 코드에서 `user._name`을 사용해 이름에 바로 접근할 수 있습니다. 그러나 밑줄 `"_"` 로 시작하는 프로퍼티는 객체 내부에서만 활용하고, 외부에서는 건드리지 않는 것이 관습입니다. +## 호환성을 위해 사용하기 -## 호환성을 위해 사용하기 - - 접근자 프로퍼티는 언제 어느 때나 getter와 setter를 사용해 데이터 프로퍼티의 행동과 값을 원하는 대로 조정할 수 있게 해준다는 점에서 유용합니다. +접근자 프로퍼티는 언제 어느 때나 getter와 setter를 사용해 데이터 프로퍼티의 행동과 값을 원하는 대로 조정할 수 있게 해준다는 점에서 유용합니다. 데이터 프로퍼티 `name`과 `age`를 사용해서 사용자를 나타내는 객체를 구현한다고 가정해봅시다. @@ -201,7 +197,7 @@ function User(name, age) { let john = new User("John", 25); -alert( john.age ); // 25 +alert(john.age); // 25 ``` 그런데 곧 요구사항이 바뀌어서 `age` 대신에 `birthday`를 저장해야 한다고 해보겠습니다. `age`보다는 `birthday`가 더 정확하고 편리하기 때문이죠. From ffbdc574282245cc144853fdfb1efff44fe7e804 Mon Sep 17 00:00:00 2001 From: meanzzi Date: Sun, 17 May 2026 03:41:20 +0900 Subject: [PATCH 8/9] =?UTF-8?q?fix=20:=20=EB=B0=B1=ED=8B=B1=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/06-advanced-functions/10-bind/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index 08d21525a..d4043bdbf 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -328,4 +328,4 @@ lodash 라이브러리의 [_.partial](https://lodash.com/docs#partial)을 사용 기존 함수의 인수 몇 개를 고정한 함수를 *부분 적용(partially applied)* 함수 또는 *부분(partial)* 함수라고 부릅니다. 부분 적용은 같은 인수를 여러 번 반복하고 싶지 않을 때 유용합니다. `send(from, to)`라는 함수가 있는데 `from`을 고정하고 싶다면 `send(from, to)`의 부분 함수를 구현해 사용하면 됩니다. -```` + From f2713c06fdb2f30ab4b6e80a3bcb76403c221409 Mon Sep 17 00:00:00 2001 From: meanzzi Date: Sun, 17 May 2026 11:31:18 +0900 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20Prettier=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=ED=8F=AC=EB=A7=B7=ED=8C=85=20=EB=90=98=EB=8F=8C=EB=A6=AC?= =?UTF-8?q?=EA=B8=B0=20=EB=B0=8F=20=EC=B6=A9=EB=8F=8C=20=EC=9E=AC=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../07-new-function/article.md | 21 +++---- .../08-settimeout-setinterval/article.md | 29 ++++----- .../09-call-apply-decorators/article.md | 60 +++++++++---------- .../10-bind/5-question-use-bind/solution.md | 7 +-- 1-js/06-advanced-functions/10-bind/article.md | 41 ++++++------- .../12-arrow-functions/article.md | 15 +++-- .../01-property-descriptors/article.md | 44 +++++++------- .../02-property-accessors/article.md | 24 ++++---- 8 files changed, 113 insertions(+), 128 deletions(-) diff --git a/1-js/06-advanced-functions/07-new-function/article.md b/1-js/06-advanced-functions/07-new-function/article.md index 6b27342e9..011d02824 100644 --- a/1-js/06-advanced-functions/07-new-function/article.md +++ b/1-js/06-advanced-functions/07-new-function/article.md @@ -1,3 +1,4 @@ + # new Function 문법 함수 표현식과 함수 선언문 이외에 함수를 만들 수도 있는 방법이 하나 더 있습니다. 잘 사용하는 방법은 아니지만, 이 방법 외에는 대안이 없을 때 사용합니다. @@ -7,7 +8,7 @@ `new Function` 문법을 사용하면 함수를 만들 수 있습니다. ```js -let func = new Function([arg1, arg2, ...argN], functionBody); +let func = new Function ([arg1, arg2, ...argN], functionBody); ``` 새로 만들어지는 함수는 인수 `arg1...argN`과 함수 본문 `functionBody`로 구성됩니다. @@ -15,9 +16,9 @@ let func = new Function([arg1, arg2, ...argN], functionBody); 인수 두 개가 있는 함수를 직접 만들어 보면서 `new Function` 문법에 대해 이해해보도록 합시다. ```js run -let sum = new Function("a", "b", "return a + b"); +let sum = new Function('a', 'b', 'return a + b'); -alert(sum(1, 2)); // 3 +alert( sum(1, 2) ); // 3 ``` 인수가 없고 함수 본문만 있는 함수를 만들어보겠습니다. @@ -45,11 +46,11 @@ func(); ## 클로저 -함수는 특별한 프로퍼티 `[[Environment]]`에 저장된 정보를 이용해 자기 자신이 태어난 곳을 기억합니다. `[[Environment]]`는 함수가 만들어진 렉시컬 환경을 참조합니다(자세한 내용은 에서 다루었습니다). +함수는 특별한 프로퍼티 `[[Environment]]`에 저장된 정보를 이용해 자기 자신이 태어난 곳을 기억합니다. `[[Environment]]`는 함수가 만들어진 렉시컬 환경을 참조합니다(자세한 내용은 에서 다루었습니다). 그런데 `new Function`을 이용해 함수를 만들면 함수의 `[[Environment]]` 프로퍼티가 현재 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하게 됩니다. -따라서 `new Function`을 이용해 만든 함수는 외부 변수에 접근할 수 없고, 오직 전역 변수에만 접근할 수 있습니다. +따라서 `new Function`을 이용해 만든 함수는 외부 변수에 접근할 수 없고, 오직 전역 변수에만 접근할 수 있습니다. ```js run function getFunc() { @@ -89,7 +90,7 @@ getFunc()(); // getFunc의 렉시컬 환경에 있는 값 *!*"test"*/!*가 출 `new Function`으로 만든 새로운 함수 내부에서 외부 변수에 접근하려 할 때, 기존 함수 선언 방식으로 작성한 함수와 동일한 동작이 보장되어야 하죠. -그런데 스크립트가 프로덕션 서버에 반영되기 전, _압축기(minifier)_ 에 의해 압축될 때 문제가 발생합니다. 압축기는 스크립트에서 주석이나 여분의 공백 등을 없애 코드 크기를 줄여주는 특수한 프로그램인데 압축기가 지역 변수 이름을 짧게 바꾸면서 문제가 발생하죠. +그런데 스크립트가 프로덕션 서버에 반영되기 전, *압축기(minifier)* 에 의해 압축될 때 문제가 발생합니다. 압축기는 스크립트에서 주석이나 여분의 공백 등을 없애 코드 크기를 줄여주는 특수한 프로그램인데 압축기가 지역 변수 이름을 짧게 바꾸면서 문제가 발생하죠. 구체적으로 어떤 부분이 문제가 되는지 예시를 통해 알아봅시다. 함수 내부에 `let userName`라는 변수가 있으면 이 지역변수는 압축기에 의해 `let a` 등(해당 글자가 이미 사용 중이라면 다른 짧은 이름)으로 대체되는데, 이때 `userName` 모두가 `a`로 교체됩니다. `userName`은 지역변수이고, 함수 외부에선 함수 내부에 있는 변수에 접근할 수 없기 때문에 이렇게 해도 전혀 문제가 없죠. 압축기는 단순히 찾아바꾸기가 아니라 코드 구조를 분석해 기존 코드의 기능을 망가뜨리지 않으면서 영리하게 제 역할을 수행합니다. @@ -106,7 +107,7 @@ getFunc()(); // getFunc의 렉시컬 환경에 있는 값 *!*"test"*/!*가 출 문법: ```js -let func = new Function([arg1, arg2, ...argN], functionBody); +let func = new Function ([arg1, arg2, ...argN], functionBody); ``` 인수를 한꺼번에 모아(쉼표로 구분) 전달할 수도 있습니다. @@ -114,9 +115,9 @@ let func = new Function([arg1, arg2, ...argN], functionBody); 아래 세 선언 방식은 동일하게 동작하죠. ```js -new Function("a", "b", "return a + b"); // 기본 문법 -new Function("a,b", "return a + b"); // 쉼표로 구분 -new Function("a , b", "return a + b"); // 쉼표와 공백으로 구분 +new Function('a', 'b', 'return a + b'); // 기본 문법 +new Function('a,b', 'return a + b'); // 쉼표로 구분 +new Function('a , b', 'return a + b'); // 쉼표와 공백으로 구분 ``` `new Function`을 이용해 만든 함수의 `[[Environment]]`는 외부 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하므로 외부 변수를 사용할 수 없습니다. 단점 같아 보이는 특징이긴 하지만 에러를 예방해 준다는 관점에선 장점이 되기도 합니다. 구조상으론 매개변수를 사용해 값을 받는 게 더 낫습니다. 압축기에 의한 에러도 방지할 수 있죠. diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md index b311a5a49..41967d1fe 100644 --- a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md @@ -64,7 +64,7 @@ setTimeout("alert('안녕하세요.')", 1000); 그런데 이렇게 문자열을 사용하는 방법은 추천하지 않습니다. 되도록 다음 예시와 같이 익명 화살표 함수를 사용하세요. ```js run no-beautify -setTimeout(() => alert("안녕하세요."), 1000); +setTimeout(() => alert('안녕하세요.'), 1000); ``` ````smart header="함수를 실행하지 말고 넘기세요." @@ -120,21 +120,17 @@ let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...) ```js run // 2초 간격으로 메시지를 보여줌 -let timerId = setInterval(() => alert("째깍"), 2000); +let timerId = setInterval(() => alert('째깍'), 2000); // 5초 후에 정지 -setTimeout(() => { - clearInterval(timerId); - alert("정지"); -}, 5000); +setTimeout(() => { clearInterval(timerId); alert('정지'); }, 5000); ``` -```smart header="`alert`창이 떠 있더라도 타이머는 멈추지 않습니다." -Chrome과 Firefox를 포함한 대부분의 브라우저는`alert/confirm/prompt` 창이 떠 있는 동안에도 내부 타이머를 멈추지 않습니다. +```smart header="`alert` 창이 떠 있더라도 타이머는 멈추지 않습니다." +Chrome과 Firefox를 포함한 대부분의 브라우저는 `alert/confirm/prompt` 창이 떠 있는 동안에도 내부 타이머를 멈추지 않습니다. 위 예시를 실행하고 첫 번째 `alert` 창이 떴을 때 몇 초간 기다렸다가 창을 닫으면, 두 번째 `alert` 창이 바로 나타나는 것을 보고 이를 확인할 수 있습니다. 이런 이유로 얼럿 창은 명시한 지연 시간인 2초보다 더 짧은 간격으로 뜨게 됩니다. - -```` +``` ## 중첩 setTimeout @@ -153,7 +149,7 @@ let timerId = setTimeout(function tick() { timerId = setTimeout(tick, 2000); // (*) */!* }, 2000); -```` +``` 다섯 번째 줄의 `setTimeout`은 `(*)`로 표시한 줄의 실행이 종료되면 다음 호출을 스케줄링합니다. @@ -162,7 +158,6 @@ let timerId = setTimeout(function tick() { 5초 간격으로 서버에 요청을 보내 데이터를 얻는다고 가정해 봅시다. 서버가 과부하 상태라면 요청 간격을 10초, 20초, 40초 등으로 증가시켜주는 게 좋을 겁니다. 아래는 이를 구현한 의사 코드입니다. - ```js let delay = 5000; @@ -179,6 +174,7 @@ let timerId = setTimeout(function request() { }, delay); ``` + CPU 소모가 많은 작업을 주기적으로 실행하는 경우에도 `setTimeout`을 재귀 실행하는 방법이 유용합니다. 작업에 걸리는 시간에 따라 다음 작업을 유동적으로 계획할 수 있기 때문입니다. **중첩 `setTimeout`을 이용하는 방법은 지연 간격을 보장하지만 `setInterval`은 이를 보장하지 않습니다.** @@ -187,7 +183,7 @@ CPU 소모가 많은 작업을 주기적으로 실행하는 경우에도 `setTim ```js let i = 1; -setInterval(function () { +setInterval(function() { func(i++); }, 100); ``` @@ -214,7 +210,7 @@ setTimeout(function run() { 그렇다면 `func`을 실행하는 데 걸리는 시간이 명시한 지연 간격보다 길 때 어떤 일이 발생할까요? -이런 경우는 엔진이 `func`의 실행이 종료될 때까지 기다려줍니다. `func`의 실행이 종료되면 엔진은 스케줄러를 확인하고, 지연 시간이 지났으면 다음 호출을 _바로_ 시작합니다. +이런 경우는 엔진이 `func`의 실행이 종료될 때까지 기다려줍니다. `func`의 실행이 종료되면 엔진은 스케줄러를 확인하고, 지연 시간이 지났으면 다음 호출을 *바로* 시작합니다. 따라서 함수 호출에 걸리는 시간이 매번 `delay` 밀리초보다 길면, 모든 함수가 쉼 없이 계속 연속 호출됩니다. @@ -260,7 +256,7 @@ alert("Hello"); 대기 시간이 0인 setTimeout을 활용한 브라우저 환경에서의 유스 케이스는 에서 자세히 다루도록 하겠습니다. ````smart header="실제로 지연시간이 0인 경우는 없습니다(브라우저 환경)." -브라우저는 [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers)에서 정한 중첩 타이머 실행 간격 관련 제약을 준수합니다. 해당 표준엔 "다섯 번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다."라는 제약이 명시되어 있습니다. +브라우저는 [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers)에서 정한 중첩 타이머 실행 간격 관련 제약을 준수합니다. 해당 표준엔 "다섯 번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다."라는 제약을 명시합니다. 예시를 보며 이 제약 사항을 이해해봅시다. 예시 내 `setTimeout`은 지연 없이 함수 run을 다시 호출할 수 있게 스케줄링 되어 있습니다. 배열 `times`에는 실제 지연 간격에 대한 정보가 기록되도록 해놓았는데, 배열 times에 어떤 값이 저장되는지 알아봅시다. @@ -292,14 +288,13 @@ setTimeout(function run() { - `setInterval(func, delay, ...args)`과 `setTimeout(func, delay, ...args)`은 `delay`밀리초 후에 `func`을 규칙적으로, 또는 한번 실행하도록 해줍니다. - `setTimeout·setInterval`을 호출하고 반환받은 값을 `clearTimeout·clearInterval`에 넘겨주면 스케줄링을 취소할 수 있습니다. -- 중첩 `setTimeout`을 사용하면 `setInterval`을 사용한 것보다 유연하게 코드를 작성할 수 있습니다. 여기에 더하여 실행 _사이의_ 지연 간격을 더 정확하게 조절할 수 있게 해줍니다. +- 중첩 `setTimeout`을 사용하면 `setInterval`을 사용한 것보다 유연하게 코드를 작성할 수 있습니다. 여기에 더하여 실행 *사이의* 지연 간격을 더 정확하게 조절할 수 있게 해줍니다. - 대기 시간이 0인 setTimeout(`setTimeout(func, 0)` 혹은 `setTimeout(func)`)을 사용하면 '현재 스크립트의 실행이 완료된 후 가능한 한 빠르게' 원하는 함수를 호출할 수 있습니다. - 지연 없이 중첩 `setTimeout`을 5회 이상 호출하거나 지연 없는 `setInterval`에서 호출이 5회 이상 이뤄지면, 4밀리초 이상의 지연 간격이 강제로 더해집니다. 이는 브라우저에만 적용되는 사항이며, 하위 호환성을 위해 유지되고 있습니다. 스케줄링 메서드를 사용할 땐 명시한 지연 간격이 *보장*되지 않을 수도 있다는 점에 유의해야 합니다. 아래와 같은 여러 이유로 브라우저 내 타이머가 느려질 수 있습니다. - - CPU가 과부하 상태인 경우 - 브라우저 탭이 백그라운드 모드인 경우 - 노트북이 배터리 절약 모드인 경우 diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md index 2acc7b1f0..a64f3243b 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md @@ -1,6 +1,6 @@ # call/apply와 데코레이터, 포워딩 -자바스크립트는 함수를 다룰 때 탁월한 유연성을 제공합니다. 함수는 이곳저곳 전달될 수 있고, 객체로도 사용될 수 있습니다. 이번 챕터에선 함수 간에 호출을 어떻게 _포워딩(forwarding)_ 하는지, 함수를 어떻게 _데코레이팅(decorating)_ 하는지에 대해 알아보겠습니다. +자바스크립트는 함수를 다룰 때 탁월한 유연성을 제공합니다. 함수는 이곳저곳 전달될 수 있고, 객체로도 사용될 수 있습니다. 이번 챕터에선 함수 간에 호출을 어떻게 *포워딩(forwarding)* 하는지, 함수를 어떻게 *데코레이팅(decorating)* 하는지에 대해 알아보겠습니다. ## 코드 변경 없이 캐싱 기능 추가하기 @@ -22,33 +22,32 @@ function slow(x) { function cachingDecorator(func) { let cache = new Map(); - return function (x) { - if (cache.has(x)) { - // cache에 해당 키가 있으면 + return function(x) { + if (cache.has(x)) { // cache에 해당 키가 있으면 return cache.get(x); // 대응하는 값을 cache에서 읽어옵니다. } - let result = func(x); // 그렇지 않은 경우엔 func를 호출하고, + let result = func(x); // 그렇지 않은 경우엔 func를 호출하고, - cache.set(x, result); // 그 결과를 캐싱(저장)합니다. + cache.set(x, result); // 그 결과를 캐싱(저장)합니다. return result; }; } slow = cachingDecorator(slow); -alert(slow(1)); // slow(1)를 캐시에 저장하고 결과를 반환합니다. -alert("다시 호출: " + slow(1)); // 캐시에서 slow(1)의 결과를 가져옵니다. +alert( slow(1) ); // slow(1)를 캐시에 저장하고 결과를 반환합니다. +alert( "다시 호출: " + slow(1) ); // 캐시에서 slow(1)의 결과를 가져옵니다. -alert(slow(2)); // slow(2)를 캐시에 저장하고 결과를 반환합니다. -alert("다시 호출: " + slow(2)); // 캐시에서 slow(2)의 결과를 가져옵니다. +alert( slow(2) ); // slow(2)를 캐시에 저장하고 결과를 반환합니다. +alert( "다시 호출: " + slow(2) ); // 캐시에서 slow(2)의 결과를 가져옵니다. ``` -`cachingDecorator`같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 _데코레이터(decorator)_ 라고 부릅니다. +`cachingDecorator`같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 *데코레이터(decorator)* 라고 부릅니다. 모든 함수를 대상으로 `cachingDecorator`를 호출 할 수 있는데, 이때 반환되는 것은 캐싱 래퍼입니다. 함수에 `cachingDecorator`를 적용하기만 하면 캐싱이 가능한 함수를 원하는 만큼 구현할 수 있기 때문에 데코레이터 함수는 아주 유용하게 사용됩니다. -캐싱 관련 코드를 함수 코드와 분리할 수 있기 때문에 함수의 코드가 간결해진다는 장점도 있습니다. +캐싱 관련 코드를 함수 코드와 분리할 수 있기 때문에 함수의 코드가 간결해진다는 장점도 있습니다. 아래 그림에서 볼 수 있듯이 `cachingDecorator(func)`를 호출하면 '래퍼(wrapper)', `function(x)`이 반환됩니다. 래퍼 `function(x)`는 `func(x)`의 호출 결과를 캐싱 로직으로 감쌉니다(wrapping). @@ -59,7 +58,7 @@ alert("다시 호출: " + slow(2)); // 캐시에서 slow(2)의 결과를 가져 `slow` 본문을 수정하는 것 보다 독립된 래퍼 함수 `cachingDecorator`를 사용할 때 생기는 이점을 정리하면 다음과 같습니다. - `cachingDecorator`를 재사용 할 수 있습니다. 원하는 함수 어디에든 `cachingDecorator`를 적용할 수 있습니다. -- 캐싱 로직이 분리되어 `slow` 자체의 복잡성이 증가하지 않습니다. +- 캐싱 로직이 분리되어 `slow` 자체의 복잡성이 증가하지 않습니다. - 필요하다면 여러 개의 데코레이터를 조합해서 사용할 수도 있습니다(추가 데코레이터는 `cachingDecorator` 뒤를 따릅니다). ## 'func.call'를 사용해 컨텍스트 지정하기 @@ -132,10 +131,9 @@ func.call(context, arg1, arg2, ...) 메서드를 호출하면 메서드의 첫 번째 인수가 `this`, 이어지는 인수가 `func`의 인수가 된 후, `func`이 호출됩니다. 아래 함수와 메서드를 호출하면 거의 동일한 일이 발생합니다. - ```js func(1, 2, 3); -func.call(obj, 1, 2, 3); +func.call(obj, 1, 2, 3) ``` 둘 다 인수로 `1`, `2`, `3`을 받죠. 유일한 차이점은 `func.call`에선 `this`가 `obj`로 고정된다는 점입니다. @@ -151,21 +149,22 @@ let user = { name: "John" }; let admin = { name: "Admin" }; // call을 사용해 원하는 객체가 'this'가 되도록 합니다. -sayHi.call(user); // this = John -sayHi.call(admin); // this = Admin +sayHi.call( user ); // this = John +sayHi.call( admin ); // this = Admin ``` 아래 예시에선 `call`을 사용해 컨텍스트와 `phrase`에 원하는 값을 지정해 보았습니다. + ```js run function say(phrase) { - alert(this.name + ": " + phrase); + alert(this.name + ': ' + phrase); } let user = { name: "John" }; // this엔 user가 고정되고, "Hello"는 메서드의 첫 번째 인수가 됩니다. -say.call(user, "Hello"); // John: Hello +say.call( user, "Hello" ); // John: Hello ``` 래퍼 안에서 `call`을 사용해 컨텍스트를 원본 함수로 전달하면 에러가 발생하지 않습니다. @@ -220,7 +219,7 @@ alert( worker.slow(2) ); // 제대로 동작합니다. 다만, 원본 함수가 let worker = { slow(min, max) { return min + max; // CPU를 아주 많이 쓰는 작업이라고 가정 - }, + } }; // 동일한 인수를 전달했을 때 호출 결과를 기억할 수 있어야 합니다. @@ -233,7 +232,7 @@ worker.slow = cachingDecorator(worker.slow); 1. 복수 키를 지원하는 맵과 유사한 자료 구조 구현하기(서드 파티 라이브러리 등을 사용해도 됨) 2. 중첩 맵을 사용하기. `(max, result)` 쌍 저장은 `cache.set(min)`으로, `result`는 `cache.get(min).get(max)`을 사용해 얻습니다. -3. 두 값을 하나로 합치기. `맵`의 키로 문자열 `"min,max"`를 사용합니다. 여러 값을 하나로 합치는 코드는 _해싱 함수(hashing function)_ 에 구현해 유연성을 높입니다. +3. 두 값을 하나로 합치기. `맵`의 키로 문자열 `"min,max"`를 사용합니다. 여러 값을 하나로 합치는 코드는 *해싱 함수(hashing function)* 에 구현해 유연성을 높입니다. 세 번째 방법만으로 충분하기 때문에 이 방법을 사용해 코드를 수정해 보겠습니다. @@ -292,7 +291,7 @@ alert( "다시 호출: " + worker.slow(3, 5) ); // 동일한 결과 출력(캐 내장 메서드 [func.apply](mdn:js/Function/apply)의 문법은 다음과 같습니다. ```js -func.apply(context, args); +func.apply(context, args) ``` `apply`는 `func`의 `this`를 `context`로 고정해주고, 유사 배열 객체인 `args`를 인수로 사용할 수 있게 해줍니다. @@ -307,19 +306,20 @@ func.apply(context, args); ``` 위 코드는 `func`을 동일한 컨텍스트와 인수로 호출합니다. + 그런데 `args`에 관해 약간의 차이가 있긴 합니다. -- 전개 구문 `...`은 _이터러블_ `args`을 분해 해 `call`에 전달할 수 있도록 해줍니다. -- `apply`는 오직 _유사 배열_ 형태의 `args`만 받습니다. +- 전개 구문 `...`은 *이터러블* `args`을 분해 해 `call`에 전달할 수 있도록 해줍니다. +- `apply`는 오직 *유사 배열* 형태의 `args`만 받습니다. 배열같이 이터러블이면서 유사 배열인 객체엔 둘 다를 사용할 수 있는데, 대부분의 자바스크립트 엔진은 내부에서 `apply`를 최적화 하기 때문에 `apply`를 사용하는 게 좀 더 빠르긴 합니다. -이렇게 컨텍스트와 함께 인수 전체를 다른 함수에 전달하는 것을 _콜 포워딩(call forwarding)_ 이라고 합니다. +이렇게 컨텍스트와 함께 인수 전체를 다른 함수에 전달하는 것을 *콜 포워딩(call forwarding)* 이라고 합니다. 가장 간단한 형태의 콜 포워딩은 다음과 같습니다. ```js -let wrapper = function () { +let wrapper = function() { return func.apply(this, arguments); }; ``` @@ -332,11 +332,11 @@ let wrapper = function () { ```js function hash(args) { - return args[0] + "," + args[1]; + return args[0] + ',' + args[1]; } ``` -지금 상태에선 인수 두 개만 다룰 수 있습니다. `args`의 요소 개수에 상관없이 요소들을 합칠 수 있으면 더 좋겠네요. +지금 상태에선 인수 두 개만 다룰 수 있습니다. `args`의 요소 개수에 상관없이 요소들을 합칠 수 있으면 더 좋겠네요. 가장 자연스러운 해결책은 배열 메서드 [arr.join](mdn:js/Array/join)을 사용하는 것입니다. @@ -372,7 +372,7 @@ function hash() { hash(1, 2); ``` -The trick is called _method borrowing_. +The trick is called *method borrowing*. 일반 배열에서 `join` 메서드를 빌려오고(`[].join`), `[].join.call`를 사용해 `arguments`를 컨텍스트로 고정한 후 `join`메서드를 호출하는 것이죠. @@ -416,7 +416,7 @@ The trick is called _method borrowing_. *콜 포워딩*은 대개 `apply`를 사용해 구현합니다. ```js -let wrapper = function () { +let wrapper = function() { return original.apply(this, arguments); }; ``` diff --git a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md index 05ea816a4..8372c465a 100644 --- a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md +++ b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md @@ -1,3 +1,4 @@ + 에러는 `askPassword`가 함수 `loginOk`, `loginFail`을 객체 없이 가지고 오기 때문에 발생합니다. ask는 `loginOk`, `loginFail`을 호출할 때 `this=undefined`라고 자연스레 가정합니다. @@ -32,13 +33,9 @@ askPassword(user.loginOk.bind(user), user.loginFail.bind(user)); 이제 잘 동작합니다. 이 외에도 다른 답이 있는데, 아래에서 확인 가능합니다. - ```js //... -askPassword( - () => user.loginOk(), - () => user.loginFail(), -); +askPassword(() => user.loginOk(), () => user.loginFail()); ``` 이렇게 화살표 함수를 사용하는 방법 또한 대개 잘 동작하며 가독성도 좋습니다. diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index d4043bdbf..760b6ee40 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -1,6 +1,5 @@ libs: - -- lodash + - lodash --- @@ -31,7 +30,7 @@ setTimeout(user.sayHi, 1000); // Hello, undefined! `this.firstName`이 "John"이 되어야 하는데, 얼럿창엔 `undefined`가 출력됩니다. -이렇게 된 이유는 `setTimeout`에 객체에서 분리된 함수인 `user.sayHi`가 전달되기 때문입니다. 위 예시의 마지막 줄은 다음 코드와 같습니다. +이렇게 된 이유는 `setTimeout`에 객체에서 분리된 함수인 `user.sayHi`가 전달되기 때문입니다. 위 예시의 마지막 줄은 다음 코드와 같습니다. ```js let f = user.sayHi; @@ -73,22 +72,19 @@ setTimeout(() => user.sayHi(), 1000); // Hello, John! `setTimeout`이 트리거 되기 전에(1초가 지나기 전에) `user`가 변경되면, 변경된 객체의 메서드를 호출하게 됩니다. + ```js run let user = { firstName: "John", sayHi() { alert(`Hello, ${this.firstName}!`); - }, + } }; setTimeout(() => user.sayHi(), 1000); // 1초가 지나기 전에 user의 값이 바뀜 -user = { - sayHi() { - alert("또 다른 사용자!"); - }, -}; +user = { sayHi() { alert("또 다른 사용자!"); } }; // setTimeout에 또 다른 사용자! ``` @@ -108,11 +104,11 @@ let boundFunc = func.bind(context); `func.bind(context)`는 함수처럼 호출 가능한 '특수 객체(exotic object)'를 반환합니다. 이 객체를 호출하면 `this`가 `context`로 고정된 함수 `func`가 반환됩니다. -따라서 `boundFunc`를 호출하면 `this`가 고정된 `func`를 호출하는 것과 동일한 효과를 봅니다. +따라서 `boundFunc`를 호출하면 `this`가 고정된 `func`를 호출하는 것과 동일한 효과를 봅니다. 아래 `funcUser`에는 `this`가 `user`로 고정된 `func`이 할당됩니다. -```js run +```js run let user = { firstName: "John" }; @@ -123,7 +119,7 @@ function func() { *!* let funcUser = func.bind(user); -funcUser(); // John +funcUser(); // John */!* ``` @@ -131,7 +127,7 @@ funcUser(); // John 인수는 원본 함수 `func`에 '그대로' 전달됩니다. -```js run +```js run let user = { firstName: "John" }; @@ -150,6 +146,7 @@ funcUser("Hello"); // Hello, John (인수 "Hello"가 넘겨지고 this는 user 이제 객체 메서드에 `bind`를 적용해 봅시다. + ```js run let user = { firstName: "John", @@ -176,14 +173,14 @@ user = { `(*)`로 표시한 줄에서 메서드 `user.sayHi`를 가져오고, 메서드에 `user`를 바인딩합니다. `sayHi`는 이제 '묶인(bound)' 함수가 되어 단독으로 호출할 수 있고 `setTimeout`에 전달하여 호출할 수도 있습니다. 어떤 방식이든 컨택스트는 원하는 대로 고정됩니다. -아래 예시를 실행하면 인수는 '그대로' 전달되고 `bind`에 의해 `this`만 고정된 것을 확인할 수 있습니다. +아래 예시를 실행하면 인수는 '그대로' 전달되고 `bind`에 의해 `this`만 고정된 것을 확인할 수 있습니다. ```js run let user = { firstName: "John", say(phrase) { alert(`${phrase}, ${this.firstName}!`); - }, + } }; let say = user.say.bind(user); @@ -197,21 +194,20 @@ say("Bye"); // Bye, John! ("Bye"를 say에 전달합니다.) ```js for (let key in user) { - if (typeof user[key] == "function") { + if (typeof user[key] == 'function') { user[key] = user[key].bind(user); } } ``` -자바스크립트 라이브러리를 사용해도 대규모 바인딩을 할 수 있습니다. lodash 라이브러리의 [\_.bindAll(object, methodNames)](https://lodash.com/docs#bindAll)이 그 예입니다. - +자바스크립트 라이브러리를 사용해도 대규모 바인딩을 할 수 있습니다. lodash 라이브러리의 [_.bindAll(object, methodNames)](https://lodash.com/docs#bindAll)이 그 예입니다. ```` ## 부분 적용 지금까진 `this` 바인딩에 대해서만 이야기해보았습니다. 한 단계 더 나아가 봅시다. -`this` 뿐만 아니라 인수도 바인딩이 가능합니다. 인수 바인딩은 잘 쓰이진 않지만 가끔 유용할 때가 있습니다. +`this` 뿐만 아니라 인수도 바인딩이 가능합니다. 인수 바인딩은 잘 쓰이진 않지만 가끔 유용할 때가 있습니다. `bind`의 전체 문법은 다음과 같습니다. @@ -281,7 +277,7 @@ alert( triple(5) ); // = mul(3, 5) = 15 네이티브 `bind`만으로는 컨텍스트를 생략하고 인수로 바로 뛰어넘지 못합니다. -다행히도 인수만 바인딩해주는 헬퍼 함수 `partial`를 구현하는 건 쉽습니다. +다행히도 인수만 바인딩해주는 헬퍼 함수 `partial`를 구현하는 건 쉽습니다. 아래와 같이 말이죠. @@ -323,9 +319,8 @@ lodash 라이브러리의 [_.partial](https://lodash.com/docs#partial)을 사용 `func.bind(context, ...args)`는 `this`가 `context`로 고정되고 인수도 고정된 함수 `func`을 반환합니다. -`bind`는 보통 객체 메서드의 `this`를 고정해 어딘가에 넘기고자 할 때 사용합니다. `setTimeout`에 넘길 때 같이 말이죠. +`bind`는 보통 객체 메서드의 `this`를 고정해 어딘가에 넘기고자 할 때 사용합니다. `setTimeout`에 넘길 때 같이 말이죠. 기존 함수의 인수 몇 개를 고정한 함수를 *부분 적용(partially applied)* 함수 또는 *부분(partial)* 함수라고 부릅니다. -부분 적용은 같은 인수를 여러 번 반복하고 싶지 않을 때 유용합니다. `send(from, to)`라는 함수가 있는데 `from`을 고정하고 싶다면 `send(from, to)`의 부분 함수를 구현해 사용하면 됩니다. - +부분 적용은 같은 인수를 여러 번 반복하고 싶지 않을 때 유용합니다. `send(from, to)`라는 함수가 있는데 `from`을 고정하고 싶다면 `send(from, to)`의 부분 함수를 구현해 사용하면 됩니다. diff --git a/1-js/06-advanced-functions/12-arrow-functions/article.md b/1-js/06-advanced-functions/12-arrow-functions/article.md index 48caa3f8b..e159ee374 100644 --- a/1-js/06-advanced-functions/12-arrow-functions/article.md +++ b/1-js/06-advanced-functions/12-arrow-functions/article.md @@ -52,7 +52,7 @@ let group = { *!* this.students.forEach(function(student) { // Error: Cannot read property 'title' of undefined - alert(this.title + ': ' + student) + alert(this.title + ': ' + student); }); */!* } @@ -67,15 +67,14 @@ group.showList(); ```warn header="화살표 함수는 `new`와 함께 실행할 수 없습니다." `this`가 없기 때문에 화살표 함수는 생성자 함수로 사용할 수 없다는 제약이 있습니다. 화살표 함수는 `new`와 함께 호출할 수 없습니다. - -```` +``` ```smart header="화살표 함수 vs. bind" 화살표 함수와 일반 함수를 `.bind(this)`를 사용해서 호출하는 것 사이에는 미묘한 차이가 있습니다. - `.bind(this)`는 함수의 '한정된 버전(bound version)'을 만듭니다. - 화살표 함수는 어떤 것도 바인딩시키지 않습니다. 화살표 함수엔 단지 `this`가 없을 뿐입니다. 화살표 함수에서 `this`를 사용하면 일반 변수 서칭과 마찬가지로 `this`의 값을 외부 렉시컬 환경에서 찾습니다. -```` +``` ## 화살표 함수엔 'arguments'가 없습니다 @@ -87,13 +86,13 @@ group.showList(); ```js run function defer(f, ms) { - return function () { + return function() { setTimeout(() => f.apply(this, arguments), ms); }; } function sayHi(who) { - alert("안녕, " + who); + alert('안녕, ' + who); } let sayHiDeferred = defer(sayHi, 2000); @@ -104,9 +103,9 @@ sayHiDeferred("철수"); // 2초 후 "안녕, 철수"가 출력됩니다. ```js function defer(f, ms) { - return function (...args) { + return function(...args) { let ctx = this; - setTimeout(function () { + setTimeout(function() { return f.apply(ctx, args); }, ms); }; diff --git a/1-js/07-object-properties/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md index a7b304817..d4ce5f806 100644 --- a/1-js/07-object-properties/01-property-descriptors/article.md +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -1,14 +1,15 @@ + # 프로퍼티 플래그와 설명자 아시다시피 객체엔 프로퍼티가 저장됩니다. -지금까진 프로퍼티를 단순히 '키-값' 쌍의 관점에서만 다뤘습니다. 그런데 사실 프로퍼티는 우리가 생각했던 것보다 더 유연하고 강력한 자료구조입니다. +지금까진 프로퍼티를 단순히 '키-값' 쌍의 관점에서만 다뤘습니다. 그런데 사실 프로퍼티는 우리가 생각했던 것보다 더 유연하고 강력한 자료구조입니다. 이 챕터에선 객체 프로퍼티 추가 구성 옵션 몇 가지를 다루고, 이어지는 챕터에선 이 옵션들을 이용해 손쉽게 getter나 setter 함수를 만드는 법을 알아보겠습니다. ## 프로퍼티 플래그 -객체 프로퍼티는 **`값(value)`** 과 함께 플래그(flag)라 불리는 특별한 속성 세 가지를 갖습니다. +객체 프로퍼티는 **`값(value)`** 과 함께 플래그(flag)라 불리는 특별한 속성 세 가지를 갖습니다. - **`writable`** -- `true`이면 값을 수정할 수 있습니다. 그렇지 않다면 읽기만 가능합니다. - **`enumerable`** -- `true`이면 반복문을 사용해 나열할 수 있습니다. 그렇지 않다면 반복문을 사용해 나열할 수 없습니다. @@ -18,10 +19,9 @@ 자 이제 본격적으로 프로퍼티 플래그에 대해 다뤄봅시다. 먼저 플래그를 얻는 방법을 알아보겠습니다. -[Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) 메서드를 사용하면 특정 프로퍼티에 대한 정보를 _모두_ 얻을 수 있습니다. +[Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) 메서드를 사용하면 특정 프로퍼티에 대한 정보를 *모두* 얻을 수 있습니다. 문법: - ```js let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); ``` @@ -38,12 +38,12 @@ let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); ```js run let user = { - name: "John", + name: "John" }; -let descriptor = Object.getOwnPropertyDescriptor(user, "name"); +let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); -alert(JSON.stringify(descriptor, null, 2)); +alert( JSON.stringify(descriptor, null, 2 ) ); /* property descriptor: { "value": "John", @@ -59,7 +59,7 @@ alert(JSON.stringify(descriptor, null, 2)); 문법: ```js -Object.defineProperty(obj, propertyName, descriptor); +Object.defineProperty(obj, propertyName, descriptor) ``` `obj`, `propertyName` @@ -68,7 +68,7 @@ Object.defineProperty(obj, propertyName, descriptor); `descriptor` : 적용하고자 하는 프로퍼티 설명자 -`defineProperty`메서드는 객체에 해당 프로퍼티가 있으면 플래그를 원하는 대로 변경해줍니다. 프로퍼티가 없으면 인수로 넘겨받은 정보를 이용해 새로운 프로퍼티를 만듭니다. 이때 플래그 정보가 없으면 플래그 값은 자동으로 `false`가 됩니다. +`defineProperty`메서드는 객체에 해당 프로퍼티가 있으면 플래그를 원하는 대로 변경해줍니다. 프로퍼티가 없으면 인수로 넘겨받은 정보를 이용해 새로운 프로퍼티를 만듭니다. 이때 플래그 정보가 없으면 플래그 값은 자동으로 `false`가 됩니다. 아래 예시를 보면 프로퍼티 `name`이 새로 만들어지고, 모든 플래그 값이 `false`가 된 것을 확인할 수 있습니다. @@ -153,7 +153,7 @@ let user = { name: "John", toString() { return this.name; - }, + } }; //커스텀 toString은 for...in을 사용해 열거할 수 있습니다. @@ -197,9 +197,9 @@ alert(Object.keys(user)); // name 내장 객체 `Math`의 `PI` 프로퍼티가 대표적인 예입니다. 이 프로퍼티는 쓰기와 열거, 구성이 불가능합니다. ```js run -let descriptor = Object.getOwnPropertyDescriptor(Math, "PI"); +let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); -alert(JSON.stringify(descriptor, null, 2)); +alert( JSON.stringify(descriptor, null, 2 ) ); /* { "value": 3.141592653589793, @@ -209,7 +209,6 @@ alert(JSON.stringify(descriptor, null, 2)); } */ ``` - 개발자가 코드를 사용해 `Math.PI` 값을 변경하거나 덮어쓰는 것도 불가능합니다. ```js run @@ -235,14 +234,14 @@ Object.defineProperty(Math, "PI", { writable: true }); ```js run let user = { - name: "John", + name: "John" }; Object.defineProperty(user, "name", { - configurable: false, + configurable: false }); -user.name = "Pete"; // 동작합니다. +user.name = "Pete"; // works fine delete user.name; // Error ``` @@ -250,12 +249,12 @@ delete user.name; // Error ```js run let user = { - name: "John", + name: "John" }; Object.defineProperty(user, "name", { writable: false, - configurable: false, + configurable: false }); // user.name과 플래그를 변경할 수 없습니다. @@ -269,7 +268,6 @@ Object.defineProperty(user, "name", { value: "Pete" }); 플래그 변경에는 한 가지 예외가 있습니다. 구성 가능하지 않은 프로퍼티라도 `writable: true`를 `false`로 변경할 수 있습니다. 이렇게 하면 값 변경을 막을 수 있습니다. 반대로 false를 true로 변경하는 것은 불가능합니다. - ``` ## Object.defineProperties @@ -281,7 +279,7 @@ Object.defineProperty(user, "name", { value: "Pete" }); ```js Object.defineProperties(obj, { prop1: descriptor1, - prop2: descriptor2, + prop2: descriptor2 // ... }); ``` @@ -312,13 +310,13 @@ let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)); ```js for (let key in user) { - clone[key] = user[key]; + clone[key] = user[key] } ``` -그런데 이 방법은 플래그를 복사하지 않습니다. 플래그 정보도 복사하려면 `Object.defineProperties`를 사용하기 바랍니다. +그런데 이 방법은 플래그는 복사하지 않습니다. 플래그 정보도 복사하려면 `Object.defineProperties`를 사용하시기 바랍니다. -또 다른 점은 `for..in`은 심볼형 프로퍼티와 열거가 불가능한 프로퍼티를 무시합니다. 하지만 `Object.getOwnPropertyDescriptors`는 심볼형 프로퍼티와 열거가 불가능한 프로퍼티를 포함한 프로퍼티 설명자 *전체*를 반환합니다. +또 다른 점은 `for..in`은 심볼형 프로퍼티와 열거가 불가능한 프로퍼티를 무시합니다. 하지만 `Object.getOwnPropertyDescriptors`는 심볼형 프로퍼티와 열거가 불가능한 프로퍼티를 포함한 프로퍼티 설명자 *전체*를 반환합니다. ## 객체 수정을 막아주는 다양한 메서드 diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md index b56b11c39..a15083124 100644 --- a/1-js/07-object-properties/02-property-accessors/article.md +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -1,10 +1,11 @@ + # 프로퍼티 getter와 setter 객체의 프로퍼티는 두 종류로 나뉩니다. -첫 번째 종류는 _데이터 프로퍼티(data property)_ 입니다. 지금까지 사용한 모든 프로퍼티는 데이터 프로퍼티입니다. 데이터 프로퍼티 조작 방법에 대해선 모두 알고 계실 것이라 생각합니다. +첫 번째 종류는 *데이터 프로퍼티(data property)* 입니다. 지금까지 사용한 모든 프로퍼티는 데이터 프로퍼티입니다. 데이터 프로퍼티 조작 방법에 대해선 모두 알고 계실 것이라 생각합니다. -두 번째는 _접근자 프로퍼티(accessor property)_ 라 불리는 새로운 종류의 프로퍼티입니다. 접근자 프로퍼티의 본질은 함수인데, 이 함수는 값을 획득(get)하고 설정(set)하는 역할을 담당합니다. 그런데 외부 코드에서는 함수가 아닌 일반적인 프로퍼티처럼 보입니다. +두 번째는 *접근자 프로퍼티(accessor property)* 라 불리는 새로운 종류의 프로퍼티입니다. 접근자 프로퍼티의 본질은 함수인데, 이 함수는 값을 획득(get)하고 설정(set)하는 역할을 담당합니다. 그런데 외부 코드에서는 함수가 아닌 일반적인 프로퍼티처럼 보입니다. ## getter와 setter @@ -29,7 +30,7 @@ getter 메서드는 `obj.propName`을 사용해 프로퍼티를 읽으려고 할 ```js let user = { name: "John", - surname: "Smith", + surname: "Smith" }; ``` @@ -52,7 +53,7 @@ alert(user.fullName); // John Smith */!* ``` -바깥 코드에선 접근자 프로퍼티를 일반 프로퍼티처럼 사용할 수 있습니다. 접근자 프로퍼티는 이런 아이디어에서 출발했습니다. 접근자 프로퍼티를 사용하면 함수처럼 _호출_ 하지 않고, 일반 프로퍼티에서 값에 접근하는 것처럼 평범하게 `user.fullName`을 사용해 프로퍼티 값을 _얻을 수 있습니다_. 나머지 작업은 getter 메서드가 뒷단에서 처리해줍니다. +바깥 코드에선 접근자 프로퍼티를 일반 프로퍼티처럼 사용할 수 있습니다. 접근자 프로퍼티는 이런 아이디어에서 출발했습니다. 접근자 프로퍼티를 사용하면 함수처럼 *호출* 하지 않고, 일반 프로퍼티에서 값에 접근하는 것처럼 평범하게 `user.fullName`을 사용해 프로퍼티 값을 *얻을 수 있습니다*. 나머지 작업은 getter 메서드가 뒷단에서 처리해줍니다. 한편, 위 예시의 `fullName`은 getter 메서드만 가지고 있기 때문에 `user.fullName=`을 사용해 값을 할당하려고 하면 에러가 발생합니다. @@ -164,13 +165,11 @@ let user = { set name(value) { if (value.length < 4) { - alert( - "입력하신 값이 너무 짧습니다. 네 글자 이상으로 구성된 이름을 입력하세요.", - ); + alert("입력하신 값이 너무 짧습니다. 네 글자 이상으로 구성된 이름을 입력하세요."); return; } this._name = value; - }, + } }; user.name = "Pete"; @@ -179,13 +178,14 @@ alert(user.name); // Pete user.name = ""; // 너무 짧은 이름을 할당하려 함 ``` -`user`의 이름은 `_name`에 저장되고, 프로퍼티에 접근하는 것은 getter와 setter를 통해 이뤄집니다. +`user`의 이름은 `_name`에 저장되고, 프로퍼티에 접근하는 것은 getter와 setter를 통해 이뤄집니다. 기술적으론 외부 코드에서 `user._name`을 사용해 이름에 바로 접근할 수 있습니다. 그러나 밑줄 `"_"` 로 시작하는 프로퍼티는 객체 내부에서만 활용하고, 외부에서는 건드리지 않는 것이 관습입니다. -## 호환성을 위해 사용하기 -접근자 프로퍼티는 언제 어느 때나 getter와 setter를 사용해 데이터 프로퍼티의 행동과 값을 원하는 대로 조정할 수 있게 해준다는 점에서 유용합니다. +## 호환성을 위해 사용하기 + + 접근자 프로퍼티는 언제 어느 때나 getter와 setter를 사용해 데이터 프로퍼티의 행동과 값을 원하는 대로 조정할 수 있게 해준다는 점에서 유용합니다. 데이터 프로퍼티 `name`과 `age`를 사용해서 사용자를 나타내는 객체를 구현한다고 가정해봅시다. @@ -197,7 +197,7 @@ function User(name, age) { let john = new User("John", 25); -alert(john.age); // 25 +alert( john.age ); // 25 ``` 그런데 곧 요구사항이 바뀌어서 `age` 대신에 `birthday`를 저장해야 한다고 해보겠습니다. `age`보다는 `birthday`가 더 정확하고 편리하기 때문이죠.