<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sjoleee</title>
        <link>https://velog.io/</link>
        <description>상조의 개발일지</description>
        <lastBuildDate>Thu, 20 Jun 2024 01:05:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sjoleee</title>
            <url>https://velog.velcdn.com/images/sjoleee_/profile/1feeb49d-9096-4061-bac2-0e102533e3a9/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sjoleee. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sjoleee_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[블로그 이사갑니다~~]]></title>
            <link>https://velog.io/@sjoleee_/%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%9D%B4%EC%82%AC</link>
            <guid>https://velog.io/@sjoleee_/%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%9D%B4%EC%82%AC</guid>
            <pubDate>Thu, 20 Jun 2024 01:05:14 GMT</pubDate>
            <description><![CDATA[<p>차일피일 미루던 블로그를 드디어 만들었습니다.
<a href="https://blog.sjoleee.info/">https://blog.sjoleee.info/</a></p>
<p>벨로그 글을 전부 옮기진 않았고 몇개만 옮겼습니다
참고로 해당 블로그는 템플릿으로 만들어 두었으니 필요하시면 사용해 보세요 츄라이</p>
<p><a href="https://github.com/sjoleee/very-simple-blog">https://github.com/sjoleee/very-simple-blog</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[블로그를 이전할 예정입니다.]]></title>
            <link>https://velog.io/@sjoleee_/%EB%B8%94%EB%A1%9C%EA%B7%B8%EB%A5%BC-%EC%9D%B4%EC%A0%84%ED%95%A0-%EC%98%88%EC%A0%95%EC%9E%85%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@sjoleee_/%EB%B8%94%EB%A1%9C%EA%B7%B8%EB%A5%BC-%EC%9D%B4%EC%A0%84%ED%95%A0-%EC%98%88%EC%A0%95%EC%9E%85%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Thu, 04 Jan 2024 05:27:23 GMT</pubDate>
            <description><![CDATA[<p>어느 순간부터 벨로그가 느릴때가 있다거나 접속이 안된다거나 해서 걱정이 많았습니다.</p>
<p>그래서 개인 블로그를 제작해야겠다고 생각은 하고 있었는데, 동아리에 회사에 스터디에... 너무 하고있는게 많아서 차일피일 미루고 있었어요.</p>
<p>현재 블로그에 작성된 글들은 제가 개발을 시작한지 얼마 안된 시기에 작성했던 글이라 부끄럽기도 하고, 1년간 개발하면서 정말 많은 것을 배웠는데 기록을 남기지 않아서 참 아쉬웠습니다.</p>
<p>이런저런 생각을 갖고 있던 와중, 지난 달 부터인가 갑자기 제 집 와이파이로 벨로그 접속이 안되기 시작했습니다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/2f533d5e-c761-4206-ae6f-97c12cc32f4d/image.png" alt=""></p>
<p>이유는 잘 모르겠구요, 인증서 문제라는데 제가 사는 원룸의 인터넷이 kt였나... 아무튼 그런 문제가 있지 않을까 싶습니다. 자세히 조사해보기엔 해야할 일이 너무 많아서 미뤄두고 있었어요.</p>
<p>이번 기회에 블로그 맛깔나게 만들어서 기록하는 데에 재미를 붙여보려고 합니다.
다 만들면 다시 만나요 👋</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[디프만 13기를 추억하며]]></title>
            <link>https://velog.io/@sjoleee_/%EB%94%94%ED%94%84%EB%A7%8C-13%EA%B8%B0%EB%A5%BC-%EC%B6%94%EC%96%B5%ED%95%98%EB%A9%B0</link>
            <guid>https://velog.io/@sjoleee_/%EB%94%94%ED%94%84%EB%A7%8C-13%EA%B8%B0%EB%A5%BC-%EC%B6%94%EC%96%B5%ED%95%98%EB%A9%B0</guid>
            <pubDate>Wed, 27 Sep 2023 05:35:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>주의) 본 포스팅은 정보 전달을 위한 것이 아닙니다.
디프만 13기가 흘러간 타임라인에 맞춰 당시를 추억하는 글이에요.</p>
</blockquote>
<h1 id="0">0</h1>
<p>나와 디프만의 인연은 2022년으로 거슬러 올라간다.
당시 재직중이던 회사에서 디프만 후원 담당자였는데, 매우매우 불미스러운 일이 있어서 디프만에 대한 나의 인식은 최악이었다.</p>
<p>퇴사하고 개발자로 전직한 뒤, DND라는 동아리를 했다. DND에서 같은 조를 했던 친구들과는 아직도 자주 연락하고, 종종 모이기도 하며 친하게 지내고 있다.
하지만 아쉬웠던 점은 활동 기간이 짧다는 점, 거기다 인원도 적다보니 기간 내에 개발을 마치기가 쉽지 않다는 점이었다.</p>
<p>뭔가 아쉬워서 다른 동아리를 찾아보았는데, 마침 디프만 모집기간이었다.
이전 일때문에 찜찜했지만, 활동 기간도 길고 팀 규모도 크고, 현직자 비율도 꽤나 높아서 지원하기로 했다.</p>
<h1 id="1">1</h1>
<p>대부분 동아리에서 자소서는 받는데, 디프만은 특이하게 추가로 기술면접이 있었다.
나에게는 기술적으로 어려운 질문이 없었고, 같이 면접보신 분에게는 peerDeps 관련된 질문이 있었던 것만 기억난다. 이때 면접관이었던 <a href="https://www.hyesungoh.xyz/">혜성이</a>와 한 팀이 되었다.(👍)</p>
<p>확실히 동아리가 체계적이라는 느낌이 들었는데, 화상면접을 위한 맵을 따로 만들어놓고(이때는 게더타운이 아니었던 것 같다.) 시간도 잘 지켜져서 좋았다. 모집 결과는 금방금방 나왔었던 걸로 기억한다. 운영진이 열일을 하지 않았을까?</p>
<h1 id="2">2</h1>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/ec09e9d5-85bf-4e23-8220-12d4e1c5aaa5/image.png" alt=""></p>
<p>뭐라고 해야하나. 킥오프? 오티?
어쨌든 동아리 활동 시작을 위한 첫 모임은 오프라인에서 진행됐다.
강남역에서 크게 멀지 않은 곳이라고 생각해서 걸어갔는데 생각보단 좀 멀었다. 첫 날부터 지각할뻔 했다.</p>
<p>가서 뻘쭘하게 자리잡고 앉아있었는데, 후원사 소개같은 걸 했는지 잘 기억이 안난다. 그러다 팀별로 모여서 둥글게 둘러앉아 자기소개도 하고 그랬었는데, 솔직히 좀 시끄러워서 팀원들이 뭐라고 했는지 하나도 안들렸다ㅋㅋㅋㅋㅋ 그냥 &#39;아~ 끄덕끄덕~&#39; 했다. 생각보다 연령층이 다양해서 놀랐다. 내가 제일 나이 많을 줄...</p>
<p>그리고 뭔가 회식을 했던 것 같다. 잘 기억이 안난다. 고기를 먹었던 것 같다.
다들 그렇겠지만 처음 만난거라 어색했다.
나는 어색한 사람들과는 밥을 잘 못먹어서(조금만 먹게된다) 집가서 뭘 더 먹었던 것 같다.</p>
<h1 id="3">3</h1>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/4b11886c-8e0e-4bd6-9f48-0d01eda2e3e6/image.png" alt=""></p>
<p>회의는 온라인으로 진행되었다. 주 1회, 게더타운에서 진행했는데 아이데이션, 기술스텍 등에 대해 논의했다.
우리 팀은 추진력이 좋은 팀장 <a href="https://github.com/dojinyou">도진이</a>를 만나서 금세 아이디어를 결정하고 개발을 시작할 수 있었다.</p>
<p>아시다시피 프론트엔드는 디자인이 나오기 전까지 별로 할 게 없다.
그나마 할 일이 스캐폴딩인데, 스캐폴딩을 비롯해서 CICD까지 거의 혜성이가 멱살잡고 이끌어서 내가 기여한게 없었다. 혜성이는 정말 물건이다.</p>
<h1 id="4">4</h1>
<p>이것도 정말 대단한데, 디자이너들이 엄청난 속도로 디자인을 뽑아주었다. <a href="https://www.behance.net/gallery/175696341/Na-Lab-">(Behance Link)</a>
디자인 토큰 정의부터 컴포넌트 정리까지 완벽했다. (솔직히 이때는 이게 대단한 건줄 몰랐다...)</p>
<p>당시 회사에서 컴포넌트 개발과 밀접한 일을 하고 있었기 때문에 이런저런 컴포넌트와 커스텀 훅 개발에 열심히 참여했다.
근데 회사 일이 너무 바빠서 좀 버겁긴 했었다 ㅠㅠ 그나마 컴포넌트 개발은 회사에서 하던 일이랑 비슷해서 금방금방 할 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/117e4a7a-5e3c-41d8-8b4c-d64ff0973af2/image.png" alt="">
<em>svg로 한땀한땀 그려낸 반원차트</em></p>
<p>아마 현직자라면(특히 야근이 많다면) 디프만 활동과 직장을 병행하기가 쉽지 않으리라 생각한다. 다음 날 출근해야하는데, 새벽에 퇴근하고 나서 개발을 할 수 있을 리가...🥲</p>
<h1 id="5">5</h1>
<p>이때쯤 디프만에서 네트워킹 데이를 진행했다.
디프만에 참가한 이유 중에 네트워킹도 있었기 때문에 굉장히 기대했으나.. 사실 이때는 우리 팀도 그닥 친하지 않았던 것 같다ㅋㅋㅋㅋ 
디프만 측에서 랜덤하게 팀과 팀을 매칭해줘서 자연스럽게 같이 어울릴 수 있게 해주었고, 나는 <a href="https://github.com/kimyouknow">윤호님</a>과 짝꿍(?)이 되어 여기저기 돌아다녔다. 돌아다녔다기 보다는 스티커만 받으러 다녔다...?</p>
<p>위에서 어색한 사람들과는 밥도 잘 못먹는다고 했는데, 매칭되었던 7조 분들이 다들 그런 편이라 뒤풀이때도 재미있었다. 뭔가 7조에는 손석구 닮으신 분이 계시다는 이야기를 하루 종일 들었던 것 같다.(근데 진짜 좀 닮긴 하셨더라) 아, 그리고 이때 도진이가 술먹으면서 소리를 너무 질러서 민원이 들어왔었다 ㅋㅋㅋㅋㅋㅋㅋㅋ</p>
<p>요런 자리가 한번정도 더 있었으면 좋았을 것 같은데, 그 뒤로는 쭉 개발만 달려서 좀 아쉽긴 했다. 근데 있었어도 참여할 여유가 됐을까?🤔</p>
<h1 id="6">6</h1>
<p>원래는 중간 발표 시기에 맞춰서 1차 배포를 진행하기로 했었는데, 현실적으로 조금 무리였다고 생각한다. 그래도 꽤나 근접하게까지 개발하는데 성공했고, 중간 발표때는 어떻게 잘... 발표를 했다. 했나? 왜 기억이 안나는지 모르겠다.</p>
<p>발표 이후 회고를 진행했다.
다들 빠른 개발 진행을 좋았던 점으로 꼽았던 것 같다.
딱히 크리티컬한 아쉬웠던 점이 나오지 않았었는데, 나는 아무리 생각해도 팀원들이 별로 안친한 것 같다고 생각했다. 술 마실때 &#39;이제 말 놓자~&#39; 하고 다음 날 회의때는 다시 존댓말하는 사이랄까...</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/df82554b-3c71-4c8d-92be-c984162777dc/image.png" alt=""></p>
<p>나는 돌려서 말하지 않는 개상남자다. 그래서 아쉬운 점에 덜친하다고 적었다(원래 안친함으로 적으려다 고쳤다.)
덕분에 조금 더 오프라인 모임(모각작 + 저녁 + 술)이 활성화 된 것 같다. 휴~ 다행.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/cb2e2577-a550-4c02-b614-827fbacdd83c/image.png" alt=""></p>
<h1 id="7">7</h1>
<p>1차 배포를 최대한 앞당겨서 진행하기로 했고, 회사 일은 점점 더 바빠졌다.
컴포넌트 작업이 끝나고 페이지 작업에 들어갔지만 나는 최대한 미루고 미뤘다. 업무 특성상 새벽에도 이슈가 계속 생겨서 동아리 개발과 병행하는게 쉽지 않았다.
이상하게 업무가 많을 땐 많고, 적을 땐 또 너무 적었다. 적어질때를 기다리면서 디프만 개발을 미뤄두었다. 그래도 컨텍스트를 놓치지 않으려고 새벽에 일하면서도 피그마에서 팀원들이 어떤걸 작업하는지 계속 모니터링했다. 그게 최선이었달까🥲</p>
<p>근데 결국 업무가 계속 많아서 날 잡고 디프만 개발을 후루룩 끝내버렸다.
혜성이, <a href="https://github.com/sumi-0011">수미</a>, <a href="https://github.com/oyeon-kwon">오연누나</a>가 잘 만들어놨는데 내 코드만 개판인 것 같아서 신경이 쓰였다. 추상화가 전혀 되어있지 않은 날 것 그대로의 코드랄까...</p>
<p>어쨌든, 다행히도 개발하면서 api 관련된 문제가 하나도 없어서 후딱 끝낼 수 있었다. 개발 과정에서 프론트-백 간의 소통 문제나 버그들이 0에 수렴한 프로젝트는 이게 처음이었던 것 같다.</p>
<h1 id="8">8</h1>
<p>1차 배포 이후, 실제 사용해보니 자잘한 버그들이 있었고, UX적으로 개선해야할 부분들도 있었다. 그래도 사용에 무리가 없는 마이너한 버그들이었고, 혜성이와 수미가 거의 다 빠르게 대응해주었다.</p>
<p>이때쯤부터 팀에 기여하고 있는게 하나도 없다는 생각이 커져서 조금 우울했다. 괜히 개발은 안하고 인구수만 채워서 다른 팀원들의 부담이 커진게 아닌가 싶었다.
그렇다고 내가 뭐 입코딩만으로 도움을 줄 수 있는 엄청난 개발자도 아니어서... 매일 감사의 정권지르기 1000회를 실시했다(구라임)</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/c9e5a892-483a-4757-a432-e0ab79962f1b/image.png" alt=""></p>
<h1 id="9">9</h1>
<p>2차 배포는 사실 큰 기능이 추가된 것은 아니었다. 이런 저런 사용성 개선과 결과페이지 및 다운로드 기능 등이 추가되었다. 이 부분은 수미가 많이 고생해주었다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/b84f8b29-609b-4703-aa28-58553fb719fc/image.png" alt=""></p>
<p>이때 또 다시 툴팁을 만들게 되었는데... 당시 직장에서 만들었던 툴팁을 거의 재활용하듯 다시 만들었다. 근데 매우 치명적인 버그를 찾아내버렸다ㅋㅋㅋㅋㅋ 직장에서 사용하던 툴팁도 테스트해봤는데 동일한 버그가 있었고, 아직도 해결법은 못찾았다.</p>
<p>아니, 찾았는데 잘 안된다...🥲
언젠가 꼭 해결해야하는 숙제랄까...?</p>
<h1 id="10">10</h1>
<p>최종 발표는 정말 다들 열심히 준비했다. 특히 디자이너들이 열심히 준비해줘서 테이블보도 깔고 포스터도 붙이고, 단체 티셔츠도 만들었다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/560caac4-f61b-4225-8fc0-30c822bfdf4b/image.png" alt=""></p>
<p>결과는 대상!
물론 나의 기여도는 높지 않았지만, 그래도 대상은 기쁜 일이랄까.
다들 너무 열심히 해줘서 좋은 결과가 있었다고 생각한다.
특히 프론트엔드는 혜성이가 있어서 너무 든든했다. 손도 빠르고 워낙 좋은 코드를 짜는 친구라...</p>
<p>이때 술을 너무 많이 마셔서 힘들었다🤮</p>
<h1 id="11">11</h1>
<p>디프만을 하면서 정말 많은 사람과 알게되었다.
일단 회사건 디프만이건 홍대 미대 사람들이 너무 많아서 한 다리만 건너면 다 아는 사이가 되었다.
심지어 지금 재직중인 회사 디자이너분도 홍머...
어딜 가나 홍대 미대가 있는게 신기하다. 과 정원이 엄청 많은건가 싶다.</p>
<h1 id="12">12</h1>
<p>엠티를 가게 되었다.
나랩 두번째 엠티였는데, 첫 엠티에 참여하지 못해서 굉장히 아쉬웠기 때문에 두번째는 꼭 참여해야겠다고 생각했다.
분명 대학생때는 엠티가면 기억 잃고 시체방에 널부러져서 잠들었던 것 같은데, 나이를 먹으니 본능적으로 조절하게 되는건지... 그냥 우리가 술을 별로 안마신 건지 취하지 않았다.
30먹고 엠티는 좀 에바긴 함;</p>
<h1 id="13">13</h1>
<p>회사에서 컴포넌트 개발만 해서 서비스 개발에 익숙치 않았는데, 디프만을 하면서 많이 배울 수 있었다.
맨날 아티클만 읽어보고 써볼 일은 없었는데, 나랩에 이것저것 사용해볼 수 있어서 좋았다(물론 혜성이가 해놓은거 갖다 쓰기만 함)
나랩 코드가 너무 깔끔하게 잘 작성되어 있어서 가끔 회사에서 개발할 때도 참고하게 된다ㅋㅋㅋㅋ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github Actions를 이용한 CI 구축하기(Prettier, ESLint, TSC)]]></title>
            <link>https://velog.io/@sjoleee_/CICD-Github-Actions%EB%A1%9C-CI-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0Prettier-ESLint-TSC</link>
            <guid>https://velog.io/@sjoleee_/CICD-Github-Actions%EB%A1%9C-CI-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0Prettier-ESLint-TSC</guid>
            <pubDate>Sat, 14 Jan 2023 14:42:02 GMT</pubDate>
            <description><![CDATA[<h1 id="왜-ci">왜 CI?</h1>
<blockquote>
<p>🧐 CI란, 소프트웨어 공학에서 지속적 통합(continuous integration, CI)은 지속적으로 품질 관리(Quality Control)를 적용하는 프로세스를 실행하는 것이다. 
<em>(위키피디아)</em></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/eda2a20c-c935-479e-9b51-dfdb2750777b/image.png" alt=""></p>
<p>예전에 진행했던 프로젝트에서, 오류있는 코드를 그대로 올려버리는 바람에 크게 문제가 된 적이 있었다.</p>
<p>1차적으로 문제없는 코드를 작성해야 하고,
2차적으로 문제가 있나 없나 잘 확인해야 할테지만...
사람이라는게 완벽할 수가 없다.</p>
<p>따라서 우리는 <strong>CI, 지속적 통합</strong>을 통해 <strong>신뢰할 수 있는 코드</strong>를 공유해야한다.
<em>(그렇지 않으면 대체 어디서부터 문제였는지, 뭐가 문제인지 커밋 기록을 다 뜯어보면서 고쳐야한다...)
(특히 저 스샷의 경우에는 코드가 잘못되었다기 보다는 파일의 대소문자를 수정하면서 생긴 오류였다. 코드로는 찾아낼 수가 없었다...)</em></p>
<p>요즘 잘 활용하고 있는 Github Actions를 사용한 CI 세팅을 기록하고자 한다!</p>
<blockquote>
<p><strong>😅 본 글은 왕왕초보 버전임을 밝힙니다!!</strong></p>
</blockquote>
<h1 id="1️⃣-eslint">1️⃣ ESLint</h1>
<p>프로젝트를 처음 시작한다고 가정하자.
CNA으로 Next + ts 프로젝트를 시작하고 나면, 나는 가장 먼저 <strong>ESLint</strong>를 세팅한다.</p>
<p><em>(CNA로 프로젝트를 구성할 경우, ESLint 사용할거냐고 물어보는 절차가 있을 것이다.
YES를 입력하면 ESLint를 별도로 설치할 필요는 없다.)</em></p>
<pre><code>npm i --save-dev eslint eslint-config-next @typescript-eslint/eslint-plugin @typescript-eslint/parser</code></pre><p>ESLint를 설치한 후, <code>.eslintrc</code>에서 규칙을 작성하자.</p>
<pre><code>//.eslintrc

{
  &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
  &quot;extends&quot;: [&quot;eslint:recommended&quot;, &quot;next&quot;, &quot;next/core-web-vitals&quot;,&quot;prettier&quot;, &quot;plugin:@typescript-eslint/recommended&quot;],
  &quot;rules&quot;: {
    &quot;prefer-const&quot;: &quot;warn&quot;,
    &quot;no-undef&quot;: &quot;off&quot;,
    &quot;import/order&quot;: [
      &quot;warn&quot;,
      {
        &quot;groups&quot;: [&quot;builtin&quot;, &quot;external&quot;, [&quot;parent&quot;, &quot;sibling&quot;], &quot;index&quot;],
        &quot;newlines-between&quot;: &quot;always&quot;
      }
    ],
    &quot;@typescript-eslint/no-unnecessary-type-constraint&quot;: &quot;off&quot;,
    &quot;@typescript-eslint/no-explicit-any&quot;: &quot;warn&quot;,
    &quot;@next/next/no-img-element&quot;: &quot;off&quot;,
    &quot;@typescript-eslint/no-empty-interface&quot;: &quot;off&quot;
  }
}</code></pre><p>나는 AirBnB의 ESLint룰을 좋아하지 않는다.
너무 빡빡하다 ㅠㅠ
recommended로 세팅하고, 필요한 룰을 그때 추가하는 방식으로 사용하면 편하다.</p>
<p>링크에서 체크되어있는 룰들이 recommended에 적용되어 있다.
<a href="https://eslint.org/docs/latest/rules/">recommended룰 확인하기</a></p>
<p>개인적으로 추가해서 좋았던 룰은 import문의 순서를 정하는 <code>import/order</code> 옵션이었다.
훨씬 가독성이 높아지는 기분이 든다!</p>
<h1 id="2️⃣-prettier">2️⃣ Prettier</h1>
<p><strong>ESLint와 Prettier의 차이</strong>를 알고 있는가?
둘 다 Formatter인데, 우리는 왜 둘을 함께 사용하고 있는 걸까?</p>
<p>ESLint는 <strong>코드의 품질 향상</strong>이 목적,
Prettier는 <strong>코드의 가독성(예쁘게 정돈)</strong>이 목적이라고 할 수 있다.</p>
<p>따라서 &quot;스프레드 문법 쓰지마&quot; 같은 코드 품질 관련된 역할은 ESLint가 담당하고,
&quot;쌍따옴표를 쓸거냐?&quot;, &quot;세미콜론 붙일거냐?&quot; 같은 정돈하는 역할은 Prettier가 담당한다.</p>
<p>이렇게 두 Formatter를 다른 목적으로 사용하지만, 겹치는 룰이 있으면 문제가 될 수 있다.
<em><del>ESLint는 A로 고치고, Prettier는 B로 고치고, ESLint는 A로 고치고, Prettier는 B로 ...</del></em></p>
<p>따라서 우리는 ESLint에게 &#39;너, Prettier 형아 말 들어!&#39;라고 설정해줄 필요가 있다.</p>
<pre><code>npm i --save-dev prettier eslint-config-prettier eslint-plugin-prettier</code></pre><p>eslint-config-prettier와 eslint-plugin-prettier을 통해 Prettier를 우선할 수 있다.</p>
<pre><code>//.prettierrc

{
  &quot;singleQuote&quot;: true,
  &quot;semi&quot;: true,
  &quot;useTabs&quot;: false,
  &quot;tabWidth&quot;: 2,
  &quot;trailingComma&quot;: &quot;all&quot;,
  &quot;printWidth&quot;: 100,
  &quot;arrowParens&quot;: &quot;always&quot;
}</code></pre><p>위 Prettier 룰은 지극히 개인적인 취향이다.
나는 큰따옴표가 뭔가 든든~하게 생겨서 좋아한다.
큰 이유는 없다. 
작은따옴표를 더 많이 선호하시는 것 같긴 하다... 왜일까?
백틱이랑 구분도 잘되고 좋은뎅...</p>
<h1 id="3️⃣-github-actions">3️⃣ Github Actions</h1>
<p>우리가 구성할 CI는</p>
<ul>
<li>Prettier</li>
<li>ESLint</li>
<li>TSC</li>
</ul>
<p>순서로 진행될 것이다.</p>
<p>Prettier, ESLint 다음에 나오는 TSC는 타입스크립트 컴파일러다.
즉, 타입스크립트를 자바스크립트(es5)로 변환하는 과정에서 에러가 있나 검사하는 과정이다.</p>
<p>이제 위 세가지 작업을 자동으로 수행하도록 세팅해보자.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/02365b58-9387-4202-8f17-1cac3146f3ee/image.png" alt=""></p>
<p>레포지토리의 Actions 탭에서 설정할 수 있다.
하지만, IDE에서 바로 작성해도 무관하다.</p>
<pre><code>// .github/workflows/node.js.yml

name: sjoleee CI

on: [ push ]

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [ 16.x, 18.x ]

    steps:
      - uses: actions/checkout@v3

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: &#39;yarn&#39;

      - name: Get yarn cache directory path
        id: yarn-cache-dir-path
        run: echo &quot;dir=$(yarn cache dir)&quot; &gt;&gt;$GITHUB_OUTPUT

      - uses: actions/cache@v3
        id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != &#39;true&#39;`)
        with:
          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles(&#39;**/yarn.lock&#39;) }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      - name: Install the project dependencies
        run: yarn install

      - name: Prettier check
        run: yarn prettier

      - name: Lint check
        run: yarn lint

      - name: TS check
        run: yarn tsc</code></pre><p>뭔가 길다.
쭉 살펴보면 어떤 환경에서 어떤 절차를 거치며 앱을 빌드할지 정의해놓은 것이다.</p>
<p>먼저, node-version은 현재 lts인 18버전, 그리고 16버전으로 설정했다.
이렇게 해두면 버전별로 CI를 수행하여 버전에 따른 이상이 없는지 검증할 수 있다.</p>
<p>steps에서 어떤 절차를 거칠지 정의하는데,
uses 항목에서 <code>actions/checkout@v3</code> <code>actions/setup-node@v3</code> 등이 보인다.</p>
<p>이것들은 누군가 미리 만들어놓은 액션인데, <a href="https://github.com/marketplace?type=actions">깃허브 마켓플레이스</a>에서 검색하여 사용할 수 있다.</p>
<p>절차 중에 <code>actions/cache@v3</code>를 사용하는 캐싱 절차가 있는데, 이것에 대한 설명은 좋은 글이 있어서 대체하고자 한다.
<a href="https://thearchivelog.dev/article/caching-dependencies-to-speed-up-workflows/">https://thearchivelog.dev/article/caching-dependencies-to-speed-up-workflows/</a></p>
<p>노드를 설치하고, 의존성을 설치하고...
그리고 나서 Prettier, Lint, TS 체크 과정이 보인다.
그런데, <code>run: yarn prettier</code> ...? 이런 스크립트가 있었나?</p>
<p>없었다. 작성해줘야 제대로 동작한다.</p>
<pre><code>// package.json

{
...

  &quot;scripts&quot;: {
    ...

    &quot;lint&quot;: &quot;next lint&quot;,
    &quot;prettier&quot;: &quot;prettier --write **/*.{ts,tsx}&quot;,
    &quot;tsc&quot;: &quot;tsc&quot;
  },

...
}</code></pre><p>이렇게 체크를 위한 스크립트까지 작성하고 나면 완성!</p>
<h1 id="4️⃣-branch-protection-rules">4️⃣ Branch protection rules</h1>
<p>이렇게 구축한 CI, 통과해야지만 머지할 수 있도록 설정해보자.</p>
<p>레포지토리 &gt; Settings &gt; Branches 로 진입하면 <strong>Branch protection rules</strong>을 설정할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/e5200cea-a275-4a0a-8a2e-54685ec71a37/image.png" alt=""></p>
<p>한 명의 approve가 있어야 머지할 수 있다는 규칙을 추가했다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/854ea691-8e74-432e-a764-8c77a38e42f3/image.png" alt=""></p>
<p>노드 16, 18버전 체크가 통과해야 머지할 수 있다는 규칙을 추가했다.</p>
<h1 id="결과">결과</h1>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/99030a24-5db6-4f8d-959c-75183509ba2f/image.png" alt=""></p>
<p>이제 협업하는 개발자가 여럿이더라도 코드 품질을 보장받으며 협업할 수 있게 되었다.</p>
<p>사용하면서도 긴가민가... 이렇게 쓰는게 맞나... 했던 부분들이 있었는데, 글을 작성하며 많이 공부할 수 있었다.
테스트코드도 작성해서 추가하면 좋을 것 같은데, 공부해서 다음 개인프로젝트에는 반영해봐야겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[약간 늦은 구름톤 4기 후기 : 팀 놀놀]]></title>
            <link>https://velog.io/@sjoleee_/%EC%95%BD%EA%B0%84-%EB%8A%A6%EC%9D%80-%EA%B5%AC%EB%A6%84%ED%86%A4-4%EA%B8%B0-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@sjoleee_/%EC%95%BD%EA%B0%84-%EB%8A%A6%EC%9D%80-%EA%B5%AC%EB%A6%84%ED%86%A4-4%EA%B8%B0-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 02 Jan 2023 19:30:52 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sjoleee_/post/d59313a3-4ff6-43c4-9684-63a508fc7aef/image.png" alt=""></p>
<h1 id="☁️-구름톤">☁️ 구름톤?</h1>
<blockquote>
<p>구름톤은 카카오와 구름이 주최하는 오프라인 해커톤(무려 제주도에서!)이다.
<a href="https://9oormthon.goorm.io/">https://9oormthon.goorm.io/</a></p>
</blockquote>
<p>나는 4기로 참여했고, 기간은 <code>12월 13일 ~ 12월 16일</code>로 4일간 진행되었다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/34c4f17e-3d0e-4de2-8809-5509c9a2e8f5/image.png" alt=""></p>
<p>혹시 구름톤에 대한 정보를 얻기 위해 이 글을 읽는 분이 계실수도 있으니 몇가지 적어보고자 한다.</p>
<h2 id="지원서">지원서</h2>
<p>지원서는 그냥 솔직하게 적었다.</p>
<p>개발하는 것을 좋아하는데, 특히 프로젝트, 과제, 해커톤처럼 <strong>목표와 제한 시간이 뚜렷한 상황에서 몰입하는 것을 좋아한다</strong>는 내용을 조물조물 버무려서 적었다.</p>
<p>그리고 <strong>해커톤 아이디어</strong> 문항이 있었는데, 아이디어 구상하는데만 2일 정도 걸렸다.
정말... 어려웠다 ㅋㅋㅋㅋㅋㅋ 억지로 짜낸 아이디어가 너무 별로라서 찜찜한 기분이 들었다.</p>
<p>지금 다시봐도 별로다...
그래도 선발된걸 보면 아무래도 지원서의 아이디어는 크게 중요하지 않은 것 같다(?)</p>
<h2 id="숙소">숙소</h2>
<p>첫날은 숙소가 지원되지 않기 때문에 에어비앤비로 제주시청 근처에 숙소를 잡았다.
교육장까지는 버스로 이동할 생각이었는데, 알고보니 <strong>제주도의 버스 배차간격은 30분이었다.</strong>
결국 택시로 이동했고, 택시비가 꽤나 나왔다...ㅜㅜ
대부분의 참가자가 제주시청 근처로 숙소를 잡으니 <strong>택시 동행</strong>을 구하도록 하자!</p>
<p>그리고, 1일차 교육이 오전 10시에 시작한다.</p>
<ul>
<li>오전 5~6시 쯤 기상</li>
<li>강남에서 김포공항으로 이동</li>
<li>제주도로 이동</li>
<li>교육장으로 이동(무거운 백팩과 함께...)</li>
</ul>
<p>나는 위 일정이 너무 힘들 것 같아서 전날 미리 제주도에 도착했다.
전날에 미리 도착해서 푹 쉬고 관광도 하고 교육에 참여하는 것을 추천한다.
그리고 3~4시쯤 도착하는 항공권은 굉장히 저렴하다!</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/e771478d-71a9-41d8-97c7-272ab5628eca/image.JPG" alt=""></p>
<p>숙소 냉장고에는 귤이 있었다.
제주도에는 귤이 지천에 널려있었다.
진짜로 어딜가도 귤이 있었다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/47d8eb4f-3317-4741-a8a8-3a0172ad0420/image.JPG" alt=""></p>
<p>구름톤 전날 카페에서 MUI + react-hook-form과 씨름함</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/87c34d90-b367-4363-a0d0-dafd5da1f26d/image.JPG" alt=""></p>
<p>구름톤 전날 혼밥했던 고기국수.
미친 양이었다 진짜</p>
<h2 id="날씨">날씨</h2>
<p>제주도를 한 번도 가보지 않은 분이 계실수도 있어서 적어본다.
제주도는 기온은 높을지 몰라도 바람이 많이 불어서 춥다.
그리고 날씨가 시시각각 바뀐다.
해커톤 하는 내내 우박, 눈, 비, 갑자기 맑은 날씨에 끝나고 나서는 대설주의보...</p>
<p>꼭 외투를 챙기자.
생존의 문제다........</p>
<h1 id="👀-구름톤에-참여한-이유">👀 구름톤에 참여한 이유</h1>
<p>위에서 적었듯이, 나는 개발하는 것을 좋아하는데 특히 프로젝트, 과제, 해커톤처럼 <strong>목표와 제한 시간이 뚜렷한 상황에서 몰입하는 것을 좋아한다</strong></p>
<p>그리고, <strong>잘하는 사람들의 노하우와 테크닉을 배우고 싶어서</strong> 참여했다.
혼자 뚝딱뚝딱 개발하다 보면 분명히 동작은 하는데, 잘 하고 있는 거 맞나? 라는 생각이 들 때가 많다.
구름톤에 참여한 많은 고수들과 코치님들께 배우고 싶었다.</p>
<p>그리고, <strong>오프라인 협업을 하고싶어서</strong> 참여했다.
온라인 협업은 여러 번 해봤는데, 오프라인으로 개발 프로젝트를 진행한 적이 없어서 아쉬웠다.
온라인으로 협업하는게 자연스러워졌다고는 해도 오프라인이 소통하기 편하고 재미있고, 유대감이 잘 생긴다고 생각한다.</p>
<h1 id="🍊-구름톤에서-무엇을-했나">🍊 구름톤에서 무엇을 했나?</h1>
<h2 id="1일차--교육">1일차 : 교육</h2>
<p>개인적으로 바쁜 일이 있어서 구름톤 며칠 전부터 제대로 잠도 못자고... 심지어는 전날 밤샘 코딩을 하고 참여해서 정신이 없었다.
점심먹다 코피 터지고, 컨디션이 정말 최악이었다 ㅠㅠ</p>
<p>그래서 기억나는게 별로 없다 ㅋㅋㅋㅋㅋㅋ
<strong>지급된 맨투맨 사이즈가 엄청 작았다는 거?</strong>
(다음에 참여하시는 분들은 1~2사이즈 큰 걸로 신청하세요!!)</p>
<p><strong>GDS 강의</strong>를 기대했는데, 당시 나는 <code>MUI</code>와 <code>react-hook-form</code>을 함께 사용하면서 엄청난 불편함을 느꼈기 때문에 <strong>구름에서는 외부 라이브러리와 디자인 시스템을 함께 사용할 때 겪는 불편함을 어떻게 해소하고 있을지 궁금했다.</strong>
(하지만 훅폼과 부트스트랩 기반 디자인시스템을 함께 사용하면 어쩔 수 없이 불편한가보더라... ㅜㅜ)</p>
<p>GDS를 직접 사용할 수 있도록 실습시간이 주어졌는데, &#39;구축하는데 정말 고생하셨겠다&#39;는 생각이 먼저 들었다.</p>
<p>그리고 <code>rem</code>을 사용하는 것이나 <code>grid</code>를 활용하여 개발하는 것을 보면서 <code>px</code> <code>flex</code>로만 사용했던 자신을 반성하게 되었다.
이런저런 기술을 많이 경험해보는 자세를 가져야겠다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/6ad5cba4-74b6-4765-b20f-af66bd09c272/image.png" alt=""></p>
<p>교육이 끝나고 제주시청 근처 고기집에서 참가자들 단체로 삼겹살을 먹었다.
제주도의 백돼지는 정말 육지의 백돼지와 다른 것인가...?
잘 모르겠지만 맛있었다.</p>
<p>먹으면서 이런저런 이야기를 많이 했는데, 주로 내일 있을 아이디어 발표에 대한 이야기였다.
이때 뇌를 거치지 않고 여러 아이디어를 쏟아냈는데, 이 중 하나를 구체화해서 피피티를 만들었다.</p>
<p>그리고 고기집 사장님한테 제주도에 불편한 게 있다면 말씀해달라고 했는데 <strong>&#39;제주도는 도로가 너무 좁다&#39;</strong> 고 도로좀 개선해달라고 하셨다.</p>
<h2 id="2일차--팀-빌딩-개발환경-세팅">2일차 : 팀 빌딩, 개발환경 세팅</h2>
<p>제주도로 출발하기 전에 썩 괜찮은 아이디어를 생각해놓은게 있었지만 추가로 공개된 주제에 부합하지 않아서 어쩔 수 없이 개발할게 조금 더 많은 서비스를 발표했다.</p>
<p>그래서 <strong>나는 빨리 개발하고 놀고싶으니, 어떡하면 더 간단하게 만들 수 있을지 같이 고민해주실 분들을 찾는다</strong>고 덧붙였다.</p>
<p>그리고 놀랍게도 순식간에 팀 빌딩이 완료되었다.
아마 4기에서 가장 빨리 결성된 팀이 아닐지</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/9f30bc54-35bb-4dac-bd59-d0d6bb175140/image.JPG" alt=""></p>
<p>팀 놀놀!(놀땐 놀자는 뜻)</p>
<p>우리팀에는 제주도 4년차 도민이신 영호님이 자차 보유자셔서(!)
여기저기 편하게 다니고 드라이브도 할 수 있었다!!</p>
<p>팀은 빠르게 꾸려졌지만, 그렇다고 개발을 바로 뚝딱 시작할 수는 없었다.
UI기획 &amp; 디자인작업이 진행되는동안 프론트 담당인 현주님과 나는 컨벤션을 정하며 슬슬 개발환경을 세팅했다.</p>
<p>분명 팀빌딩때 <strong>React + JS로 빠르게 개발하자!</strong> 고 얘기했었는데... Next + TS로 급선회했다.
(다 해놓고 보니 Next를 사용하는게 큰 장점이 없는 서비스긴 하다... ㅋㅋㅋㅋㅋㅋㅋㅋ)
이 기회를 빌어서 갑자기 Next에 TS로 개발하게되신 현주님께 심심한 사과를 전한다...</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/e123f41a-f5e3-4d32-a377-f5c09009af81/image.JPG" alt=""></p>
<p>살치살 스테이크가 나온다길래 기대했던 비어파티.
생각보다 살치살 스테이크는 별로였고 다른 음식들이 엄청 맛있었다!
구름에서 어색하지 않게 질문카드도 잘 준비해주셔서 재미있게 이야기를 나눴다.</p>
<p>10시쯤 비어파티를 빠져나와 다시 자리에 앉았다.
간단하게 깃헙액션으로 CI를 설정해두고 이것저것 하다보니 어느새 밤이 되었고, 본격적인 개발보다는 다음날 빠르게 개발하기 위한 레이아웃 정도만 만들고 쏠랑 자러갔다.</p>
<p>우리 팀의 규칙은 <strong>잠은 꼭 자기</strong> 였다.
(근데 나만 잔 것 같다...)</p>
<h2 id="3일차--개발-개발-개발">3일차 : 개발, 개발, 개발...</h2>
<p>자고 아침일찍 와보니 디자이너인 다영님이 고작 몇시간만에 엄청난 디자인을 완성시켜두셨다.
이건 못참지...
덕분에 간만에 즐겁게 코딩할 수 있었다.</p>
<p>밤샘 디자인해주신 다영님의 컨디션이 매우 걱정되었는데, 가능하면 푹 쉬실 수 있게 알잘딱으로 개발해야겠다고 생각했다.</p>
<p>하지만 나의 실력부족으로 인해 꽤나 많은 수정요청을 드린 것 같다 ㅋㅋㅋㅋㅋㅠㅠ 
아.. 아니... 시간ㅇㅣ 충분하면 다 구현할 ㅅ ㅜ있었다고,,... </p>
<p>그리고 개발범위가 정말정말 필요한 최소한의 기능만으로 잘 나와서 걱정했던 것보다 훨씬 수월하게 개발할 수 있었다.</p>
<p>밥먹고 잠깐 바닷가로 산책도 다녀오고,
다같이 바다뷰 카페가서 개발하자!고 나왔는데 정말 빡집중해서 묵언코딩하기도 하고</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/d23b9e36-57b4-4749-b1c8-9d6cbdc18df6/image.JPG" alt=""></p>
<p>저녁먹으면서 <strong>우리 빨리 끝내면 술마시러가자며... 거의 끝난거같다며...</strong> 그런 얘기도 나누고...
하지만 아침까지 그냥 코딩만 했다.(해치웠나? 같은 대사는 하는게 아니다.)</p>
<p>새벽에 map 메서드를 중첩하여 사용하는 코드를 작성하는데 비몽사몽 return을 빼먹어놓고 왜안되냐며 좌절하기도 했다 ㅋㅋㅋㅋ
이때 코치님이 아기달래듯 차근차근~ 리턴을 넣어주면~ 되겠죠~? 라고 알려주시는데 나는 거의 혼이 나가서 어버버대고...</p>
<p>그리고 새벽에 깃그래프 보면서 엄청 웃었던게 기억난다.
진짜... 브랜치가 뭐가 어떻게 된거지 싶었는데 깃에서 아무런 문제도 없었던게 참 신기했다 ㅋㅋㅋㅋㅋㅋ
(심지어 지금까지도 우리는 main 브랜치를 사용하지 않고 있다.)</p>
<p>그리고 엄청 편했던게, <strong>종근님이 너무 완벽하게 해주셔서 백엔드 걱정을 하나도 안했다.</strong>
나는 에러없이 완벽한 API가 바로바로 만들어질 수 있다는 걸 처음 알았다 ㅋㅋㅋㅋㅋㅋ
종근님이랑 같이 안했으면 어쩔뻔...</p>
<h2 id="4일차--끝">4일차 : 끝</h2>
<p>발표 직전까지도 계속 버그를 잡고, 기능을 구현했다.
점심으로 나온 롯데리아 한우버거는 구경도 못해보고 계속 개발만 했다.</p>
<p>자꾸 상단 네비게이션이 지멋대로 틀어져서 시간을 많이 잡아먹었는데, 현주님이 바로 해결법을 알려주셔서 정말정말 완벽한 뷰를 구현할 수 있었다.
솔직히 진짜 잘만들었음...</p>
<p>솔직히 이때 개발은 약간의 욕심이었는데, 기능은 이미 완성되었고 CSS라던지, SEO라던지, 채널톡이라던지... 완성도를 높이고 있었다.</p>
<p>이때 개발을 멈추고 발표준비를 열심히 도왔다면 어땠을까 싶다 ㅠㅠ
발표 시간이 짧기도 해서 압축적으로 전달해야하는 어려운 발표였는데... 개발한답시고 발표자인 영호님께만 큰 부담을 드린게 아닌가 싶다... ㅠㅠㅠ</p>
<p>결과는... 무관의 제왕이랄까 하하
내 맘속에선 1등이야...</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/7cee971a-bb95-4a68-8a18-c02e5c8f3838/image.png" alt="">
제주도 관광객의 쓰레기문제를 해결하는 잉여 여행용품 나눔 플랫폼, <strong>제주고올래</strong>
<a href="https://jejugolleh.life/">https://jejugolleh.life/</a></p>
<ul>
<li><a href="https://www.notion.so/dayounglee0518/Product-99fe0a565ae14d299e224d45d6b9fea2">소개페이지</a></li>
<li><a href="https://github.com/NolNol9oormthon/UseMineClient">클라이언트 레포지토리</a></li>
<li><a href="https://github.com/NolNol9oormthon/UseMineServer">서버 레포지토리</a></li>
</ul>
<h1 id="👏-구름톤에서-얻은-것">👏 구름톤에서 얻은 것</h1>
<h2 id="팀-놀놀">팀 놀놀</h2>
<p>우리 팀이 비록 다른팀처럼 핵인싸는 아닐지라도(...)
다들 뛰어난 실력과 책임감을 가진 완벽한 동료들이다!</p>
<p>기획자이자 4년차 제주도민으로 팀의 힐링을 책임진 <strong>영호님</strong>,
그저 빛... 기적처럼 하룻밤만에 서비스 디자인을 완성시켜버린 디자이너 <strong>다영님</strong>,
감동적이게도 첨부터 나와 같이하겠다고 해주신 든든한 백엔드 <strong>종근당님</strong>,
그리고 전E, 현I라 그런지 팀의 분위기메이커를 도맡아주신 실질적 개발팀장 <strong>현주님</strong>(역시 현직자야...)</p>
<p>울 팀원들과 함께 제주고올래 서비스도 틈날때마다 개선하고 있고, 기회 생긴다면 공모전이라던지 여기저기 출품해볼 생각이다!
<del><em>(무관으로 남을 서비스가 아니야......)</em></del></p>
<p>한번 모이면 좋을 것 같은데... 안타깝게도 거주지가 달라서 쉽게 모이긴 어려울 듯 하다 ㅠㅠ
다수결에 따라 서울로 올라오세요들,......</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/c722b5bb-5aa3-437a-8637-a196c77e4d4b/image.png" alt=""></p>
<h2 id="나의-성향">나의 성향</h2>
<p>나는 확실히 목표가 있어야 효율적으로 움직이는 사람이라는 것을 깨달았다 ㅋㅋㅋㅋ
이걸 어떻게 하루이틀만에 다 개발했지 싶을 정도로 엄청난 효율이었달까</p>
<p>지금 생각해보면 시험은 죄다 벼락치기, 심지어 수능도 벼락치기로 공부했었던 내 자신...
역시 나는 결승선이 눈에 보여야 열심히 하는 것 같다 하하!</p>
<p>그런 이유로 뽀모도로를 더욱 열심히 해야겠다고 생각했다.
집에서는 뽀모도로 타이머를 잘 사용하고 있는데, 카페에서는 들고다니기가 좀 그래서 적당한 앱을 찾았다.</p>
<p><img src="https://is4-ssl.mzstatic.com/image/thumb/Purple7/v4/10/e6/40/10e640c9-7344-35d8-631a-14bee79a257a/5263d5c90befa29ad75dd14dcc6da9d9.jpg/626x0w.webp" alt="">
드래그하여 사용하는 뽀모도로 타이머 <a href="https://apps.apple.com/kr/app/gestimer/id990588172?mt=12">gestimer</a></p>
<p>예전에 인프런 다닐때 그랩님 유튜브 촬영하면서 주워들은 생산성 앱인데, 번뜩 생각나서 얼른 구매했다.
나와 비슷한 분이 계시다면 사용해보기를!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] react-hook-form의 checkbox selectAll]]></title>
            <link>https://velog.io/@sjoleee_/React-react-hook-form%EC%9D%98-checkbox-selectAll</link>
            <guid>https://velog.io/@sjoleee_/React-react-hook-form%EC%9D%98-checkbox-selectAll</guid>
            <pubDate>Mon, 28 Nov 2022 15:39:02 GMT</pubDate>
            <description><![CDATA[<h1 id="react-hook-form">react-hook-form</h1>
<p>간만에 <code>react-hook-form</code>으로 여러 체크박스를 한 번에 전체 선택하고, 선택 취소할 수 있는 <code>selectAll</code> 기능을 구현해보았다.</p>
<p>대부분의 예제가 간단하게 한 페이지, 한 컴포넌트 안에서 이루어진다.
하지만 실제로 개발을 하다보면 컴포넌트 구조가 복잡해지기 마련이다.
input이 여기도 있고 저기도 있고.. button은 또 따로 저~ 구석에 있고...</p>
<p>이번에는 input 을 각기 다른 컴포넌트에 분산해두고, button 역시 input들과 다른 곳에 두어서 <code>react-hook-form</code>의 <code>useFormContext</code>를 활용해보았다.</p>
<h1 id="app-구조">App 구조</h1>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/80c7d5ae-dc0f-4dd3-8214-c310fbdc6978/image.jpg" alt=""></p>
<p>SubmitButton을 클릭하면 Table 내부의 각 체크박스들의 checked 값과 Header 내부의 input 2개의 값을 가져오고 싶다. + SelectAll 체크박스를 통한 전체 선택 및 선택 해제</p>
<p>이를 위해 컴포넌트를 작성했다. (css는 작성하지 않았다)</p>
<pre><code>📦src
 ┣ 📂components
 ┃ ┣ 📜Header.tsx
 ┃ ┣ 📜SubmitButton.tsx
 ┃ ┗ 📜Table.tsx
 ┣ 📜App.tsx
 ┗ 📜index.tsx</code></pre><p><img src="https://velog.velcdn.com/images/sjoleee_/post/920b1248-763d-4419-a4d9-090f7cedac39/image.png" alt=""></p>
<h1 id="비제어-컴포넌트">비제어 컴포넌트</h1>
<p><code>react-hook-form</code>은 <strong>비제어 컴포넌트</strong>를 활용하고 있다.
<code>ref</code> 기반의 비제어 컴포넌트는 <strong>렌더링 최적화</strong> 측면에서 상당한 이점을 가질 수 있고, 제어 컴포넌트에서 <code>setState</code>가 제대로 동작하는지 끊임없이 의심하게 되는 부담을 덜 수 있어서 매우 유용하다.(ㅋㅋㅋ)</p>
<h1 id="appjsx">App.jsx</h1>
<pre><code class="language-js">import React from &quot;react&quot;;
import { FormProvider, useForm } from &quot;react-hook-form&quot;;
import Header from &quot;./components/Header&quot;;
import Table from &quot;./components/Table&quot;;

function App() {
  const methods = useForm();
  return (
    &lt;FormProvider {...methods}&gt;
      &lt;Header /&gt;
      &lt;Table /&gt;
    &lt;/FormProvider&gt;
  );
}

export default App;</code></pre>
<p><code>FormProvider</code>로 컴포넌트를 감싸주면 어디서든 react-hook-form에 등록된 input의 값에 접근할 수 있다.</p>
<h1 id="headerjsx">Header.jsx</h1>
<pre><code class="language-js">import { useFormContext } from &quot;react-hook-form&quot;;
import SubmitButton from &quot;./SubmitButton&quot;;

const Header = () =&gt; {
  const { register } = useFormContext();
  return (
    &lt;&gt;
      &lt;input {...register(&quot;input1&quot;)} placeholder=&quot;input1&quot; /&gt;
      &lt;input {...register(&quot;input2&quot;)} placeholder=&quot;input2&quot; /&gt;
      &lt;button
        type=&quot;button&quot;
        onClick={() =&gt; {
          console.log(&quot;다른행동&quot;);
        }}
      &gt;
        다른버튼
      &lt;/button&gt;
      &lt;SubmitButton /&gt;
    &lt;/&gt;
  );
};

export default Header;</code></pre>
<p>input 2개와 button 2개를 작성해주었다.</p>
<p>다른 행동을 하는 버튼이 존재하는 이유는, App 전체를 form으로 감싸서 react-hook-form의 <code>handleSubmit</code>을 사용하는 시나리오를 생각했기 때문이다.
form 내부의 button은 클릭 시 기본적으로 submit을 실행한다.
다만, <code>type=&quot;button&quot;</code> 으로 그것을 방지할 수 있다.</p>
<p>그런데, form 내부의 input에 focus를 두고 enter를 입력하면 submit이 실행된다.
이것도 막으려면... input에 submit을 방지하는 코드를 추가해주어야 한다.</p>
<pre><code class="language-js">onKeyDown={(e) =&gt; {
  if (e.code === &quot;Enter&quot;) e.preventDefault();
}}</code></pre>
<p>다만, submit을 사용하지 않고 getValues로 가져온 값을 가공하는 경우가 더 많을 것 같아서 form으로 감싸주지 않았다.</p>
<h1 id="tablejsx">Table.jsx</h1>
<pre><code class="language-js">import React from &quot;react&quot;;
import { useFormContext } from &quot;react-hook-form&quot;;

const data = [&quot;S00001&quot;, &quot;S00002&quot;, &quot;S00003&quot;, &quot;S00004&quot;, &quot;S00005&quot;];

const Table = () =&gt; {
  const { register, setValue } = useFormContext();

// 모든 체크박스를 체크, 체크해제하는 로직
  const handleSelectAll = (e: React.FormEvent&lt;HTMLInputElement&gt;) =&gt; {
    if (e.currentTarget.checked) { // selectAll 체크박스가 체크되면
      data.forEach((item) =&gt; {
        setValue(`id.${item}`, true); // 모든 체크박스의 value를 true로
      });
    } else { // selectAll 체크박스가 체크해제되면
      data.forEach((item) =&gt; {
        setValue(`id.${item}`, false); // 모든 체크박스의 value를 false로
      });
    }
  };

  return (
    &lt;&gt;
      &lt;div&gt;
        &lt;input
          type=&quot;checkbox&quot;
          {...register(&quot;selectAll&quot;)} // 전체 체크, 체크해제를 담당하는 체크박스
          onChange={handleSelectAll}
        /&gt;
      &lt;/div&gt;
      {data.map((item) =&gt; ( // data를 체크박스로
        &lt;input {...register(`id.${item}`)} key={`${item}`} type=&quot;checkbox&quot; /&gt;
      ))}
    &lt;/&gt;
  );
};

export default Table;</code></pre>
<p><code>register</code>를 통해 등록해놓은 input은 react-hook-form이 제공하는 각종 메서드를 통해 접근 및 수정할 수 있다.</p>
<p>id.{item}으로 등록할 경우, data에서 id 객체의 프로퍼티로 조회된다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/c48e1c53-125b-4c8a-83a2-0c87f79b8c31/image.gif" alt=""></p>
<h1 id="submitbuttonjsx">SubmitButton.jsx</h1>
<pre><code class="language-js">import { useFormContext } from &quot;react-hook-form&quot;;

const SubmitButton = () =&gt; {
  const { getValues } = useFormContext();
  return (
    &lt;button
      onClick={() =&gt; {
        console.log(getValues());
      }}
    &gt;
      제출하기
    &lt;/button&gt;
  );
};

export default SubmitButton;</code></pre>
<p>SubmitButton에서 getValues를 이용하면 FormProvider 내의 모든 input의 값을 조회할 수 있다.
이것으로 복잡한 구조의 input이라도 손쉽게 다룰 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/7ae11836-09ab-41a3-9625-938a3545ea1a/image.gif" alt=""></p>
<h1 id="리렌더링">리렌더링</h1>
<p>비제어 컴포넌트의 장점은 state를 변화시키지 않기 때문에 렌더링에서 이득을 본다는 점이다.
작동하는 화면에서 사실 React Developer Tools에서 제공하는 렌더링 체크 기능을 실행해둔 상태다.
<strong>즉, 위 작업을 하는 동안 한 번도 리렌더링이 발생하지 않았다.</strong>
꽤나 만족스러운 결과물이다.</p>
<p>다만, 한 가지 문제가 있다.
아래 이미지처럼 모든 체크박스를 체크할 경우 자동으로 selectAll 체크박스가 체크되고, 하나라도 체크 해제될 경우 selectAll 체크박스가 체크 해제되는 기능을 구현하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/5f912d12-0f64-423a-800e-2ffdf6262137/image.gif" alt=""></p>
<p>잘 작동하는데 뭐가 문제냐면,
실제로는 체크박스를 조작할 때마다 리렌더링이 발생하고 있다.</p>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/3c69ccad-f032-4197-98bc-d30f1f9916ab/image.gif" alt=""></p>
<p>이렇게 리렌더링이 발생하는 방식으로 구현한 이유는...
해당 기능을 구현하기 위해서는 모든 체크박스의 값이 true가 될 때를 찾아야 하기 때문이다.
즉, react-hook-form이 제공하는 watch를 사용해서 체크박스를 조작할 때마다 값을 가져와야 하는데, watch는 context를 구독한 컴포넌트의 렌더링을 트리거한다.</p>
<p>getValues는 실행된 시점의 값을 가져오는 것이라서 적합하지 않다.</p>
<pre><code class="language-js">  useEffect(
    () =&gt; {
      if (data.every((item) =&gt; watch(`id.${item}`) === true)) {
        setValue(&quot;selectAll&quot;, true);
      } else {
        setValue(&quot;selectAll&quot;, false);
      }
    },
    data.map((item) =&gt; watch(`id.${item}`))
  );</code></pre>
<p>위 기능을 구현하게 되면 체크박스를 조작할 때마다 리렌더링이 발생하는 비효율적인 상황이 벌어진다.</p>
<p>useWatch를 사용하면 리렌더링의 범위를 해당 컴포넌트로 한정할 수 있으니 필요하다면 사용해볼 수 있겠다.</p>
<p>혹시 위 기능을 렌더링 관점에서 효율적으로 구현할 수 있는 방법을 알고 있다면.. 알려주세요...</p>
<h1 id="느낀점">느낀점</h1>
<p>react-hook-form을 공부하면서 비제어 컴포넌트의 유용함을 느꼈다.
개발중인 아이돌만들기 서비스에도 여러개의 아이템을 선택해서 다음 페이지로 넘겨줘야하는 기능이 있는데, 비제어 방식으로 동작하게 만든다면 렌더링 관점에서 훨씬 효율적일 것 같다.</p>
<p>다만... 아직까지 내공이 부족한 탓인지 렌더링을 최적화 하면서 사용하기가 꽤나 까다롭다 ㅠㅠ</p>
<blockquote>
<p>작성된 내용은 아래 레포지토리에서 확인할 수 있습니다.
<a href="https://github.com/sjoleee/reacthookform-practice">https://github.com/sjoleee/reacthookform-practice</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 26. ES6 함수의 추가 기능]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-26.-ES6-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B6%94%EA%B0%80-%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-26.-ES6-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B6%94%EA%B0%80-%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Sun, 27 Nov 2022 16:10:37 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="26-es6-함수의-추가-기능">26. ES6 함수의 추가 기능</h1>
<h2 id="261-함수의-구분">26.1 함수의 구분</h2>
<p>ES6 이전의 모든 함수는 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있다.
일반적으로 메서드라고 부르던 객체에 바인딩된 함수 역시 <code>callable</code>이며 <code>constructor</code>이다.</p>
<p>객체에 바인딩된 함수가 <code>constructor</code>라는 것은 <code>prototype</code> 프로퍼티를 가지며, 프로토타입 객체도 생성한다는 것을 의미하기 때문에 성능 면에서도 문제가 있다.
콜백 함수 역시 <code>constructor</code>이기 때문에 불필요한 프로토타입 객체를 생성한다.</p>
<p>이를 해결하기 위해 <code>non-constructor</code>인 ES6의 메서드와 화살표 함수가 등장했다.</p>
<h2 id="262-메서드">26.2 메서드</h2>
<p>ES6 사양에서 메서드는 <strong>메서드 축약 표현으로 정의된 함수만을 의미한다.</strong>
ES6 사양에서 메서드는 인스턴스를 생성할 수 없는 <code>non-constructor</code>다. 따라서 생성자 함수로 호출할 수 없다.
인스턴스를 생성할 수 없으므로 <code>prototype</code> 프로퍼티가 없고 프로토타입도 생성하지 않는다.</p>
<p>ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 <code>[[HomeObject]]</code>를 갖는다.
<code>super</code> 참조는 내부 슬롯 <code>[[HomeObject]]</code>를 사용하여 수퍼클래스의 메서드를 참조하므로 ES6 메서드는 <code>super</code> 키워드를 사용할 수 있다.
ES6 메서드가 아닌 함수는 내부 슬롯 <code>[[HomeObject]]</code>를 갖지 않기 때문에 <code>super</code> 키워드를 사용할 수 없다.</p>
<h2 id="263-화살표-함수">26.3 화살표 함수</h2>
<p>기존의 함수 정의 방식보다 간략하며 내부 동작도 기존 함수보다 간략하다.
콜백 함수 내부에서 <code>this</code>가 전역 객체를 가리키는 문제를 해결하기 위한 대안으로 유용하다.</p>
<h3 id="2631-화살표-함수-정의">26.3.1 화살표 함수 정의</h3>
<h4 id="함수-정의">함수 정의</h4>
<p>함수 선언문으로 정의할 수 없고 함수 표현식으로 정의해야 한다.</p>
<h4 id="매개변수-선언">매개변수 선언</h4>
<p>소괄호 안에 매개변수를 선언한다. 매개변수가 한 개인 경우 소괄호를 생략할 수 있다. 매개변수가 없다면 생략할 수 없다.</p>
<h4 id="함수-몸체-정의">함수 몸체 정의</h4>
<p>함수 몸체가 하나의 문으로 구성된다면 중괄호를 생략할 수 있다.
이때 함수 몸체 내부의 문이 값으로 평가될 수 있는 표현식인 문이라면 암묵적으로 반환된다.
생략과 함께 객체 리터럴을 반환하는 경우 객체 리터럴을 소괄호로 감싸 주어야 한다.</p>
<p>화살표 함수도 즉시 실행 함수로 사용할 수 있다.
또한 일급 객체이므로 각종 고차 함수에 인수로 전달할 수 있다.</p>
<h3 id="2632-화살표-함수와-일반-함수의-차이">26.3.2 화살표 함수와 일반 함수의 차이</h3>
<ul>
<li>화살표 함수는 인스턴스를 생성할 수 없는 <code>non-constructor</code>다.</li>
<li>중복된 매개변수 이름을 선언할 수 없다.</li>
<li>화살표 함수는 함수 자체의 <code>this</code> <code>arguments</code> <code>super</code> <code>new.target</code> 바인딩을 갖지 않는다.</li>
</ul>
<h3 id="2633-this">26.3.3 this</h3>
<p>this 바인딩은 함수의 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.
주의할 것은 일반 함수로서 호출되는 콜백 함수의 경우다.
고차 함수의 인수로 전달되어 고차 함수 내부에서 호출되는 콜백 함수도 중첩 함수라고 할 수 있다.</p>
<pre><code class="language-js">class Prefixer {
  constructor(prefix) {
    this.prefix = prefix;
  }

  add(arr) {
    return arr.map(function (item){
      return this.prefix + item; // TypeError
    });
  }
}

const prefixer = new Prefixer(&quot;-webkit-&quot;);
console.log(prefixer.add([&#39;transition&#39;, &#39;user-select&#39;]));</code></pre>
<p>위 예시에서 <code>Array.prototype.map</code> 메서드가 콜백 함수를 일반 함수로서 호출하기 때문에 <code>this</code>가 전역 객체를 가리키게 된다.
그런데 클래스 내부의 모든 코드에는 strict mode가 암묵적으로 적용되고, 이 경우 <code>this</code> 에는 <code>undefined</code>가 바인딩된다.</p>
<p>이렇게 콜백 함수의 <code>this</code>와 외부 함수의 <code>this</code>가 다른 값을 가리키는 문제를 해결하기 위해 ES6 이전에는 다음과 같은 방법을 사용했다.</p>
<ul>
<li><code>this</code>를 회피시킨 후에 콜백 함수 내부에서 사용한다.(<code>that</code>)</li>
<li><code>Array.prototype.map</code>의 두 번째 인수로 <code>this</code>를 전달한다.</li>
<li><code>Function.prototype.bind</code> 메서드를 사용하여 <code>this</code>를 바인딩한다.</li>
</ul>
<p>ES6에서는 화살표 함수를 사용하여 콜백 함수 내부의 <code>this</code> 문제를 해결할 수 있다.
<strong>화살표 함수는 함수 자체의 <code>this</code> 바인딩을 갖지 않는다.
따라서 화살표 함수 내부에서 <code>this</code>를 참조하면 상위 스코프의 <code>this</code>를 그대로 참조한다.
이를 <code>lexical this</code>라 한다.</strong></p>
<p>화살표 함수는 함수 자체의 <code>this</code> 바인딩을 갖지 않기 때문에 <code>Function.prototype.bind/call/apply</code>를 사용해도 화살표 함수 내부의 <code>this</code>를 교체할 수 없다.
<code>Function.prototype.bind/call/apply</code>를 호출할 수 없는 것은 아니고, <code>this</code> 바인딩이 없어서 언제나 상위 스코프의 <code>this</code>바인딩을 참조한다.</p>
<h3 id="2634-super">26.3.4 super</h3>
<p><strong>화살표 함수는 함수 자체의 <code>super</code> 바인딩을 갖지 않는다.
따라서 화살표 함수 내부에서 <code>super</code>를 참조하면 상위 스코프의 <code>super</code>를 참조한다.</strong></p>
<h3 id="2635-arguments">26.3.5 arguments</h3>
<p><strong>화살표 함수는 함수 자체의 <code>arguments</code> 바인딩을 갖지 않는다.
따라서 화살표 함수 내부에서 <code>arguments</code>를 참조하면 상위 스코프의 <code>arguments</code>를 참조한다.</strong>
화살표 함수에서는 <code>arguments</code> 객체를 사용할 수 없으므로 Rest 파라미터를 사용하자.</p>
<h2 id="264-rest-파라미터">26.4 Rest 파라미터</h2>
<h3 id="2641-기본-문법">26.4.1 기본 문법</h3>
<p><code>Rest</code> 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받는다.</p>
<pre><code class="language-js">function foo(param, ...rest) {
  console.log(param); // 1
  console.log(rest); // [2, 3]
}

foo(1, 2, 3);</code></pre>
<p><code>Rest</code> 파라미터는 먼저 선언된 매개변수에 할당된 인수를 제외한 나머지 인수들로 구성된 배열이 할당된다.
따라서 <code>Rest</code> 파라미터는 반드시 마지막 파리머터여야 한다.</p>
<h3 id="2642-rest-파라미터와-arguments-객체">26.4.2 Rest 파라미터와 arguments 객체</h3>
<p><code>arguments</code> 객체는 함수 호출 시 전달된 인수들의 정보를 담고 잇는 순회 가능한 유바 배열 객체이며, 함수 내부에서 지역 변수처럼 사용할 수 있다.
하지만 <code>arguments</code> 객체는 유사 배열 객체이므로 배열 메서드를 사용하기 번거로웠다.
ES6의 <code>Rest</code> 파라미터는 배열로 직접 전달받을 수 있기 때문에 유용하다. 특히, 화살표 함수에서는 반드시 <code>Rest</code> 파라미터를 사용해야 한다.</p>
<h2 id="265-매개변수-기본값">26.5 매개변수 기본값</h2>
<p>자바스크립트 엔진은 매개변수의 개수와 인수의 개수를 체크하지 않는다.
인수가 전달되지 않은 매개변수의 값은 <code>undefined</code>다.
이를 방치하면 의도치 않은 결과가 나올 수 있다.</p>
<pre><code class="language-js">function sum(a, b) {
  return a + b;
}

console.log(sum(1)); // NaN</code></pre>
<p>ES6에서 도입된 매개변수 기본값을 사용하면 인수 체크 및 초기화를 간소화할 수 있다.</p>
<pre><code class="language-js">function sum(a = 0, b = 0) {
  return a + b;
}

console.log(sum(1)); // 1</code></pre>
<p>매개변수 기본값 매개변수에 인수를 전달하지 않은 경우와 <code>undefined</code>를 전달한 경우에만 유효하다.
<code>Rest</code> 파라미터에는 기본값을 지정할 수 없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 25. 클래스]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-25.-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-25.-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Sat, 26 Nov 2022 08:03:41 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="25-클래스">25. 클래스</h1>
<h2 id="251-클래스는-프로토타입의-문법적-설탕인가">25.1 클래스는 프로토타입의 문법적 설탕인가?</h2>
<blockquote>
<p>💡 클래스를 프로토타입 기반 객체 생성 패턴의 단순한 문법적 설탕이라고 보기보다는 새로운 객체 생성 메커니즘으로 보는 것이 좀 더 합당하다.</p>
</blockquote>
<p>클래스와 생성자 함수의 차이는 다음과 같다.</p>
<ul>
<li>클래스를 <code>new</code> 연산자 없이 호출하면 에러가 발생한다. 생성자 함수를 <code>new</code> 연산자 없이 호출하면 일반 함수로 호출된다.</li>
<li>클래스는 상속을 지원하는 <code>extends</code>와 <code>super</code> 키워드를 제공한다.</li>
<li>클래스는 호이스팅이 발생하지 않는 것처럼 동작한다.</li>
<li>클래스 내의 모든 코드에는 암묵적으로 <code>strict mode</code>가 지정되어 실행된다.</li>
<li>클래스의 <code>constructor</code>, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 <code>[[Enumerable]]</code>의 값이 <code>false</code>다. 열거되지 않는다.</li>
</ul>
<p>생성자 함수와 클래스는 프로토타입 기반의 객체지향을 구현했다는 점에서 유사하다.
하지만 클래스는 생성자 함수 기반의 객체 생성 방식보다 견고하고 명료하다.</p>
<h2 id="252-클래스-정의">25.2 클래스 정의</h2>
<p>클래스는 <code>class</code> 키워드를 사용하여 정의한다.
일반적이지는 않지만 표현식으로 정의할 수도 있다.</p>
<pre><code class="language-js">const Person = class {}; // 익명 클래스 표현식
const Person = class MyClass {}; // 기명 클래스 표현식</code></pre>
<p>클래스를 표현식으로 정의할 수 있다는 것은 클래스가 값으로 사용할 수 있는 일급 객체라는 것을 의미한다.</p>
<ul>
<li>무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.</li>
<li>변수나 자료구조에 저장할 수 있다.</li>
<li>함수의 매개변수에게 전달할 수 있다.</li>
<li>함수의 반환값으로 사용할 수 있다.</li>
</ul>
<p>클래스 몸체에는 0개 이상의 메서드만 정의할 수 있다.
constructor, 프로토타입 메서드, 정적 메서드의 세 가지가 있다.
클래스와 생성자 함수의 정의 방식은 형태적인 면에서 매우 유사하다.</p>
<h2 id="253-클래스-호이스팅">25.3 클래스 호이스팅</h2>
<p><strong>클래스는 함수로 평가된다.</strong>
따라서 클래스 선언문으로 정의한 클래스는 함수 선언문과 같이 소스코드 평가 과정, 즉 런타임 이전에 먼저 평가되어 함수 객체를 생성한다.
이때 클래스가 평가되어 생성된 함수 객체는 <code>constructor</code>다.
생성자 함수로서 호출할 수 있는 함수는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성된다.
프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재하기 때문이다.</p>
<p><strong>하지만 클래스는 클래스 정의 이전에 참조할 수 없다.</strong>
마치 호이스팅이 발생하지 않는 것처럼 보이나 그렇지 않다.</p>
<pre><code class="language-js">const Person = &quot; &quot;;

{
  // 호이스팅이 발생하지 않는다면 &quot; &quot;이 출력되어야 한다.
  console.log(Person); // ReferenceError

  class Person {};
}</code></pre>
<p>클래스 선언문도 변수 선언, 함수 정의와 마찬가지로 호이스팅이 발생한다.
단, 클래스는 <code>let</code> <code>const</code> 키워드로 선언한 변수처럼 호이스팅된다.
따라서 클래스 선언문 이전에 TDZ에 빠진다.</p>
<h2 id="254-인스턴스-생성">25.4 인스턴스 생성</h2>
<p>클래스는 생성자 함수이며 <code>new</code> 연산자와 함께 호출되어 인스턴스를 생성한다.</p>
<h2 id="255-메서드">25.5 메서드</h2>
<p>클래스 몸체에는 0개 이상의 메서드만 선언할 수 있다.
클래스 몸체에서 정의할 수 있는 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드의 세 가지가 있다.</p>
<h3 id="2551-constructor">25.5.1 constructor</h3>
<p><code>constructor</code>는 인스턴스를 생성하고 초기화하기 위한 특수한 메서드다.
이름을 변경할 수 없다.</p>
<pre><code class="language-js">class Person {
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name;
  }
}</code></pre>
<p>클래스는 평가되어 함수 객체가 된다.
모든 함수 객체가 가지고 있는 <code>prototype</code> 프로퍼티가 가리키는 프로토타입 객체의 <code>constructor</code> 프로퍼티는 클래스 자신을 가리키고 있다.
이는 클래스가 인스턴스를 생성하는 생성자 함수라는 것을 의미한다.</p>
<p>클래스가 생성한 인스턴스의 내부를 들여다보면 수퍼 클래스의 <code>constructor</code> 내부에서 <code>this</code>에 추가한 프로퍼티가 인스턴스의 프로퍼티로 추가된 것을 확인할 수 있다.
즉, <code>constructor</code> 내부의 <code>this</code>는 생성자 함수와 마찬가지로 클래스가 생성한 인스턴스를 가리킨다.</p>
<p>그런데 클래스가 평가되어 생성된 함수 객체나 인스턴스 어디에도 <code>constructor</code> 메서드를 확인할 수 없다.
이는 <code>constructor</code>가 단순한 메서드가 아니라는 것을 의미한다.
<code>constructor</code>는 메서드로 해석되는 것이 아니라 클래스가 평가되어 생성한 함수 객체 코드의 일부가 된다.
다시 말해, 클래스 정의가 평가되면 <code>constructor</code>의 기술된 동작을 하는 함수 객체가 생성된다.</p>
<ul>
<li><code>constructor</code>는 클래스 내에 최대 한 개만 존재할 수 있다.</li>
<li><code>constructor</code>는 생략할 수 있다. 생략하면 빈 <code>constructor</code>가 암묵적으로 정의된다.</li>
<li>인스턴스를 초기화하려면 <code>constructor</code>를 생략해서는 안된다.</li>
<li><code>constructor</code>는 별도의 반환문을 갖지 않아야 한다. 암묵적으로 <code>this</code>, 즉 인스턴스를 반환하기 때문이다.</li>
</ul>
<h3 id="2552-프로토타입-메서드">25.5.2 프로토타입 메서드</h3>
<p>생성자 함수를 사용하여 프로토타입 메서드를 생성하기 위해서는 명시적으로 프로토타입에 메서드를 추가해야 한다.
하지만 클래스 몸체에서 정의한 메서드는 기본적으로 프로토타입 메서드가 된다.</p>
<pre><code class="language-js">class Person {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log(`Hi, ${this.name}`)
  }
}

const me = new Person(&quot;Lee&quot;);
me.sayHi(); // Hi, Lee</code></pre>
<p>생성자 함수와 마찬가지로 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 된다.</p>
<h3 id="2553-정적-메서드">25.5.3 정적 메서드</h3>
<p>정적 메서드는 인스턴스를 생성하지 않아도 호출할 수 있는 메서드를 말한다.</p>
<p>생성자 함수의 경우 정적 메서드를 생성하기 위해서 명시적으로 생성자 함수에 메서드를 추가해야한다.
하지만 클래스에서는 메서드에 <code>static</code> 키워드를 붙이면 적적 메서드(클래스 메서드)가 된다.</p>
<pre><code class="language-js">class Person {
  constructor(name) {
    this.name = name;
  }

  static sayHi() {
    console.log(&quot;Hi&quot;)
  }
}

Person.sayHi(); // Hi</code></pre>
<p>정적 메서드는 클래스에 바인딩된 메서드가 된다.
인스턴스의 프로토타입 체인 상에 존재하지 않기 때문에 인스턴스로는 호출할 수 없다.</p>
<h3 id="2554-정적-메서드와-프로토타입-메서드의-차이">25.5.4 정적 메서드와 프로토타입 메서드의 차이</h3>
<ul>
<li>정적 메서드와 프로토타입 메서드는 속해 있는 프로토타입 체인이 다르다.</li>
<li>정적 메서드는 클래스로 호출하고, 프로토타입 메서드는 인스턴스로 호출한다.</li>
<li>정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다. (this 바인딩이 다르다)</li>
</ul>
<h3 id="2555-클래스에서-정의한-메서드의-특징">25.5.5 클래스에서 정의한 메서드의 특징</h3>
<ul>
<li><code>function</code> 키워드를 생략한 메서드 축약 표현을 사용한다.</li>
<li>클래스에 메서드를 정의할 때는 콤마가 필요 없다.</li>
<li>암묵적으로 strict mode로 실행된다.</li>
<li><code>for ... in</code> 문이나 <code>Object.keys</code> 메서드 등으로 열거할 수 없다. <code>[[Enumerable]]</code>의 값이 <code>false</code>다.</li>
<li>내부 메서드 <code>[[Construct]]</code>를 갖지 않는 <code>non-constructor</code>다.</li>
</ul>
<h2 id="256-클래스의-인스턴스-생성-과정">25.6 클래스의 인스턴스 생성 과정</h2>
<p><code>new</code> 연산자와 함께 클래스를 호출하면 클래스의 내부 메서드 <code>[[Construct]]</code>가 호출된다.
이때 다음과 같은 과정을 거쳐 인스턴스가 생성된다.</p>
<h3 id="1-인스턴스-생성과-this-바인딩">1. 인스턴스 생성과 this 바인딩</h3>
<p><code>new</code> 연산자와 함께 클래스를 호출하면 <code>constructor</code> 내부 코드가 실행되기에 앞서 암묵저긍로 빈 객체가 생성된다.
이 빈 객체가 바로 클래스가 생성한 인스턴스다.
이때 클래스가 생성한 인스턴스의 프로토타입으로 클래스의 <code>prototype</code> 프로퍼티가 가리키는 객체가 설정된다.
그리고 인스턴스는 <code>this</code>에 바인딩된다.</p>
<h3 id="2-인스턴스-초기화">2. 인스턴스 초기화</h3>
<p><code>constructor</code>의 내부 코드가 실행되어 <code>this</code>에 바인딩되어 있는 인스턴스를 초기화한다.
즉, 인스턴스에 프로퍼티를 추가하고 <code>constructor</code>가 인수로 전달받은 초기값으로 인스턴스의 프로퍼티 값을 초기화한다.
만약 <code>constructor</code>가 생략되었다면 이 과정도 생략된다.</p>
<h3 id="3-인스턴스-반환">3. 인스턴스 반환</h3>
<p>클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.</p>
<h2 id="257-프로퍼티">25.7 프로퍼티</h2>
<h3 id="2571-인스턴스-프로퍼티">25.7.1 인스턴스 프로퍼티</h3>
<p>인스턴스 프로퍼티는 <code>constructor</code> 내부에서 정의해야 한다.</p>
<pre><code class="language-js">class Person {
  constructor(name) {
    this.name = name;
  }
}

const me = new Person(&quot;Lee&quot;);
console.log(me); // Person {name: &quot;Lee&quot;}</code></pre>
<p><code>constructor</code> 내부 코드가 실행되기 이전에 이미 this에는 빈 객체(인스턴스)가 바인딩되어 있다.
생성자 함수에서 인스턴스의 프로퍼티를 정의하는 것과 마찬가지로 <code>constructor</code> 내부에서 <code>this</code>에 인스턴스 프로퍼티를 추가한다.
이로써 인스턴스에 프로퍼티가 추가되어 인스턴스가 초기화된다.
인스턴스 프로퍼티는 언제나 <code>public</code>하다.</p>
<h3 id="2572-접근자-프로퍼티">25.7.2 접근자 프로퍼티</h3>
<p>접근자 프로퍼티는 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티다.
즉, <code>getter</code> <code>setter</code> 함수로 구성되어 있다.</p>
<pre><code class="language-js">class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }

  set fullName(name) {
    [this.firstName, this.lastName] = name.split(&quot; &quot;);
  }
}

const me = new Person(&quot;sangjo&quot;, &quot;Lee&quot;);
console.log(me.firstName); // sangjo 인스턴스 프로퍼티는 public하다.

// 접근자 프로퍼티를 통한 프로퍼티 값의 저장
// fullName에 값을 저장하면 setter 함수가 호출된다.
me.fullName = &quot;gildong Hong&quot;;
console.log(me); // {firstName: &quot;gildong&quot;, lastName: &quot;Hong&quot;}

// 접근자 프로퍼티를 통한 프로퍼티 값의 참조
// fullName에 접근하면 getter 함수가 호출된다.
console.log(me.fullName); // gildong Hong</code></pre>
<p><code>setter</code>는 무엇인가를 프로퍼티에 할당해야 할 때 사용하므로 반드시 매개변수가 있어야 한다.
다만 하나의 값만 할당받기 때문에 단 하나의 매개변수만 선언할 수 있다.</p>
<p>클래스의 메서드는 기본적으로 프로토타입 메서드가 된다.
따라서 클래스의 접근자 프로퍼티 또한 인스턴스 프로퍼티가 아닌 프로토타입의 프로퍼티가 된다.</p>
<h3 id="2573-클래스필드-정의-제안">25.7.3 클래스필드 정의 제안</h3>
<p>클래스 필드는 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어다.
자바스크립트에서 클래스가 생성할 인스턴스의 프로퍼티는 <code>constructor</code> 내부에서 <code>this</code>에 프로퍼티를 추가해야 한다고 위에서 기술하였다.
또한 자바스크립트 클래스 몸체에는 메서드만 선언할 수 있다.</p>
<p>하지만, 실제로 최신 브라우저나 최신 Node.js에서 실행하면 문법 에러가 발생하지 않고 정상 동작한다.</p>
<p>이유는 새로운 표준 사양인 &quot;Class field declarations&quot;가 제안되어 있기 때문이다.</p>
<pre><code class="language-js">class Person {
  name = &quot;Lee&quot;;
}

const me = new Person();
console.log(me); // Person {name: &quot;Lee&quot;}</code></pre>
<p>클래스 몸체에서 클래스필드를 정의하는 경우 <code>this</code>에 클래스 필드를 바인딩해서는 안된다.
<code>this</code>는 <code>constructor</code>와 메서드 내에서만 유효하다.</p>
<pre><code class="language-js">class Person {
  name = &quot;Lee&quot;;
  age; // 초기값을 할당하지 않으면 undefined를 갖는다

  constructor() {
    console.log(name); // ReferenceError
    // 클래스 필드를 참조할 경우 this를 사용해야 한다.
  }
}

const me = new Person();
console.log(me); // Person {name: &quot;Lee&quot;}</code></pre>
<p>인스턴스를 생성할 때 클래스 필드를 초기화할 필요가 있다면(외부의 초기값으로 초기화) <code>constructor</code> 밖에서 클래스 필드를 정의할 필요가 없다.
어차피 인스턴스에 클래스 필드에 해당하는 프로퍼티가 자동으로 추가되기 때문이다.</p>
<p>함수는 일급 객체이므로 함수를 클래스 필드에 할당할 수 있다.
클래스 필드를 통해 메서드를 정의할 수 있는데, 이 경우 인스턴스 메서드가 된다.
모든 클래스 필드는 인스턴스 프로퍼티가 되기 때문이다.
따라서 클래스 필드에 함수를 할당하는 것은 권장하지 않는다.</p>
<h3 id="2574-private-필드-정의-제안">25.7.4 private 필드 정의 제안</h3>
<p>자바스크립트는 캡슐화를 완전하게 지원하지 않는다. 
접근 제한자를 지원하지 않기 때문에 인스턴스 프로퍼티는 언제나 <code>public</code>이다.</p>
<p>그러나 <code>private</code> 필드를 정의할 수 있는 새로운 표준 사양이 제안되어 있다.</p>
<pre><code class="language-js">class Person {
  #name = &quot;&quot;;

  constructor(name) {
    // private 필드 참조
    this.#name = name;
  }
}

const me = new Person(&quot;Lee&quot;);

// private 필드 #name은 클래스 외부에서 참조할 수 없다.
console.log(me.#name); // SyntaxError</code></pre>
<p><code>private</code> 필드의 선두에는 <code>#</code>을 붙여준다. 참조할 때도 마찬가지다.</p>
<p><code>private</code> 필드는 클래스 내부에서만 참조할 수 있다.
하지만 접근자 프로퍼티를 통해 간접적으로 접근하는 방법은 유효하다.</p>
<p><code>private</code> 필드는 반드시 클래스 몸체에 정의해야 한다.
<code>private</code> 필드를 직접 <code>constructor</code>에 정의하면 에러가 발생한다.</p>
<h3 id="2575-static-필드-정의-제안">25.7.5 static 필드 정의 제안</h3>
<p>클래스에는 <code>static</code> 키워드를 사용하여 정적 메스드를 정의할 수 있다.
하지만 <code>static</code> 키워드를 사용하여 정적 필드를 정의할 수는 없었다.</p>
<p>하지만 새로운 표준 사양인 &quot;Static class features&quot;가 제안되어 있다.</p>
<pre><code class="language-js">class MyMath {
  static PI = 22/7;
}

console.log(MyMyth.PI); // 3.142857 ... </code></pre>
<h2 id="258-상속에-의한-클래스-확장">25.8 상속에 의한 클래스 확장</h2>
<h3 id="2581-클래스-상속과-생성자-함수-상속">25.8.1 클래스 상속과 생성자 함수 상속</h3>
<p>프로토타입 기반 상속은 프로토타입 체인을 통해 다른 객체의 자산을 상속받는 것.
상속에 의한 클래스 확장은 기존 클래스를 상속받아 새로은 클래스를 확장하여 정의하는 것이다.</p>
<h3 id="2582-extends-키워드">25.8.2 extends 키워드</h3>
<p>상속을 통해 확장된 클래스를 서브 클래스, 파생 클래스, 자식 클래스라 부르며, 상속된 클래스를 수퍼 클래스, 베이스 클래스, 부모 클래스라고 부른다.</p>
<p><code>extends</code> 키워드의 역할은 수퍼클래스와 서브클래스 간의 상속 관계를 설정하는 것이다.
수퍼클래스와 서브클래스는 인스턴스의 프로토타입 체인뿐 아니라 클래스 간의 프로토타입 체인도 생성한다.
이를 통해 프로토타입 메서드, 정적 메서드 모두 상속이 가능하다.</p>
<h3 id="2583-동적-상속">25.8.3 동적 상속</h3>
<p><code>extends</code> 키워드는 클래스뿐만 아니라 생성자 함수를 상속받아 클래스를 확장할 수도 있다.
단, <code>extends</code> 키워드 앞에는 반드시 클래스가 와야 한다.</p>
<pre><code class="language-js">// 생성자 함수
function Base(a) {
  this.a = a;
}

// 생성자 함수를 상속받는 서브클래스
class Derived extends Base {}

const derived = new Drived(1);
console.log(derived); // Derived {a: 1}</code></pre>
<p><code>extends</code> 키워드 다음에는 클래스 뿐만이 아니라 <code>[[Construct]]</code> 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식을 사용할 수 있다.
이를 통해 동적으로 상속받을 대상을 결정할 수 있다.</p>
<h3 id="2584-서브클래스의-constructor">25.8.4 서브클래스의 constructor</h3>
<p>클래스에서 <code>constructor</code>를 생략하면 비어있는 <code>constructor</code>가 암묵적으로 정의된다.
서브클래스에서 <code>constructor</code>를 생략하면 다음과 같은 <code>constructor</code>가 암묵적으로 정의된다.</p>
<pre><code class="language-js">constructor(...args) { super(...args); }</code></pre>
<p><code>super()</code>는 수퍼클래스의 <code>constructor</code>를 호출하여 인스턴스를 생성한다.</p>
<h3 id="2585-super-키워드">25.8.5 super 키워드</h3>
<p><code>super</code> 키워드는 함수처럼 호출할 수도 있고 식별자처럼 참조할 수 있는 특수한 키워드다.</p>
<ul>
<li><code>super</code>를 호출하면 수퍼클래스의 <code>constructor</code>를 호출한다.</li>
<li><code>super</code>를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.</li>
</ul>
<h4 id="super-호출"><code>super</code> 호출</h4>
<p><code>super</code>를 호출하면 수퍼클래스의 <code>constructor</code>를 호출한다.</p>
<pre><code class="language-js">class Base(a) {
  constructor(a) {
    this.a = a;
  }
}

class Derived extends Base {
  // 다음과 같이 암묵적으로 constructor가 정의된다.
  // constructor(...args) { super(...args); }
}

const derived = new Drived(1);
console.log(derived); // Derived {a: 1}</code></pre>
<p><code>new</code> 연산자와 함께 <code>Drived</code> 클래스를 호출하면서 전달한 인수 <code>1</code>은 <code>Drived</code> 클래스의 <code>constructor</code>에 전달되고 <code>super</code> 호출을 통해 <code>Base</code> 클래스의 <code>constructor</code>에 전달된다.</p>
<p><code>super</code>를 호출할 때 주의할 사항은 다음과 같다.</p>
<ul>
<li>서브클래스에서 <code>constructor</code>를 생략하지 않는 경우 서브클래스의 <code>constructor</code>에서는 반드시 <code>super</code>를 호출해야 한다.</li>
<li>서브클래스의 <code>constructor</code>에서 <code>super</code>를 호출하기 전에는 <code>this</code>를 참조할 수 없다.</li>
<li><code>super</code>는 반드시 서브클래스의 <code>constructor</code>에서만 호출한다. 서브클래스가 아닌 클래스의 <code>constructor</code>나 함수에서 <code>super</code>를 호출하면 에러가 발생한다.</li>
</ul>
<h4 id="super-참조"><code>super</code> 참조</h4>
<p>메서드 내에서 <code>super</code>를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.</p>
<h5 id="서브클래스의-프로토타입-메서드-내에서-supersayhi는-수퍼클래스의-프로토타입-메서드-sayhi를-가리킨다">서브클래스의 프로토타입 메서드 내에서 super.sayHi는 수퍼클래스의 프로토타입 메서드 sayHi를 가리킨다.</h5>
<pre><code class="language-js">class Base {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return `Hi, ${this.name}`
  }
}

// 서브클래스
class Derived extends Base {
  sayHi() {
    return `${super.sayHi()}. how r u?`
  }
}

const derived = new Derived(&quot;Lee&quot;);
console.log(derived.sayHi()); // Hi, Lee. how r u?</code></pre>
<p>위 상황에서 서브클래스의 <code>this</code>는 인스턴스를 가리킨다.
우리가 원하는 것은 <code>Base.prototype</code>에 존재하는 <code>sayHi</code> 메서드이기 때문에 <code>this</code>를 사용하면 안된다.</p>
<p>이처럼 <code>super</code> 참조가 동장하기 위해서는 <code>super</code>를 참조하고 있는 메서드(<code>Derived</code>의 <code>sayHi</code>)가 바인딩되어 있는 객체(<code>Derived.prototype</code>)의 프로토타입(<code>Base.prototype</code>)을 찾을 수 있어야 한다.
이를 위해 메서드는 내부 슬롯 <code>[[HomeObject]]</code>를 가지며, 자신을 바인딩하고 있는 객체를 가리킨다.</p>
<p>즉, <code>Derived</code> 클래스의 <code>sayHi</code> 메서드의 <code>[[HomeObject]]</code>는 <code>Derived.prototype</code>이고, 이를 통해 <code>Derived</code> 클래스의 <code>sayHi</code> 메서드 내부의 <code>super</code> 참조가 <code>Base.prototype</code>으로 결정된다.</p>
<p>주의할 것은 ES6의 메서드 축약 표현으로 정의된 함수만이 <code>[[HomeObject]]</code>를 갖는다는 것이다.</p>
<h5 id="서브클래스의-정적-메서드-내에서-supersayhi는-수퍼클래스의-정적-메서드-sayhi를-가리킨다">서브클래스의 정적 메서드 내에서 super.sayHi는 수퍼클래스의 정적 메서드 sayHi를 가리킨다.</h5>
<h3 id="2586-상속-클래스의-인스턴스-생성-과정">25.8.6 상속 클래스의 인스턴스 생성 과정</h3>
<pre><code class="language-js">// 수퍼클래스
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }

  toString() {
    return `width = ${this.width}, height = ${this.height}`
  }
}

// 서브클래스
class ColorRectangle extends Rectangle {
  constructor(width, height, color) {
    super(width, height);
    this.color = color;
  }

  toString() {
    return `${super.toString}, color = ${this.color}`
  }
}

const colorRectangle = new ColorRectangle(2, 4, &#39;red&#39;)
console.log(colorRectangle); // ColorRectangle {width: 2, height: 4, color: &#39;red&#39;}

// 상속을 통해 getArea 메서드 호출
console.log(colorRectangle.getArea()); // 8
// 오버라이딩된 toString 메서드를 호출
console.log(colorRectangle.toString()); // width = 2, height = 4, color = red</code></pre>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/5d6fbf81-b549-4078-9c7b-257973ea80fa/image.jpeg" alt=""></p>
<p>서브클래스 <code>ColorRectangle</code>이 <code>new</code> 연산자와 함께 호출되면 다음 과정을 통해 인스턴스를 생성한다.</p>
<h4 id="1-서브클래스의-super-호출">1. 서브클래스의 super 호출</h4>
<p>자바스크립트 엔진은 클래스를 평가할 때 수퍼클래스와 서브클래스를 구분하기 위해 &quot;base&quot; 또는 &quot;derived&quot;를 값으로 갖는 내부 슬롯 <code>[[ConstructorKind]]</code>를 갖는다.</p>
<p><strong>이를 통해 서브클래스로 구분된 클래스는 자신이 직접 인스턴스를 생성하지 않고 수퍼클래스에게 인스턴스 생성을 위임한다.
이것이 바로 서브클래스의 <code>constructor</code>에서 반드시 <code>super</code>를 호출해야 하는 이유다.</strong></p>
<p>서브클래스의 <code>constructor</code> 내부에 <code>super</code> 호출이 없으면 에러가 발생한다.
실제로 인스턴스를 생성하는 주체는 수퍼클래스이므로 수퍼클래스의 <code>constructor</code>를 호출하는 <code>super</code>가 호출되지 않으면 인스턴스를 생성할 수 없기 때문이다.</p>
<h4 id="2-수퍼클래스의-인스턴스-생성과-this-바인딩">2. 수퍼클래스의 인스턴스 생성과 this 바인딩</h4>
<p>수퍼클래스의 <code>constructor</code> 내부의 코드가 실행되기 이전에 암묵적으로 빈 객체를 생성한다. 이 빈 객체가 바로 클래스가 생성한 인스턴스다.
그리고 이 인스턴스는 <code>this</code>에 바인딩된다.
따라서 수퍼클래스 <code>constructor</code> 내부의 <code>this</code>는 인스턴스를 가리킨다.</p>
<p>분명 인스턴스는 수퍼클래스가 생성하는 것이라고 기술했다.
하지만 <code>new</code> 연산자와 함께 호출된 클래스는 서브클래스다.
따라서 <code>new.target</code>은 서브클래스를 가리킨다.
<code>new.target</code>은 <code>new</code> 연산자와 함께 호출된 함수를 가리키기 때문이다.
따라서 <strong>인스턴스는 <code>new.target</code>이 가리키는 서브클래스가 생성한 것으로 처리된다.</strong></p>
<p>따라서 생성된 인스턴스의 프로토타입은 수퍼클래스의 <code>prototype</code> 프로퍼티가 가리키는 객체가 아니라 서브클래스의 <code>prototype</code> 프로퍼티가 가리키는 객체다.</p>
<h4 id="3-수퍼클래스의-인스턴스-초기화">3. 수퍼클래스의 인스턴스 초기화</h4>
<p>수퍼클래스의 <code>constructor</code>가 실행되어 <code>this</code>에 바인딩되어 있는 인스턴스를 초기화한다.</p>
<h4 id="4-서브클래스-constructor로의-복귀와-this-바인딩">4. 서브클래스 constructor로의 복귀와 this 바인딩</h4>
<p><code>super</code>의 호출이 종료되고 제어 흐름이 서브클래스 <code>constructor</code>로 돌아온다.
<strong>이때 <code>super</code>가 반환한 인스턴스가 <code>this</code>에 바인딩된다.
서브클래스는 별도의 인스턴스를 생성하지 않고 <code>super</code>가 반환한 인스턴스를 <code>this</code>에 바인딩하여 그대로 사용한다.</strong></p>
<p><strong>따라서, <code>super</code>가 호출되지 않으면 서브클래스 <code>constructor</code>에서 <code>this</code>가 생성되지 않는다.</strong>
서브클래스 <code>constructor</code>에서 <code>super</code>를 호출하기 전에는 <code>this</code>를 참조할 수 없는 이유가 이 때문이다.</p>
<h4 id="5-서브클래스의-인스턴스-초기화">5. 서브클래스의 인스턴스 초기화</h4>
<p><code>super</code> 호출 이후, 서브클래스의 <code>constructor</code>에 기술되어 있는 인스턴스 초기화가 실행된다. <code>this</code>에 바인딩되어 있는 인스턴스에 프로퍼티를 추가한다.</p>
<h4 id="6-인스턴스-반환">6. 인스턴스 반환</h4>
<p>완성된 인스턴스가 바인딩된 <code>this</code>가 반환된다.</p>
<h3 id="2587-표준-빌트인-생성자-함수-확장">25.8.7 표준 빌트인 생성자 함수 확장</h3>
<p><code>extends</code> 키워드 다음에는 <code>[[Construct]]</code> 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식을 사용할 수 있다.</p>
<p><code>String</code> <code>Number</code> <code>Array</code>같은 표준 빌트인 객체도 <code>[[Construct]]</code> 내부 메서드를 갖는 생성자 함수이므로 <code>extends</code> 키워드를 사용하여 확장할 수 있다.</p>
<pre><code class="language-js">class MyArray extends Array {

  // 중복된 배열 요소 제거
  uniq() {
    return this.filter((v, i ,self) =&gt; self.indexOf(v) === i);
  }

  // 배열 요소의 평균
  average() {
    return this.reduce((pre, cur) =&gt; pre + cur, 0) / this.length;
  }
}

const myArray = new MyArray(1, 1, 2, 3);
console.log(myArray); // MyArray(4) [1, 1, 2, 3]

// MyArray.prototype.uniq 호출
console.log(myArray.uniq()); // MyArray(3) [1, 2, 3]

// MyArray.prototype.average 호출
console.log(myArray.average()); // 1.75

// 메서드 체이닝
console.log(myArray.uniq().average()); // 2</code></pre>
<p>주의할 것은 새로운 배열을 반환하는 메서드가 <code>MyArray</code> 클래스의 인스턴스를 반환한다는 것이다.
<code>myArray.filter</code>가 반환한 배열은 <code>MyArray</code> 클래스의 인스턴스다.
따라서 메서드를 연이어 호출할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 24. 클로저]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-24.-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-24.-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Sun, 20 Nov 2022 12:45:43 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="24-클로저">24. 클로저</h1>
<h2 id="241-렉시컬-스코프">24.1 렉시컬 스코프</h2>
<blockquote>
<p>💡 함수를 어디서 호출했는지가 아니라 어디서 정의했는지에 따라 상위 스코프를 결정하는 것을 렉시컬 스코프(정적 스코프)라 한다.</p>
</blockquote>
<pre><code class="language-js">const x = 1;

function foo() {
  const x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // 1
bar(); // 1</code></pre>
<p><code>foo</code>와 <code>bar</code>은 모두 전역 함수다.
함수의 상위 스코프는 함수를 어디서 정의했느냐에 따라 결정되므로 두 함수의 상위 스코프는 전역이다.</p>
<p><strong>스코프의 실체는 실행 컨텍스트의 렉시컬 환경이다.</strong>
이 렉시컬 환경은 자신의 <strong>외부 렉시컬 환경에 대한 참조</strong>를 통해 <strong>상위 렉시컬 환경과 연결</strong>된다. 
이것이 바로 <strong>스코프 체인</strong>이다.</p>
<p>렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값, 즉 상위 스코프에 대한 참조는 <strong>함수 정의가 평가되는 시점</strong>에 <strong>함수가 정의된 환경에 의해 결정</strong>된다. 
이것이 바로 <strong>렉시컬 스코프</strong>다.</p>
<h2 id="242-함수-객체의-내부-슬롯-environment">24.2 함수 객체의 내부 슬롯 [[Environment]]</h2>
<p>렉시컬 스코프가 가능하려면 함수는 자신이 호출되는 환경과 상관없이 정의된 환경을 기억해야 한다.
이를 위해 함수는 자신의 내부 슬롯 <code>[[Environment]]</code>에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.</p>
<pre><code class="language-js">const x = 1;

function foo() {
  const x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // 1
bar(); // 1</code></pre>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/cf4ee28e-8d5c-409d-9555-8a72e418a300/image.jpeg" alt=""></p>
<p><code>foo</code> 와 <code>bar</code> 함수의 <code>[[Environment]]</code> 내부 슬롯에는 함수 정의가 평가된 시점, 즉 전역 코드 평가 시점에 실행 중인 실행 컨텍스트의 렉시컬 환경인 전역 렉시컬 환경의 참조가 저장된다.</p>
<p>이후 함수가 호출되면 함수 코드 평가가 시작된다.</p>
<ul>
<li>함수 실행 컨텍스트 생성</li>
<li>함수 렉시컬 환경 생성<ul>
<li>함수 환경 레코드 생성</li>
<li>this 바인딩</li>
<li>외부 렉시컬 환경에 대한 참조 결정</li>
</ul>
</li>
</ul>
<p>위 과정에서 함수 렉시컬 환경의 구성 요소인 외부 렉시컬 환경에 대한 참조에는 함수 객체의 내부 슬롯 <code>[[Environment]]</code>에 저장된 렉시컬 환경의 참조가 할당된다.
이것이 바로 함수 정의 위치에 따라 상위 스코프를 결정하는 렉시컬 스코프의 실체다.</p>
<h2 id="243-클로저와-렉시컬-환경">24.3 클로저와 렉시컬 환경</h2>
<pre><code class="language-js">const x = 1;

function outer() {
  const x = 10;
  const inner = function() { console.log(x); };
  return inner;
}

const innerFunc = outer();
innerFunc(); // 10</code></pre>
<p>위 예제에서 <code>outer</code> 함수는 중첩 함수 <code>inner</code>를 반환하고 생명 주기를 마감한다.
따라서 outer 함수에서 정의한 x에 접근할 수 있는 방법은 없어보인다. 하지만 innerFunc를 실행한 결과는 10으로, outer 함수의 x를 참조하고 있다.</p>
<p><code>inner</code> 함수는 정의될 때 <code>outer</code>의 렉시컬 환경을 <code>[[Environment]]</code> 내부 슬롯에 저장한다.
이 <code>[[Environment]]</code>에 저장된 상위 스코프는 함수가 존재하는 한 유지된다.
비록 outer 함수 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 사라졌지만, <code>inner</code>함수의 <code>[[Environment]]</code> 내부 슬롯에 <code>outer</code>의 렉시컬 환경이 저장되어 있다.</p>
<p><code>innerFunc</code>를 호출하면 <code>outer</code> 함수가 반환한 <code>inner</code> 함수가 호출된다.
<code>inner</code> 함수의 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 푸시된다.
그리고 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에는 <code>[[Environment]]</code> 내부 슬롯에 저장되어 있는 참조값이 할당된다.</p>
<p>중첩 함수 <code>inner</code>는 외부 함수 <code>outer</code>보다 오래 생존했다.
이때 외부 함수보다 더 오래 생존한 중첩 함수는 외부 함수의 생존 여부와 상관없이 자신이 정의된 위치에 의해 결정된 상위 스코프를 기억한다.
이처럼 상위 스코프의 식별자를 참조할 수 있고 식별자의 값을 변경할 수도 있다.</p>
<p>자바스크립트의 모든 함수는 상위 스코프를 기억하므로 이론적으로 모든 함수는 클로저다.
하지만 모든 함수를 클로저라고 하지는 않는다.</p>
<pre><code class="language-js">const x = 1;

function outer() {
  const inner = function() { 
    const y = 10;
    console.log(y); 
  };
  return inner;
}

const innerFunc = outer();
innerFunc(); // 10</code></pre>
<p>위 예제에서, <code>inner</code>은 외부 함수인 <code>outer</code>보다 오래 살아남았지만, 상위 스코프인 <code>outer</code>의 어떤 식별자도 참조하지 않는다.
이처럼 상위 스코프의 어떤 식별자도 참조하지 않는 경우 대부분의 모던 브라우저는 최적화를 통해 상위 스코프를 기억하지 않는다.
참조하지도 않는 식별자를 기억하는 것은 메모리 낭비이기 때문이다.
따라서 <code>inner</code>는 클로저라고 할 수 없다.</p>
<pre><code class="language-js">const x = 1;

function outer() {
  const x = 10;
  const y = 100;
  const inner = function() { console.log(x); };
  return inner;
}

const innerFunc = outer();
innerFunc(); // 10</code></pre>
<p>위 예제에서 <code>inner</code>은 클로저다.
다만, 상위 스코프의 식별자 <code>x</code> <code>y</code>중 하나만을 참조하고 있다.
이 경우, 대부분의 모던 브라우저는 최적화를 통해 클로저가 참조하고 있는 식별자만을 기억한다.</p>
<p>클로저에 의해 참조되는 상위 스코프의 변수를 자유 변수라고 부른다.</p>
<h2 id="244-클로저의-활용">24.4 클로저의 활용</h2>
<p>클로저는 <strong>상태를 안전하게 변경하고 유지하기 위해 사용</strong>한다.
상태가 의도치 않게 변경되지 않도록 <strong>상태를 안전하게 은닉</strong>하고 <strong>특정 함수에게만 상태 변경을 허용</strong>하기 위해 사용한다.</p>
<p>함수가 호출될 때마다 호출된 횟수를 누적하여 출력하는 카운터를 만들어보자.
호출된 횟수 <code>num</code>가 바로 안전하게 변경하고 유지해야 할 상태다.</p>
<pre><code class="language-js">let num = 0;

const increase = function() {
  return ++num;
}

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3</code></pre>
<p>위 코드는 잘 동작하지만 오류를 발생시킬 가능성을 내포하고 있는 좋지 않은 코드다.
카운트 상태는 전역 변수를 통해 관리되고 있기 때문에 언제든지 누구나 접근할 수 있고 변경할 수 있다.(암묵적 결합)
의도치 않게 상태가 변경될 수 있으며, 이는 곧 오류로 이어진다.</p>
<p>따라서 카운트 상태를 안전하게 변경하고 유지하기 위해서 <code>increase</code> 함수만이 <code>num</code> 변수를 참조하고 변경할 수 있게 하는 것이 바람직하다.
이를 위해 전역 변수 <code>num</code>을 <code>increase</code> 함수의 지역 변수로 바꾸어 의도치 않은 상태 변경을 방지해보자.</p>
<pre><code class="language-js">const increase = function() {
  let num = 0;
  return ++num;
}

console.log(increase()); // 1
console.log(increase()); // 1
console.log(increase()); // 1</code></pre>
<p>의도치 않은 상태 변경은 방지했으나, <code>increase</code> 함수가 호출될 때마다 지역 변수 <code>num</code>이 다시 선언되고, 0으로 초기화 되기 때문에 출력 결과는 언제나 1이다.
즉, 상태가 변경되기 이전 상태를 유지하지 못한다.
클로저를 사용해보자.</p>
<pre><code class="language-js">const increase = (function() {
  let num = 0;
  return function() {
    return ++num;
  }
})();

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3</code></pre>
<p>위 코드가 실행되면 즉시 실행 함수가 호출되고, 즉시 실행 함수가 반환한 함수가 <code>increase</code> 변수에 할당된다.
<code>increase</code> 변수에 할당된 함수는 자신이 정의된 위치에 의해 결정된 상위 스코프인 즉시 실행 함수의 렉시컬 환경을 기억하는 클로저다.
즉시 실행 함수는 한 번만 실행되므로 <code>increase</code>가 호출될 때마다 <code>num</code> 변수가 초기화될 일은 없을 것이다.
또한 <code>num</code> 변수는 외부에서 접근할 수 없는 은닉된 <code>private</code> 변수이므로 안정적인 프로그래밍이 가능하다.</p>
<p>이처럼 <strong>클로저는 상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.</strong></p>
<p>카운트 상태를 감소시킬 수도 있도록 발전시켜 보자.</p>
<pre><code class="language-js">const counter = (function() {
  let num = 0;

  return {
    increase() {
      return ++num;
    }
    decrease() {
      return --num;
    }
  }
})();

console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0</code></pre>
<p>위 예제에서 즉시 실행 함수가 반환하는 객체 리터럴은 즉시 실행 함수의 실행 단계에서 평가되어 객체가 된다.
이때 객체의 메서드도 함수 객체로 생성된다.
객체 리터럴의 중괄호는 코드 블록이 아니므로 별도의 스코프를 생성하지 않는다.</p>
<p>위 예제의 <code>increase</code> <code>decrease</code> 메서드의 상위 스코프는 즉시 실행 함수 실행 컨텍스트의 렉시컬 환경이다.
따라서 두 함수는 즉시 실행 함수의 스코프의 식별자를 참조할 수 있다.</p>
<p>위 예제를 생성자 함수로 표현해보자.</p>
<pre><code class="language-js">const Counter = (function() {
  let num = 0;

  function Counter() {}

  Counter.prototype.increase = function() {
      return ++num;
  }

  Counter.prototype.decrease = function() {
      return --num;
  }

  return Counter;
})();

const counter = new Counter();

console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0</code></pre>
<p>만약 <code>num</code>이 생성자 함수 <code>Counter</code>가 생성할 인스턴스의 프로퍼티라면 인스턴스를 통해 외부에서 접근이 가능한 <code>public</code> 프로퍼티가 된다.
하지만 위 예제의 <code>num</code>은 즉시 실행 함수의 지역 변수이기 때문에 외부에서 접근할 수 없는 은닉된 변수다.</p>
<p>생성자 함수 <code>Counter</code>는 프로토타입을 통해 <code>increase</code> <code>decrease</code> 메서드를 상속받는 인스턴스를 생성한다.
두 함수는 모두 정의가 평가되어 함수 객체가 될 때 실행 중인 실행 컨텍스트인 즉시 실행 함수 실행 컨텍스트의 렉시컬 환경을 기억하는 클로저다.</p>
<p>외부 상태 변경이나 가변 데이터를 피하고 불변성을 지향하는 함수형 프로그래밍에서 부수 효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이기 위해 클로저는 적극적으로 사용된다.</p>
<pre><code class="language-js">
function makeCounter(aux) {
  let counter = 0;
  return function() {
    counter = aux(counter);
    return counter;
  };
}

function increase(n) {
      return ++n;
}

function decrease(n) {
      return --n;
}


const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2

const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2</code></pre>
<p><code>makeCounter</code> 함수는 보조 함수를 인자로 전달받고 함수를 반환하는 고차 함수다.
<code>makeCounter</code> 함수가 반환하는 함수는 자신이 생성됐을 때의 렉시컬 환경인 <code>makeCounter</code> 함수의 스코프에 속한 <code>counter</code> 변수를 기억하는 클로저다.</p>
<p>하지만 <code>makeCounter</code> 함수를 호출해 함수를 반환할 때 반환된 함수는 자신만의 독립된 렉시컬환경을 갖는다는 것이다.
이는 함수를 호출하면 그때마다 새로운 <code>makeCounter</code> 함수 실행 컨텍스트의 렉시컬 환경이 생성되기 때문이다.</p>
<p>즉, <code>increaser</code> <code>decreaser</code>를 정의하는 과정에서 각각 <code>makeCounter</code>가 호출된다. 반환하는 함수는 각각의 <code>makeCounter</code> 함수 실행 컨텍스트의 렉시컬 환경을 <code>[[Environment]]</code> 내부 슬롯에 저장한다.</p>
<p>이제 <code>increaser</code> <code>decreaser</code> 둘은 독립된 렉시컬 환경을 갖기 때문에 자유 변수 <code>counter</code>를 공유하지 않는다.
따라서 카운터의 증감이 연동되지 않는다.
이를 위해서는 <code>makeCounter</code> 함수를 두 번 호출하지 않도록 작성해야 한다.</p>
<pre><code class="language-js">const counter = (function() {
  let counter = 0;

  return function(aux) {
    counter = aux(counter);
    return counter;
  };
})();

function increase(n) {
      return ++n;
}

function decrease(n) {
      return --n;
}

console.log(counter(increase)); // 1
console.log(counter(increase)); // 2
console.log(counter(decrease)); // 1
console.log(counter(decrease)); // 0</code></pre>
<h2 id="245-캡슐화와-정보-은닉">24.5 캡슐화와 정보 은닉</h2>
<p>캡슐화는 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작하는 동작인 메서드를 하나로 묶는 것을 말한다.
캡슐화는 객체의 특정 프로퍼티나 메서드를 감추는 정보 은닉을 목적으로 사용한다.</p>
<p>정보 은닉은 외부에 공개할 필요가 없는 구현의 일부를 외부에 공개되지 않도록 감추어 객체의 상태가 변경되는 것을 방지해 정보를 보호하고, 객체 간의 상호 의존성, 즉 결합도를 낮추는 효과가 있다.</p>
<p>자바스크립트는 <code>public</code> <code>private</code> <code>protected</code>같은 접근 제한자를 제공하지 않는다.
따라서 자바스크립트 객체의 모든 프로퍼티와 메서드는 기본적으로 <code>public</code>하다.</p>
<pre><code class="language-js">function Person(name, age) {
  this.name = name; // public
  let _age = age; // private

  this.sayHi = function() {
    console.log(this.name, _age)
  };
}

const me = new Person(&quot;sangjo&quot;, 29);
me.sayHi(); // sangjo 29
console.log(me.name); // sangjo
console.log(me._age); // undefined

const you = new Person(&quot;lee&quot;, 100);
you.sayHi(); // lee 100
console.log(you.name); // lee
console.log(you._age); // undefined</code></pre>
<p>위 예제의 <code>name</code> 프로퍼티는 외부로 공개되어 있어서 자유롭게 참조하거나 변경할 수 있다.
하지만 <code>_age</code> 변수는 Person 생성자 함수의 지역 변수이므로 외부에서 참조하거나 변경할 수 없다.</p>
<p>하지만 위 예제의 <code>sayHi</code>메서드는 인스턴스 메서드이므로 <code>Person</code> 객체가 생성될 때마다 중복 생성된다.
<code>sayHi</code>메서드를 프로토타입 메서드로 변경하여 <code>sayHi</code>메서드의 중복 생성을 방지해 보자.</p>
<pre><code class="language-js">function Person(name, age) {
  this.name = name; // public
  let _age = age; // private
}


Person.prototype.sayHi = function() {
  console.log(this.name, _age) // _age를 참조할 수 없다.
};</code></pre>
<p>이때, <code>_age</code>를 참조할 수 없는 문제가 있다.
따라서 즉시 실행 함수를 사용하여 <code>Person</code> 생성자 함수와 <code>Person.prototype.sayHi</code> 메서드를 하나의 함수 내에 모아보자.</p>
<pre><code class="language-js">const Person = (function() {
  let _age = 0;

  function Person(name, age) {
    this.name = name; // public
    _age = age;
  }

  Person.prototype.sayHi = function() {
    console.log(this.name, _age) // _age를 참조할 수 있다.
  };

  return Person;
})();


const me = new Person(&quot;sangjo&quot;, 29);
me.sayHi(); // sangjo 29
console.log(me.name); // sangjo
console.log(me._age); // undefined

const you = new Person(&quot;lee&quot;, 100);
you.sayHi(); // lee 100
console.log(you.name); // lee
console.log(you._age); // undefined</code></pre>
<p>위 패턴을 사용하면 <code>Person</code> 생성자 함수와 <code>sayHi</code> 메서드는 즉시 실행 함수의 지역 변수 <code>_age</code>를 참조할 수 있는 클로저다.</p>
<p>그러나, <code>Person</code> 생성자 함수가 여러 개의 인스턴스를 생성할 경우 <code>_age</code>를 공유하게 되면서 상태가 유지되지 않는다는 문제가 있다.</p>
<pre><code class="language-js">...

const me = new Person(&quot;sangjo&quot;, 29);
me.sayHi(); // sangjo 29

const you = new Person(&quot;lee&quot;, 100);
you.sayHi(); // lee 100


me.sayHi(); // lee 100</code></pre>
<p>이는 <code>sayHi</code> 메서드가 단 한 번 생성되는 클로저이기 때문에 발생하는 현상이다.
즉시 실행 함수가 호출될 때 생성되는데, 이때 <code>sayHi</code> 메서드는 즉시 실행 함수의 실행 컨텍스트의 렉시컬 환경을 <code>[[Environment]]</code> 내부 슬롯에 저장한다.
따라서 <code>Person</code> 생성자 함수의 모든 인스턴스가 상속을 통해 호출할 수 있는 <code>sayHi</code> 메서드는 어떤 인스턴스로 호출하더라도 동일한 상위 스코프를 사용하게 된다.</p>
<p>이처럼 자바스크립트는 정보 은닉을 완전하게 지원하지 않는다...</p>
<h2 id="246-자주-발생하는-실수">24.6 자주 발생하는 실수</h2>
<pre><code class="language-js">var funcs = [];

for (var i = 0; i &lt; 3; i++) {
  funcs[i] = function() { return i; };
}

for (var j = 0; j &lt; 3; j++) {
  console.log(funcs[j]()); // 3 3 3
}</code></pre>
<p>반복문의 변수 선언문에서 <code>var</code> 키워드로 선언한 <code>i</code> 변수는 블록 레벨 스코프가 아닌 함수 레벨 스코프를 갖기 때문에 전역 변수다.
따라서 반복문을 돌고 난 후 <code>i</code> 변수에는 <code>3</code>이 할당되어 있다.
따라서 <code>funcs[j]</code>를 호출한 결과는 <code>3</code>이 된다.</p>
<p>클로저를 사용해 수정해보자.</p>
<pre><code class="language-js">var funcs = [];

for (var i = 0; i &lt; 3; i++) {
  funcs[i] = (function(id) {
    return function() {
      return id;
    };
  })(i);
}

for (var j = 0; j &lt; 3; j++) {
  console.log(funcs[j]()); // 0 1 2
}</code></pre>
<p>즉시 실행 함수는 전역 변수 <code>i</code>에 현재 할당되어 있는 값을 인수로 전달받아 매개변수 <code>id</code>에 할당한 후 중첩 함수를 반환하고 종료된다.
중첩 함수는 즉시 실행 함수의 렉시컬 환경을 기억하는, 자유 변수 <code>id</code>를 참조할 수 있는 클로저다.</p>
<p>사실 이는 <code>let</code>을 사용하면 해결된다.
<code>let</code>은 블록 레벨 스코프를 지원하기 때문이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 23. 실행 컨텍스트]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-23.-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-23.-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Sun, 20 Nov 2022 10:25:23 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="23-실행-컨텍스트">23. 실행 컨텍스트</h1>
<blockquote>
<p>💡 <strong>실행 컨텍스트는 소스코드를 실행하는 데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역이다.</strong>
<strong>실행 컨텍스트는 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.</strong></p>
</blockquote>
<h2 id="231-소스코드의-타입">23.1 소스코드의 타입</h2>
<p>ECMAScript 사양은 소스코드를 4가지 타입으로 구분한다.</p>
<ol>
<li><p>전역 코드
전역 코드는 전역 변수를 관리하기 위해 최상위 스코프인 전역 스코프를 생성해야 한다.
var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역 객체와 연결되어야 한다. 이를 위해 <strong>전역 코드가 평가되면 전역 실행 컨텍스트가 생성</strong>된다.</p>
</li>
<li><p>함수 코드
함수 코드는 지역 스코프를 생성하고 지역 변수, 매개변수, arguments 객체를 관리해야 한다.
그리고 생성한 지역 스코프를 전역 스코프에서 시작하는 스코프 체인의 일원으로 연결해야 한다. 이를 위해 <strong>함수 코드가 평가되면 함수 실행 컨텍스트가 생성</strong>된다.</p>
</li>
<li><p>eval 코드
eval 코드는 strict mode에서 자신만의 독자적인 스코프를 생성한다. 이를 위해 eval 코드가 평가되면 eval 실행 컨텍스트가 생성된다.</p>
</li>
<li><p>모듈 코드
모듈 코드는 모듈별로 독립적인 모듈 스코프를 생성한다. 이를 위해 모듈 코드가 평가되면 모듈 실행 컨텍스트가 생성된다.</p>
</li>
</ol>
<h2 id="232-소스코드의-평가와-실행">23.2 소스코드의 평가와 실행</h2>
<p>자바스크립트 엔진은 소스코드를 2개의 &#39;소스코드의 평가&#39;와 &#39;소스코드의 실행&#39; 과정으로 나누어 처리한다.</p>
<p><strong>소스코드 평가 과정</strong>에서는 <strong>실행 컨텍스트를 생성</strong>하고 변수, 함수 등의 <strong>선언문만 먼저 실행</strong>하여 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 <strong>스코프(렉시컬 환경의 환경 레코드)에 등록</strong>한다.</p>
<p>소스코드 평가 과정이 끝나면 비로소 선언문을 제외한 소스코드가 순차적으로 실행되기 시작한다.(<strong>런타임</strong> 시작) 이때 소스코드 실행에 필요한 정보, 즉 <strong>변수나 함수의 참조를 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득</strong>한다. 그리고 변수 값의 변경 등 소스코드의 <strong>실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록</strong>된다.</p>
<pre><code class="language-js">var x;
x = 1;</code></pre>
<ul>
<li>소스코드 평가 과정<ul>
<li>변수 선언문 <code>var x;</code>를 먼저 실행.</li>
<li>생성된 변수 식별자 x는 실행 컨텍스트가 관리하는 스코프에 등록되고 <code>undefined</code>로 초기화</li>
</ul>
</li>
<li>소스코드 실행 과정<ul>
<li>변수 할당문 <code>x = 1;</code>만 실행</li>
<li>할당하려면 <code>x</code>가 선언된 변수인지 확인부터 해야함.</li>
<li>실행 컨텍스트가 관리하는 스코프에 <code>x</code>변수가 등록되어 있는지 확인.</li>
<li>선언된 변수라면 값을 할당하고 할당 결과를 실행 컨텍스트에 등록하여 관리.</li>
</ul>
</li>
</ul>
<h2 id="233-실행-컨텍스트의-역할">23.3 실행 컨텍스트의 역할</h2>
<pre><code class="language-js">const x = 1;
const y = 2;

function foo(a) {
  const x = 10;
  const y = 20;

  console.log(a + x + y);
}

foo(100); // 130

console.log(x + y); // 3</code></pre>
<ol>
<li><p>전역 코드 평가
전역 코드를 실행하기에 앞서 전역 코드 평가 과정을 거친다.
전역 코드의 변수 선언문과 함수 선언문이 먼저 실행되고, 생성된 전역 변수와 전역 함수가 실행 컨텍스트가 관리하는 전역 스코프에 등록된다.
이때 <code>var</code> 키워드로 선언된 전역 변수와 전역 함수는 전역 객체의 프로퍼티와 메서드가 된다.</p>
</li>
<li><p>전역 코드 실행
런타임이 시작된다. 전역 변수에 값이 할당되고 함수가 호출된다.
함수가 호출되면 전역 코드의 실행을 일시 중단하고 코드 실행 순서를 변경하여 함수 내부로 진입한다.</p>
</li>
<li><p>함수 코드 평가
함수 내부의 문들을 실행하기에 앞서 함수 코드 평가 과정을 거친다.
매개변수와 지역 변수 선언문이 먼저 실행되고, 생성된 매개변수와 지역 변수가 실행 컨텍스트가 관리하는 지역 스코프에 등록된다.
또한 함수 내부에서 지역 변수처럼 사용할 수 있는 <code>arguments</code> 객체가 생성되어 지역 스코프에 등록되고 <code>this</code> 바인딩도 결정된다.</p>
</li>
<li><p>함수 코드 실행
런타임이 시작된다.
매개변수와 지역 변수에 값이 할당되고 <code>console.log</code> 메서드가 호출된다.
먼저 식별자인 <code>console</code>을 스코프 체인을 통해 검색한다. <code>console</code> 식별자는 스코프 체인에 등록되어 있지 않고 전역 객체에 프로퍼티로 존재한다. 즉, 전역 객체의 프로퍼티는 전역 스코프를 통해 검색 가능하다.
<code>log</code> 프로퍼티를 <code>console</code> 객체의 프로토타입 체인을 통해 검색한다. 그후 <code>console.log</code> 메서드에 인수로 전달된 표현식 <code>a + x + y</code>가 평가된다. <code>a</code> <code>x</code> <code>y</code> 식별자는 스코프 체인을 통해 검색한다.
<code>console.log</code> 메서드의 실행이 종료되면 함수 코드 실행 과정이 종료되고 함수 호출 이전으로 되돌아가 전역 코드 실행을 계속한다.</p>
</li>
</ol>
<p>이처럼, 코드가 실행되려면 스코프, 식별자, 코드 실행 순서 등의 관리가 필요하다.
<strong>실행 컨텍스트는 소스코드를 실행하는 데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역이다.</strong>
<strong>실행 컨텍스트는 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.</strong></p>
<p>식별자와 스코프는 실행 컨텍스트의 렉시컬 환경으로 관리하고, 코드 실행 순서는 실행 컨텍스트 스택으로 관리한다.</p>
<h2 id="234-실행-컨텍스트의-역할">23.4 실행 컨텍스트의 역할</h2>
<pre><code class="language-js">const x = 1;

function foo() {
  const y = 2;

  function bar() {
    const z = 3;
    console.log(x + y + z);
  }
  bar();
}

foo(); // 6</code></pre>
<p><img src="https://velog.velcdn.com/images/sjoleee_/post/a4c1afea-a85f-4374-9464-463c3da41481/image.jpeg" alt=""></p>
<p><strong>실행 컨텍스트 스택은 코드의 실행 순서를 관리한다.</strong>
실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행 중인 코드의 실행 컨텍스트다. 이를 <strong>실행 중인 실행 컨텍스트</strong>라 부른다.</p>
<h2 id="235-렉시컬-환경">23.5 렉시컬 환경</h2>
<p><strong>렉시컬 환경은 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트다.</strong>
즉, 렉시컬 환경은 스코프를 구분하여 식별자를 등록하고 관리하는 저장소 역할을 하는 <strong>렉시컬 스코프의 실체</strong>다.</p>
<p>렉시컬 환경은 두 개의 컴포넌트로 구성된다.</p>
<ul>
<li>환경 레코드<ul>
<li>스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소다.</li>
</ul>
</li>
<li>외부 렉시컬 환경에 대한 참조<ul>
<li>외부 렉시컬 환경에 대한 참조는 상위 스코프를 가리킨다.</li>
<li>상위 스코프란 외부 렉시컬 환경, 즉 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 말한다.</li>
<li>이를 통해 단방향 링크드 리스트인 스코프 체인을 구현한다.</li>
</ul>
</li>
</ul>
<h2 id="236-실행-컨텍스트의-생성과-식별자-검색-과정">23.6 실행 컨텍스트의 생성과 식별자 검색 과정</h2>
<pre><code class="language-js">var x = 1;
const y = 2;

function foo(a) {
  var x = 3;
  const y = 4;

  function bar(b) {
    const z = 5;
    console.log(a + b + x + y + z);
  }
  bar(10);
}

foo(20); // 42</code></pre>
<h3 id="2361-전역-객체-생성">23.6.1 전역 객체 생성</h3>
<p>전역 객체는 전역 코드가 평가되기 이전에 생성된다.
전역 객체에 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체가 추가되며 각종 <code>Web API</code>또는 호스트 객체를 포함한다.
전역 객체도 <code>Object.prototype</code>을 상속받는다. 즉, 전역 객체도 프로토타입 체인의 일원이다.</p>
<h3 id="2362-전역-코드-평가">23.6.2 전역 코드 평가</h3>
<p>전역 코드 평가 순서는 다음과 같다.</p>
<ul>
<li>전역 실행 컨텍스트 생성</li>
<li>전역 렉시컬 환경 생성<ul>
<li>전역 환경 레코드 생성<ul>
<li>객체 환경 레코드 생성</li>
<li>선언적 환경 레코드 생성</li>
</ul>
</li>
<li>this 바인딩</li>
<li>외부 렉시컬 환경에 대한 참조 결정</li>
</ul>
</li>
</ul>
<p><strong>1. 전역 실행 컨텍스트 생성</strong></p>
<p>비어있는 전역 실행 컨텍스트를 생성하여 실행 컨텍스트 스택에 푸시한다.
전역 실행 컨텍스트가 실행 중인 실행 컨텍스트가 된다.</p>
<p><strong>2. 전역 렉시컬 환경 생성</strong></p>
<p>전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩한다.
렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성된다.</p>
<p><strong>2.1. 전역 환경 레코드 생성</strong></p>
<p>전역 환경 레코드는 <code>var</code> 키워드와 <code>let</code> <code>const</code> 키워드로 선언된 전역 변수를 구분하기 위해 <strong>객체 환경 레코드</strong>와 <strong>선언적 환경 레코드</strong>로 구성되어 있다.</p>
<p><strong>2.1.1. 객체 환경 레코드 생성</strong></p>
<p>객체 환경 레코드는 <code>BindingObject</code>라고 부르는 객체와 연결된다.
<code>BindingObject</code>는 전역 객체다.</p>
<p>전역 코드 평가 과정에서 <code>var</code> 키워드로 선언한 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 환경 레코드의 객체 환경 레코드에 연결된 <code>BindingObject</code>를 통해 전역 객체의 프로퍼티와 메서드가 된다.</p>
<p><strong>2.1.2. 선언적 환경 레코드 생성</strong></p>
<p><code>let</code> <code>const</code> 키워드로 선언된 전역 변수는 선언적 환경 레코드에 등록되고 관리된다.
해당 변수들은 선언 단계와 초기화 단계가 분리되어 진행된다.
따라서 런타임에 실행 흐름이 변수 선언문에 도달하기 전까지 일시적 사각지대에 빠진다.</p>
<p><strong>2.2. this 바인딩</strong></p>
<p>전역 환경 레코드의 <code>[[GlobalThisValue]]</code> 내부 슬롯에 <code>this</code>가 바인딩된다.
일반적으로 전역 코드에서 <code>this</code>는 전역 객체를 가리킨다.
전역 코드에서 <code>this</code>를 참조하면 전역 환경 레코드의 <code>[[GlobalThisValue]]</code> 내부 슬롯에 바인딩되어 있는 객체가 반환된다.</p>
<p><strong>2.3. 외부 렉시컬 환경에 대한 참조 결정</strong></p>
<p>외부 렉시컬 환경에 대한 참조는 상위 스코프를 가리킨다.
전역 코드를 포함하는 소스코드는 없으므로 <code>null</code>이 할당된다.
이는 전역 렉시컬 환경이 스코프 체인의 종점에 존재함을 의미한다.</p>
<h3 id="2363-전역-코드-실행">23.6.3 전역 코드 실행</h3>
<p>런타임이 시작되고, 변수 할당문이 실행되어 전역 변수 <code>x</code> <code>y</code>에 값이 할당된다.
그리고 <code>foo</code>함수가 호출된다.</p>
<p>변수 할당문 또는 함수 호출문을 실행하려면 변수 또는 함수 이름이 선언된 식별자인지 확인해야한다.
선언되지 않은 식별자는 참조할 수 없다.
또한 식별자는 스코프가 다르면 같은 이름을 가질 수 있다. 즉, 동일한 이름의 식별자가 다른 스코프에 여러 개 존재할 수도 있다.
따라서 어느 스코프의 식별자를 참조하면 되는지 결정해야 하는데, 이를 <strong>식별자 결정</strong>이라 한다.</p>
<h3 id="2364-foo-함수-코드-평가">23.6.4 foo 함수 코드 평가</h3>
<p><code>foo</code> 함수 코드 평가 순서는 다음과 같다.</p>
<ul>
<li>함수 실행 컨텍스트 생성</li>
<li>함수 렉시컬 환경 생성<ul>
<li>함수 환경 레코드 생성</li>
<li><code>this</code> 바인딩</li>
<li>외부 렉시컬 환경에 대한 참조 결정</li>
</ul>
</li>
</ul>
<p><strong>1. 함수 실행 컨텍스트 생성</strong></p>
<p><code>foo</code> 함수 실행 컨텍스트를 생성한다.
생성된 함수 실행 컨텍스트는 함수 렉시컬 환경이 완성된 다음 실행 컨텍스트 스택에 푸시된다.
이때 <code>foo</code> 함수 실행 컨텍스트는 실행 중인 실행 컨텍스트가 된다.</p>
<p><strong>2. 함수 렉시컬 환경 생성</strong></p>
<p><code>foo</code> 함수 렉시컬 환경을 생성하고 <code>foo</code> 함수 실행 컨텍스트에 바인딩한다.
렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성된다.</p>
<p><strong>2.1. 함수 환경 레코드 생성</strong></p>
<p>함수 환경 레코드는 매개변수, <code>arguments</code> 객체, 지역 변수와 중첩 함수를 등록하고 관리한다.</p>
<p><strong>2.2. this 바인딩</strong></p>
<p>함수 환경 레코드의 <code>[[ThisValue]]</code> 내부 슬롯에 this가 바인딩된다.
<code>[[ThisValue]]</code> 내부 슬롯에 바인딩될 객체는 함수 호출 방식에 따라 결정된다.</p>
<p>foo 함수는 일반 함수로 호출되었으므로 this는 전역 객체를 가리킨다.
따라서 <code>[[ThisValue]]</code> 내부 슬롯에는 전역 객체가 바인딩된다.
foo 함수 내부에서 this를 참조하면 <code>[[ThisValue]]</code> 내부 슬롯에 바인딩되어 있는 객체가 반환된다.</p>
<p><strong>2.3. 외부 렉시컬 환경에 대한 참조 결정</strong></p>
<p><code>foo</code> 함수 정의가 평가된 시점에 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 할당된다.
<code>foo</code> 함수는 전역 코드에 정의된 전역 함수다. 따라서 외부 렉시컬 환경에 대한 참조에는 전역 렉시컬 환경의 참조가 할당된다.</p>
<p>자바스크립트는 함수를 어디서 호출했는지가 아니라 어디서 정의했는지에 따라 상위 스코프를 결정한다.
함수 객체는 자신이 정의된 스코프, 즉 상위 스코프를 기억한다.
자바스크립트 엔진은 함수 정의를 평가하여 함수 객체를 생성할 대 현재 실행 중인 실행 컨텍스트의 렉시컬 환경, 즉 함수의 상위 스코프를 함수 객체의 내부 슬롯 <code>[[Environment]]</code>에 저장한다.
함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 할당되는 것은 바로 함수의 상위 스코프를 가리키는 함수 객체의 내부 슬롯 <code>[[Environment]]</code>에 저장된 렉시컬 환경의 참조다.</p>
<h3 id="2365-foo-함수-코드-실행">23.6.5 foo 함수 코드 실행</h3>
<p>런타임이 시작되어 <code>foo</code> 함수의 소스코드가 순차적으로 실행되기 시작한다.
매개변수에 인수가 할당되고, 변수 할당문이 실행되어 지역 변수 <code>x</code> <code>y</code>에 값이 할당된다.
이때 식별자 결정을 위해 실행 중인 실행 컨텍스트의 렉시컬 환경에서 식별자를 검색하기 시작한다.
그리고 함수 <code>bar</code>가 호출된다.</p>
<h3 id="2366-bar-함수-코드-평가">23.6.6 bar 함수 코드 평가</h3>
<p>foo 함수 코드 평가와 동일한 과정을 거친다.</p>
<h3 id="2367-bar-함수-코드-실행">23.6.7 bar 함수 코드 실행</h3>
<p>런타임이 시작되어 <code>bar</code> 함수의 소스코드가 순차적으로 실행되기 시작한다.
매개변수에 인수가 할당되고, 변수 할당문이 실행되어 지역 변수 <code>z</code>에 값이 할당된다.</p>
<p>그리고 <code>console.log(a + b + x + y + z)</code>가 실행된다.</p>
<p><strong>console 식별자 검색</strong>
<code>console</code> 식별자를 스코프 체인에서 검색한다. 스코프 체인은 현재 실행 중인 실행 컨텍스트의 렉시컬 환경에서 시작하여 외부 렉시컬 환경에 대한 참조로 이어지는 렉시컬 환경의 연속이다.</p>
<ol>
<li>먼저 <code>bar</code> 함수 렉시컬 환경에서 <code>console</code> 식별자를 검색한다.</li>
<li>없으므로 스코프 체인 상의 상위 스코프, 즉 외부 렉시컬 환경에 대한 참조가 가리키는 <code>foo</code> 함수 렉시컬 환경으로 이동하여 <code>console</code> 식별자를 검색한다.</li>
<li>마찬가지로 없다. 그렇다면 다시 한 번 상위 스코프로 이동한다. <code>foo</code> 함수 렉시컬 환경에서 외부 렉시컬 환경에 대한 참조가 가리키는 것은 전역 렉시컬 환경이다.</li>
<li>전역 렉시컬 환경의 객체 환경 레코드의 <code>BindingObject</code>를 통해 전역 객체에서 <code>console</code>을 찾을 수 있다.</li>
</ol>
<p><strong>log 메서드 검색</strong></p>
<p>이제 <code>console</code> 식별자에 바인딩된 객체, 즉 <code>console</code> 객체에서 <code>log</code> 메서드를 검색한다.
이때 <code>console</code> 객체의 프로토타입 체인을 통해 메서드를 검색한다. <code>log</code>는 상속된 프로퍼티가 아니라 <code>console</code> 객체가 직접 소유하는 프로퍼티다.</p>
<p><strong>표현식 a + b + x + y + z의 평가</strong></p>
<p>이제 <code>console.log</code> 메서드에 전달할 인수인 표현식 <code>a + b + x + y + z</code>를 평가하기 위해 <code>a</code> <code>b</code> <code>x</code> <code>y</code> <code>z</code> 식별자를 검색한다.</p>
<p><strong>console.log 메서드 호출</strong></p>
<p>표현식 <code>a + b + x + y + z</code>가 평가되어 생성한 값 <code>42</code>를 <code>console.log</code> 메서드에 전달하여 호출한다.</p>
<h3 id="2368-bar-함수-코드-실행-종료">23.6.8 bar 함수 코드 실행 종료</h3>
<p>더 이상 실행할 코드가 없으므로 <code>bar</code> 함수 코드의 실행이 종료된다.
실행 컨텍스트 스택에서 <code>bar</code> 함수 실행 컨텍스트가 팝되어 제거되고 foo 실행 컨텍스트가 실행 중인 실행 컨텍스트가 된다.</p>
<h3 id="2369-foo-함수-코드-실행-종료">23.6.9 foo 함수 코드 실행 종료</h3>
<p>더 이상 실행할 코드가 없으므로 foo 함수 코드의 실행이 종료된다.
실행 컨텍스트 스택에서 foo 함수 실행 컨텍스트가 팝되어 제거되고 전역 실행 컨텍스트가 실행 중인 실행 컨텍스트가 된다.</p>
<h3 id="23610-전역-코드-실행-종료">23.6.10 전역 코드 실행 종료</h3>
<p>foo 함수가 종료되면 더는 실행할 전역 코드가 없으므로 전역 코드의 실행이 종료되고 전역 실행 컨텍스트도 실행 컨텍스트 스택에서 팝되어 실행 컨텍스트 스택에는 아무것도 남아있지 않게 된다.</p>
<h2 id="237-실행-컨텍스트와-블록-레벨-스코프">23.7 실행 컨텍스트와 블록 레벨 스코프</h2>
<p><code>let</code> <code>const</code>로 선언한 변수는 모든 코드 블록을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.</p>
<pre><code class="language-js">let x = 1;

if(true) {
  let x = 10;
  console.log(x); // 10
}

console.log(x); // 1</code></pre>
<p>if문의 코드 블록 이 실행되면 코드 블록을 위한 블록 레벨 스코프를 생성해야 한다.
렉시컬 환경을 새롭게 생성하여 기존의 전역 렉시컬 환경을 교체한다.
실행 컨텍스트를 새롭게 생성하는 것이 아니라 렉시컬 환경을 생성하여 기존의 렉시컬 환경을 교체한다는 점을 주의하자.
이때 생성된 코드 블록을 위한 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 if 문이 실행되기 이전의 전역 렉시컬 환경을 가리킨다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 22. this]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-22.-this</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-22.-this</guid>
            <pubDate>Sat, 19 Nov 2022 14:12:37 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="22-this">22. this</h1>
<h2 id="221-this-키워드">22.1 this 키워드</h2>
<blockquote>
<p>💡  <code>this</code>는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다.</p>
</blockquote>
<p>객체에서 메서드는 자신이 속한 객체의 상태, 즉 프로퍼티를 참조하고 변경하는 역할을 한다.
이때, 메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 먼저 <strong>자신이 속한 객체를 가리키는 식별자</strong>가 필요할 것이다.</p>
<pre><code class="language-js">const circle = {
  radius: 5,
  getDiameter() {
    return 2 * circle.radius;
  }
};

console.log(circle.getDiameter()); // 10 </code></pre>
<p>객체 리터럴 방식으로 생성한 객체의 경우, 메서드 내부에서 메서드 사진이 속한 객체를 가리키는 식별자를 재귀적으로 참조할 수 있다.</p>
<p><code>getDiameter</code> 메서드에서 <code>circle</code>를 참조하는 표현식이 평가되는 것은 <code>getDiameter</code> 메서드가 호출되고 함수 몸체가 실행되는 시점이다.
위 객체 리터럴은 <code>circle</code> 변수에 할당되기 직전에 평가된다. 따라서 <code>getDiameter</code> 메서드가 호출되는 시점에는 이미 객체 리터럴의 평가가 완료되어 <code>circle</code>에 할당되어 있다. 그러므로 <code>getDiameter</code> 메서드는 <code>circle</code> 식별자를 참조할 수 있다.</p>
<p>하지만, 이렇게 재귀적 참조 방식은 일반적이지 않으며 바람직하지 않다.
또한, 객체 리터럴이 아닌 생성자 함수 방식이라면 어떨까?</p>
<pre><code class="language-js">function Circle(radius) {
  ???.radius = radius;
};

Circle.prototype.getDiameter = function() {
  return 2 * ???.radius;
}

const circle = new Circle(5);</code></pre>
<p>생성자 함수 내부에서 프로퍼티 또는 메서드를 추가하기 위해서 자신이 생성할 인스턴스를 참조할 수 있어야 한다.
하지만 인스턴스를 만들기 위해서는 생성자 함수가 먼저 존재할 필요가 있다.
따라서, 생성자 함수가 프로퍼티, 메서드를 만들기 위해서는 <strong>존재하기 이전인 인스턴스를 미리 참조할 수 있는 특수한 식별자</strong>가 필요하다.</p>
<p><strong><code>this</code>는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다.
단, <code>this</code>가 가리키는 값, 즉 <code>this</code> 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.</strong></p>
<pre><code class="language-js">// 전역에서 this는 전역 객체 window를 가리킨다.
console.log(this); // window

// 일반 함수 내부에서 this는 전역 객체 window를 가리킨다.
function square(number) {
  console.log(this); // window
  return number*number;
}

//메서드 내부에서 this는 메서드를 호출한 객체를 가리킨다.
const person = {
  name: &quot;sangjo&quot;,
  getName() {
    console.log(this); // {name: &quot;sangjo&quot;, getName: ƒ}
    return this.name;
  }
};
console.log(person.getName()); // sangjo

// 생성자 함수 내부에서 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
function Person(name) {
  this.name = name;
  console.log(this); // Person {name: &quot;sangjo&quot;}
};
const me = new Person(&quot;sangjo&quot;)</code></pre>
<h2 id="222-함수-호출-방식과-this-바인딩">22.2 함수 호출 방식과 this 바인딩</h2>
<blockquote>
<p>💡 <code>this</code> 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.</p>
</blockquote>
<p>함수를 호출하는 방식은 다양하다.</p>
<ul>
<li>일반 함수 호출</li>
<li>메서드 호출</li>
<li>생성자 함수 호출</li>
<li><code>Function.prototype.apply/call/bind</code> 메서드에 의한 간접 호출</li>
</ul>
<pre><code class="language-js">const foo = function() {
  console.dir(this);
};

// 1. 일반 함수 호출
// 일반 함수 내부의 this는 전역 객체를 가리킨다.
foo(); // window

// 2. 메서드 호출
// 메서드를 호출한 객체를 가리킨다.
const obj = { foo };
obj.foo(); // obj

// 3. 생성자 함수 호출
// 생성자 함수가 생성한 인스턴스를 가리킨다.
new foo(); // foo {}

// 4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출
// 메서드의 인수에 따라 결정된다.
const bar = { name: &quot;bar&quot; };

foo.apply(bar); // bar
foo.call(bar); // bar
foo.bind(bar)(); // bar</code></pre>
<h3 id="2221-일반-함수-호출">22.2.1 일반 함수 호출</h3>
<blockquote>
<p>💡 일반 함수로 호출된 모든 함수(중첩 함수, 콜백 함수 포함) 내부의 <code>this</code>에는 전역 객체가 바인딩된다.</p>
</blockquote>
<p>전역 함수는 물론이고 중첩 함수를 일반 함수로 호출하면 함수 내부의 <code>this</code>에는 전역 객체가 바인딩된다.
메서드 내에서 정의한 중첩 함수 역시 일반 함수라면 <code>this</code>에는 전역 객체가 바인딩된다.</p>
<p>하지만 <code>this</code>는 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 객체를 생성하지 않는 일반 함수에서 <code>this</code>는 의미가 없다.
그리고 <code>strict mode</code>가 적용된 일반 함수 내부의 <code>this</code>에는 <code>undefined</code>가 바인딩된다.</p>
<p>콜백 함수가 일반 함수로 호출된다면 콜백 함수 내부의 this에도 전역 객체가 바인딩 된다.
이 경우 곤란한 상황이 발생할 수 있다.</p>
<pre><code class="language-js">var value = 1;

const obj = {
  value: 100,
  foo() {
    console.log(&quot;foo&#39;s this&quot;, this); // {value: 100, foo: ƒ}
    setTimeout(function() {
      console.log(&quot;callback&#39;s this&quot;, this); // window
      console.log(&quot;callback&#39;s this.value&quot;, this.value); // 1
    }, 1000)
  }
}</code></pre>
<p>중첩 함수 또는 콜백 함수는 외부 함수의 일부 로직을 대신하는 경우가 대부분이다.
하지만 외부 함수인 메서드와 중첩 함수 또는 콜백 함수의 this가 일치하지 않는다는 것은 이들을 헬퍼 함수로 동작하기 어렵게 만든다.</p>
<p>이 경우 사용할 수 있는 방법은 다음과 같다.</p>
<pre><code class="language-js">var value = 1;

const obj = {
  value: 100,
  foo() {
    // 메서드의 this를 that에 할당한다.
    const that = this;

    setTimeout(function() {
      // this대신 that을 참조한다.
      console.log(that.value); // 100
    }, 1000)
  }
}</code></pre>
<pre><code class="language-js">var value = 1;

const obj = {
  value: 100,
  foo() {
    // 콜백 함수에 명시적으로 this를 바인딩한다.
    setTimeout(function() {
      console.log(this.value); // 100
    }.bind(this), 1000)
  }
}</code></pre>
<pre><code class="language-js">var value = 1;

const obj = {
  value: 100,
  foo() {
    // 화살표 함수 내부의 this는 언제나 상위 스코프의 this를 가리킨다.
    setTimeout(() =&gt; console.log(this.value), 1000) // 100
  }
}</code></pre>
<h3 id="2222-메서드-호출">22.2.2 메서드 호출</h3>
<blockquote>
<p>💡 메서드 내부의 <code>this</code>는 메서드를 소유한 객체가 아닌 <strong>메서드를 호출한 객체에 바인딩된다.</strong></p>
</blockquote>
<pre><code class="language-js">function Person(name) {
  this.name = name;
}

Person.prototype.getName = function() {
  return this.name;
}

const me = new Person(&quot;sangjo&quot;);
console.log(me.getName()); // 1. sangjo

Person.prototype.name = &quot;LEE&quot;
console.log(Person.prototype.getName()); // 2. LEE</code></pre>
<p>1의 경우, <code>getName</code> 메서드를 호출한 객체는 <code>me</code>이므로 <code>this</code>는 <code>me</code>를 가리킨다.
2의 경우, <code>getName</code> 메서드를 호출한 객체는 <code>Person.prototype</code>이므로 <code>this</code>는 <code>Person.prototype</code>을 가리킨다.</p>
<h3 id="2223-생성자-함수-호출">22.2.3 생성자 함수 호출</h3>
<blockquote>
<p>💡 생성자 함수 내부의 this에는 미래에 생성할 인스턴스가 바인딩된다.</p>
</blockquote>
<h3 id="2224-functionprototypeapplycallbind-메서드에-의한-간접-호출">22.2.4 Function.prototype.apply/call/bind 메서드에 의한 간접 호출</h3>
<blockquote>
<p>💡 Function.prototype.apply/call/bind 메서드는 this로 사용할 객체를 전달받는다.</p>
</blockquote>
<pre><code class="language-js">const foo = function() {
  console.dir(this);
};

const bar = { name: &quot;bar&quot; };

foo(); // window
foo.apply(bar); // bar
foo.call(bar); // bar
foo.bind(bar)(); // bar</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 21. 빌트인 객체]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-21.-%EB%B9%8C%ED%8A%B8%EC%9D%B8-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-21.-%EB%B9%8C%ED%8A%B8%EC%9D%B8-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Sun, 06 Nov 2022 11:54:15 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="21-빌트인-객체">21. 빌트인 객체</h1>
<h2 id="211-자바스크립트-객체의-분류">21.1 자바스크립트 객체의 분류</h2>
<p>자바스크립트 객체는 다음과 같이 크게 3개의 객체로 분류할 수 있다.</p>
<ul>
<li>표준 빌트인 객체<ul>
<li>ECMAScript 사양에 정의된 객체를 말하며 애플리케이션 전역의 공통 기능을 제공한다. 자바스크립트 실행 환경에 관계없이 언제나 사용할 수 있다. 전역 객체의 프로퍼티로서 제공된다.</li>
</ul>
</li>
<li>호스트 객체<ul>
<li>ECMAScript 사양에 정의되어 있지 않지만 자바스크립트 실행 환경에서 추가로 제공하는 객체를 말한다.</li>
<li>브라우저 환경에서는 DOM, BOM, Canvas, XMLHttpRequest, fetch 등과 같은 클라이언트 사이드 웹 API를 호스트 객체로 제공한다.</li>
</ul>
</li>
<li>사용자 정의 객체<ul>
<li>사용자가 직접 정의한 객체</li>
</ul>
</li>
</ul>
<h2 id="212-표준-빌트인-객체">21.2 표준 빌트인 객체</h2>
<p><code>Object</code> <code>String</code> <code>Number</code> <code>Boolean</code> <code>Symbol</code> <code>Date</code> <code>Math</code> <code>RegExp</code> <code>Array</code> <code>Map/Set</code> <code>WeakMap/WeakSet</code> <code>Function</code> <code>Promise</code> <code>Reflect</code> <code>Proxy</code> <code>JSON</code> <code>Error</code> 등 자바스크립트는 40여 개의 표준 빌트인 객체를 제공한다.</p>
<p>이 중에서 <code>Math</code> <code>Reflect</code> <code>JSON</code>을 제외한 표준 빌트인 객체는 모두 인스턴스를 생성할 수 있는 <strong>생성자 함수 객체</strong>다.
생성자 함수 객체인 표준 빌트인 객체는 프로토타입 메서드와 정적 메서드를 제공하고, 생성자 함수 객체가 아닌 <code>Math</code> <code>Reflect</code> <code>JSON</code>는 정적 메서드만을 제공한다.</p>
<p>생성자 함수인 표준 빌트인 객체가 생성한 인스턴스의 프로토타입은 표준 빌트인 객체의 <code>prototype</code> 프로퍼티에 바인딩된 객체다.</p>
<pre><code class="language-js">const numObj = new Number(1.5);

//프로토타입 메서드?
//모든 인스턴스가 상속을 통해 사용할 수 있다.
console.log(numObj.toFixed()); // 2

//정적 메서드?
//인스턴스를 생성하지 않아도 생성자 함수를 통해 사용할 수 있다.
console.log(Number.isInteger(0.5)); // false</code></pre>
<h2 id="213-원시값과-래퍼-객체">21.3 원시값과 래퍼 객체</h2>
<p>문자열이나 숫자, 불리언 등의 원시값이 있는데도 <code>String</code> <code>Number</code> <code>Boolean</code> 등의 표준 빌트인 생성자 함수가 존재하는 이유는 무엇일까?</p>
<p>원시값은 객체가 아니므로 프로퍼티나 메서드를 가질 수 없는데도 객체처럼 동작한다.</p>
<pre><code class="language-js">const str = &quot;hello&quot;;

console.log(str.length); // 5</code></pre>
<p>이는 원시값인 문자열, 숫자, 불리언 값의 경우 <strong>객체처럼 마침표 표기법(또는 대괄호 표기법)으로 접근하면 자바스크립트 엔진이 일시적으로 원시값을 연관된 객체로 변환해 주기 때문</strong>이다.</p>
<p>즉, 원시값을 객체처럼 사용하면 자바스크립트 엔진은 <strong>암묵적으로 연관된 객체를 생성</strong>하여 생성된 객체로 <strong>프로퍼티에 접근하거나 메서드를 호출하고 다시 원시값으로 되돌린다.</strong></p>
<p>이처럼 문자열, 숫자, 불리언 값에 대해 <strong>객체처럼 접근하면 생성되는 임시 객체</strong>를 <strong>래퍼 객체</strong>라 부른다.</p>
<p>예를 들어, 문자열에 대해 마침표 표기법으로 접근하면 그 순간 래퍼 객체인 <code>String</code> 생성자 함수의 인스턴스가 생성되고, 기존 문자열은 <code>[[StringData]]</code> 내부 슬롯에 할당된다.
그리고 래퍼 객체의 처리가 종료되면 <code>[[StringData]]</code> 내부 슬롯에 할당되었던 기존 원시값으로 식별자가 원시값을 갖도록 되돌리고 래퍼 객체는 가비지 컬렉션의 대상이 된다.</p>
<pre><code class="language-js">const str = &quot;hello&quot;;

str.name = &quot;Sangjo&quot;
//래퍼 객체가 암묵적으로 생성되고, [[StringData]] 슬롯에 hello가 할당된다.
//name 프로퍼티가 동적 생성되며, 값으로 Sangjo가 할당된다
//그리고 str은 다시 [[StringData]] 슬롯의 문자열로 돌아간다.

console.log(str.name); // undefined
//위에서 생성되었던 래퍼 객체는 이미 가비지 컬렉션의 대상이 되어 사라졌다.
//이번에 생성된 래퍼 객체는 이전의 래퍼 객체와 다르다. 따라서 name 프로퍼티가 존재하지 않는다.</code></pre>
<p>이처럼, <strong>문자열, 숫자, 불리언 값은 굳이 표준 빌트인 생성자 함수를 사용하여 생성할 필요가 없다.</strong>
(참고, 심벌은 일반적인 원시값과 달리 리터럴 표기법으로 생성할 수 없다.)
문자열, 숫자, 불리언, 심벌 이외의 원시값인 <code>null</code>과 <code>undefined</code>는 래퍼 객체를 생성하지 않는다.
따라서 <strong><code>null</code>과 <code>undefined</code>를 객체처럼 사용하면 에러가 발생한다.</strong></p>
<h2 id="214-전역-객체">21.4 전역 객체</h2>
<blockquote>
<p>💡 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체이며, 어떤 객체에도 속하지 않은 최상위 객체다.</p>
</blockquote>
<p>전역 객체는 자바스크립트 환경에 따라 지칭하는 이름이 제각각이다.
브라우저 환경에서는 <code>window</code>, Node.js 환경에서는 <code>global</code>이 전역 객체를 가리킨다.</p>
<ul>
<li><code>globalThis</code><ul>
<li>ES11에서 도입된 식별자로, 전역 객체를 가리키던 다양한 식별자를 통일한 식별자다.</li>
</ul>
</li>
</ul>
<p>전역 객체는 표준 빌트인 객체와 환경에 따른 호스트 객체, 그리고 <code>var</code> 키워드로 선언한 전역 변수와 전역 함수를 프로퍼티로 갖는다.</p>
<p>즉, <strong>전역 객체</strong>는 계층적 구조상 어떤 객체에도 속하지 않은 <strong>모든 빌트인 객체의 최상위 객체</strong>다.
그렇지만 <strong>프로토타입 상속 관계상에서 최상위 객체라는 의미는 아니다.</strong>
<strong>전역 객체 자신은 어떤 객체의 프로퍼티도 아니다.</strong></p>
<p>전역 객체의 특징</p>
<ul>
<li>개발자가 의도적으로 생성할 수 없다.(전역 객체를 생성할 수 있는 생성자 함수가 없다.)</li>
<li>전역 객체의 프로퍼티를 참조할 때 <code>window</code> 또는 <code>global</code>을 생략할 수 있다.</li>
<li>전역 객체는 모든 표준 빌트인 객체를 프로퍼티로 가지고 있다.</li>
<li>자바스크립트 실행 환경에 따라 추가적인 프로퍼티와 메서드를 갖는다.(호스트 객체)</li>
<li><code>var</code> 키워드로 선언한 전역 변수와 암묵적 전역, 그리고 전역 함수는 전역 객체의 프로퍼티다.</li>
<li>브라우저 환경의 모든 자바스크립트 코드는 전역 객체 <code>window</code>를 공유한다.</li>
</ul>
<h3 id="2141-빌트인-전역-프로퍼티">21.4.1 빌트인 전역 프로퍼티</h3>
<p>빌트인 전역 프로퍼티는 <strong>전역 객체의 프로퍼티</strong>를 의미한다.
주로 애플리케이션 전역에서 사용하는 값을 제공한다.</p>
<h4 id="infinity">Infinity</h4>
<p><code>Infinity</code> 프로퍼티는 무한대를 나타내는 숫자값 <code>Infinity</code>를 갖는다.</p>
<pre><code class="language-js">console.log(3/0); // Infinity
console.log(-3/0); // -Infinity
console.log(typeof Infinity); // number</code></pre>
<h4 id="nan">NaN</h4>
<p><code>NaN</code> 프로퍼티는 숫자가 아님을 나타내는 숫자값 <code>NaN</code>을 갖는다.</p>
<pre><code class="language-js">console.log(Number(&quot;str&quot;)); // NaN
console.log(1 * &quot;str&quot;); // NaN
console.log(typeof NaN); // number</code></pre>
<h4 id="undefined">undefined</h4>
<p><code>undefined</code> 프로퍼티는 원시 타입 <code>undefined</code>를 값으로 갖는다.</p>
<pre><code class="language-js">var foo;
console.log(foo); // undefined
console.log(typeof undefined); // undefined</code></pre>
<h3 id="2142-빌트인-전역-함수">21.4.2 빌트인 전역 함수</h3>
<p>빌트인 전역 함수는 애플리케이션 전역에서 호출할 수 있는 빌트인 함수로서 <strong>전역 객체의 메서드다.</strong></p>
<h4 id="eval">eval</h4>
<p><code>eval</code> 함수는 자바스크립트 코드를 나타내는 문자열을 인수로 전달받는다.
전달받은 문자열 코드가 표현식이라면 <code>eval</code> 함수는 문자열 코드를 런타임에 평가하여 값을 생성한다.
전달받은 인수가 표현식이 아닌 문이라면 <code>eval</code> 함수는 문자열 코드를 런타임에 실행한다.</p>
<pre><code class="language-js">const x = 1;

function foo() {
  eval(&quot;var x = 2;&quot;);
  console.log(x) // 2
}

foo()
console.log(x) // 1</code></pre>
<p>(위 예제는 eval이 없어도 동일하게 출력됨)</p>
<p><code>eval</code>이 런타임에 <code>var x = 2;</code>를 실행하기 전에, 이미 <code>foo</code> 함수의 스코프는 존재한다.
하지만 <strong><code>eval</code> 함수는 기존의 스코프를 런타임에 동적으로 수정한다.</strong></p>
<p>단, strict mode에서 <code>eval</code> 함수는 기존의 스코프를 수정하지 않고 <code>eval</code> 함수 자신의 자체적인 스코프를 생성한다.</p>
<pre><code class="language-js">const x = 1;

function foo() {
  &#39;use strict&#39;;

  eval(&quot;var x = 2; console.log(x);&quot;); // 2
  console.log(x) // 1
}

foo()
console.log(x) // 1</code></pre>
<p>또한, <code>eval</code> 함수가 인수로 전달받은 문자열 코드가 <code>let</code> <code>const</code> 키워드를 사용한 변수 선언문이라면 암묵적으로 strict mode가 적용된다.</p>
<pre><code class="language-js">const x = 1;

function foo() {
  eval(&quot;var x = 2; console.log(x);&quot;); // 2

  //const 키워드가 사용되어 strict mode가 적용됨. eval 함수 자신만의 스코프 생성.
  eval(&quot;const x = 3; console.log(x);&quot;); // 3
  console.log(x) // 2
}

foo()
console.log(x) // 1</code></pre>
<p><code>eval</code> 함수를 통해 사용자로부터 입력받은 콘텐츠를 수행하는 것은 보안에 매우 취약하다.
또한 <code>eval</code> 함수를 통해 실행되는 코드는 자바스크립트 엔진에 의해 최적화가 수행되지 않으므로 일반적인 코드 실행에 비해 처리 속도가 느리다.</p>
<p><strong>따라서 <code>eval</code> 함수의 사용은 금지해야 한다.</strong></p>
<h4 id="isfinite">isFinite</h4>
<p>전달받은 인수가 유한수이면 <code>true</code>를, 무한수이면 <code>false</code>를 반환한다.
전달받은 인수의 타입이 숫자가 아니면, 숫자로 타입을 변환한 후 검사를 수행한다.
이때 인수가 <code>NaN</code>으로 평가되는 값이라면 <code>false</code>를 반환한다.</p>
<p><code>isFinite(null)</code>은 <code>true</code>다.
<code>null</code>을 숫자로 변환하면 <code>0</code>이기 때문이다.</p>
<h4 id="isnan">isNaN</h4>
<p>전달받은 인수가 <code>NaN</code>인지 검사하여 결과를 불리언 타입으로 반환한다.
전달받은 인수의 타입이 숫자가 아닌 경우 숫자로 타입을 변환한 후 검사를 수행한다.</p>
<p><code>isNaN(&quot;&quot;)</code>은 <code>false</code>다.
<code>&quot;&quot;</code>을 숫자로 변환하면 <code>0</code>이기 때문이다.</p>
<h4 id="parsefloat">parseFloat</h4>
<p>전달받은 문자열 인수를 부동 소수점 숫자, 즉 실수로 해석하여 반환한다.</p>
<h4 id="parseint">parseInt</h4>
<p>전달받은 문자열 인수를 정수로 해석하여 반환한다.
전달받은 인수가 문자열이 아니면 문자열로 변환한 다음 정수로 해석하여 반환한다.</p>
<p>두 번째 인수로 진법을 나타내는 기수를 전달할 수 있다.
첫 번째 문자가 해당 지수의 숫자로 변환될 수 없다면 <code>NaN</code>을 반환한다.
첫 번째 문자는 변환될 수 있으나, 그 이후에 변환될 수 없는 문자를 만날 경우 해당 문자와 그 이후의 문자들은 무시된다.
문자열에 공백이 있다면 첫 번째 문자열만 해석된다. 단, 앞뒤의 공백은 무시된다.</p>
<pre><code class="language-js">// 첫 번째 문자가 10진수로 해석할 수 없음.
parseInt(&quot;A0&quot;); // NaN

// 2는 2진수로 해석할 수 없음.
parseInt(&quot;1020000100&quot;, 2); // 2

// 공백이 있다면 첫 번째 문자열만 해석된다.
parseInt(&quot;10 20 30&quot;); // 10

// 앞뒤 공백은 무시된다.
parseInt(&quot; 10 &quot;); // 10</code></pre>
<h4 id="encodeuri--decodeuri">encodeURI / decodeURI</h4>
<p><code>encodeURI</code> 함수는 완전한 URI를 문자열로 전달받아 이스케이프 처리를 위해 인코딩한다.
<code>decodeURI</code> 함수는 인코딩된 URI를 문자열로 전달받아 이스케이프 처리 이전으로 디코딩한다.</p>
<p>이스케이프 처리는 네트워크를 통해 정보를 공유할 때 어떤 시스템에서도 읽을 수 있는 아스키 문자 셋으로 변환하는 것이다.
<code>encodeURI</code>는 쿼리 스트링 구분자로 사용되는 <code>=</code> <code>?</code> <code>&amp;</code>는 인코딩하지 않는다.</p>
<h4 id="encodeuricomponent--decodeuricomponent">encodeURIComponent / decodeURIComponent</h4>
<p><code>encodeURIComponent</code> 함수는 URI 구성 요소를 인수로 전달받아 인코딩한다.
<code>decodeURIComponent</code> 함수는 인코딩된 URI를 구성 요소를 문자열로 전달받아 이스케이프 처리 이전으로 디코딩한다.</p>
<p><code>encodeURIComponent</code>는 쿼리 스트링 구분자로 사용되는 <code>=</code> <code>?</code> <code>&amp;</code>까지 인코딩한다.</p>
<h3 id="2143-암묵적-전역">21.4.3 암묵적 전역</h3>
<pre><code class="language-js">var x = 10;

function foo() {
  // 선언하지 않은 식별자에 값을 할당
  y = 20; // window.y = 20;
}
foo();

console.log(x + y); // 30</code></pre>
<p><code>foo</code> 함수 내의 <code>y</code>는 선언하지 않은 식별자이기 때문에 참조 에러가 발생할 것이라고 생각할 수 있다.
하지만 마치 선언된 전역 변수처럼 동작하는데, 이는 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되기 때문이다.</p>
<p><code>foo</code> 함수가 호출되면 자바스크립트 엔진은 <code>y</code> 변수에 값을 할당하기 위해 스코프 체인을 통해 선언된 변수인지 확인한다.
이때 어느 스코프에서도 <code>y</code> 변수의 선언을 찾을 수 없으므로 참조 에러가 발생한다.
하지만 자바스크립트 엔진은 <code>y = 20</code> 을 <code>window.y = 20</code> 으로 해석하여 전역 객체에 프로퍼티를 동적 생성한다.
결국 <code>y</code>는 전역 객체의 프로퍼티가 되어 전역 변수처럼 동작한다.
이러한 현상을 암묵적 전역이라 한다.</p>
<p><code>y</code>는 변수 선언 없이 단지 전역 객체의 프로퍼티로 추가되었을 뿐이다.
따라서 <code>y</code> 는 변수가 아니고 변수 호이스팅이 발생하지 않는다.</p>
<pre><code class="language-js">console.log(x); // undefined
console.log(y); // ReferenceError

var x = 10;

function foo() {
  // 선언하지 않은 식별자에 값을 할당
  y = 20; // window.y = 20;
}
foo();

console.log(x + y); // 30</code></pre>
<p>또한 변수가 아닌 프로퍼티인 <code>y</code>는 <code>delete</code> 연산자로 삭제할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 20. strict mode]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-20.-strict-mode</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-20.-strict-mode</guid>
            <pubDate>Sun, 06 Nov 2022 06:39:45 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="20-strict-mode">20. strict mode</h1>
<h2 id="201-strict-mode란">20.1 strict mode란?</h2>
<blockquote>
<p>💡 strict mode는 자바스크립트 언어의 문법을 좀 더 엄격히 적용하여 오류를 발생시킬 가능성이 높거나 자바스크립트 엔진의 최적화 작업에 문제를 일으킬 수 있는 코드에 대해 명시적인 에러를 발생시킨다.</p>
</blockquote>
<pre><code class="language-js">function foo() {
  x = 10;
}
foo();

console.log(x); // 10</code></pre>
<p>위 예시에서, <code>x</code> 변수는 선언되지 않았다.
자바스크립트 엔진은 <code>foo</code> 함수의 스코프에서부터 출발하여 전역 스코프까지 <code>x</code>를 찾게 된다.
그런데, 어디에도 없는 <code>x</code> 변수로 인해 에러가 발생할 것 같지만 <strong>자바스크립트 엔진은 암묵적으로 전역 객체에 <code>x</code> 프로퍼티를 동적 생성한다.</strong>
이때 전역 객체의 x 프로퍼티는 마치 전역 변수처럼 사용할 수 있다.
이러한 현상을 <strong>암묵적 전역</strong>이라 한다.</p>
<p>이러한 암묵적 전역을 예상치 못한 오류를 발생시키는 원인이 될 가능성이 크다.
따라서 잠재적인 오류를 방생시키기 어려운 개발 환경을 만드는 것이 바로 <strong>strict mode</strong>다.</p>
<h2 id="202-strict-mode의-적용">20.2 strict mode의 적용</h2>
<p>strict mode를 적용하려면 전역의 선두, 혹은 함수 몸체의 선두에 <code>&#39;use strict&#39;;</code>를 추가한다.
전역의 선두에 추가하면 스크립트 전체에 strict mode가 적용된다.</p>
<h2 id="203-전역에-strict-mode를-적용하는-것은-피하자">20.3 전역에 strict mode를 적용하는 것은 피하자</h2>
<p>스크립트 단위로 적용된 strict mode는 다른 스크립트에 영향을 주지 않고 해당 스크립트에 한정되어 적용된다.
하지만, strict mode 스크립트와 non-strict mode 스크립트를 혼용하는 것은 오류를 발생시킬 수 있다.</p>
<p>특히, 외부 서드파티 라이브러리를 사용하는 경우 라이브러리가 non-strict mode인 경우도 있기 때문에 전역에 strict mode를 사용하는 것은 바람직하지 않다.</p>
<p>이러한 경우, 즉시 실행 함수로 스크립트 전체를 감싸서 스코프를 구분하고 즉시 실행 함수의 선두에 strict mode를 적용한다.</p>
<h2 id="204-함수-단위로-strict-mode를-적용하는-것도-피하자">20.4 함수 단위로 strict mode를 적용하는 것도 피하자</h2>
<p>어떤 함수는 strict mode를 적용하고, 어떤 함수는 적용하지 않는 것도 바람직하지 않다.
또한 일일히 적용하는 것이 번거롭기도 하다.</p>
<pre><code class="language-js">(function() {
  var let = 10; // non-strict mode에서는 에러가 발생하지 않음
  function foo() {
    &#39;use strict&#39;;
    let = 20 // syntaxError
  }
  foo();
};)()</code></pre>
<h2 id="205-strict-mode가-발생시키는-에러">20.5 strict mode가 발생시키는 에러</h2>
<p>strict mode를 적용했을 때 에러가 발생하는 대표적인 사례</p>
<h3 id="2051-암묵적-전역">20.5.1 암묵적 전역</h3>
<p>선언하지 않은 변수를 참조하면 ReferenceError가 발생한다.</p>
<pre><code class="language-js">(function () {
  &#39;use strict&#39;;

  x = 10;
  console.log(x); // ReferenceError
})()</code></pre>
<h3 id="2052-변수-함수-매개변수의-삭제">20.5.2 변수, 함수, 매개변수의 삭제</h3>
<p>delete 연산자로 변수, 함수, 매개변수를 삭제하면 SyntaxError가 발생한다.</p>
<pre><code class="language-js">(function () {
  &#39;use strict&#39;;

  x = 10;
  delete x; // SyntaxError
})()</code></pre>
<h3 id="2053-매개변수-이름의-중복">20.5.3 매개변수 이름의 중복</h3>
<p>중복된 매개변수 이름을 사용하면 SyntaxError가 발생한다.</p>
<pre><code class="language-js">(function () {
  &#39;use strict&#39;;

  // SyntaxError
  function foo(x, x) {
    return x + x;
  }
  console.log(foo(1, 2));
})()</code></pre>
<h3 id="2054-with문의-사용">20.5.4 with문의 사용</h3>
<p>with 문을 사용하면 SyntaxError가 발생한다.</p>
<p>with 문은 전달된 객체를 스코프 체인에 추가한다.
with문은 동일한 객체의 프로퍼티를 반복해서 사용할 때 객체 이름을 생략할 수 있어서 코드가 간단해지는 효과가 있지만, 성능과 가독성이 나빠지는 문제가 있다.</p>
<p>따라서 사용하지 않는 것을 권장함.</p>
<pre><code class="language-js">(function () {
  &#39;use strict&#39;;

  // SyntaxError
  with({x: 1}) {
    console.log(x);
  }
})()</code></pre>
<h2 id="206-strict-mode-적용에-의한-변화">20.6 strict mode 적용에 의한 변화</h2>
<h3 id="2061-일반-함수의-this">20.6.1 일반 함수의 this</h3>
<p>strict mode에서 함수를 일반 함수로서 호출하면 <code>this</code>에 <code>undefined</code>가 바인딩된다.
생성자 함수가 아닌 <strong>일반 함수 내부에서는 <code>this</code>를 사용할 필요가 없기 때문이다.</strong>
이때 에러는 발생하지 않는다.</p>
<h3 id="2062-arguments-객체">20.6.2 arguments 객체</h3>
<p>strict mode에서는 매개변수에 전달된 인수를 재할당하여 변경해도 arguments 객체에 반영되지 않는다.
초기값을 유지하는 듯 하다.</p>
<pre><code class="language-js">function foo(a) {
  &#39;use strict&#39;;

  a = 2;
  console.log(arguments); // {0: 1, length: 1}
}

foo(1);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 19. 프로토타입]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-19.-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-bdtutx9z</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-19.-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-bdtutx9z</guid>
            <pubDate>Sun, 06 Nov 2022 06:05:03 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="19-프로토타입">19. 프로토타입</h1>
<blockquote>
<p>💡 자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어다.</p>
</blockquote>
<p>자바스크립트는 객체 기반의 프로그래밍 언어이며 자바스크립트를 이루고 있는 거의 모든 것이 객체다.</p>
<h2 id="191-객체지향-프로그래밍">19.1 객체지향 프로그래밍</h2>
<blockquote>
<p>💡 <strong>객체지향 프로그래밍</strong>은 여러 개의 독립적 단위, 즉 <strong>객체의 집합으로 프로그램을 표현</strong>하려는 프로그래밍 패러다임이다.
(명령형 프로그래밍은 프로그램을 명령어 또는 함수의 목록으로 보는 전통적인 절차지향적 관점을 갖고 있다.)</p>
</blockquote>
<p>사람에게는 <code>이름</code> <code>나이</code> <code>주소</code> <code>성격</code> <code>직업</code> 등의 여러 속성이 있다.
이 속성을 구체적으로 표현하면 <strong>특정한 사람을 다른 사람과 구별하여 인식</strong>할 수 있게 된다.</p>
<p>사람에게 다양한 속성이 있지만, 만약 우리가 구현하려는 프로그램에서는 사람의 <code>이름</code> <code>주소</code> 만 관심이 있다고 해보자.
이처럼 사람의 <strong>여러 속성 중에서 프로그램에 필요한 속성만 간추려 내어 표현</strong>하는 것을 <strong>추상화</strong>라 한다.</p>
<pre><code class="language-js">const person = {
  name: &quot;Sangjo&quot;,
  address: &quot;Seoul&quot;
};</code></pre>
<p>이제 프로그래머는 <code>person</code>을 다른 객체와 구별하여 인식할 수 있다.
이처럼 <strong>속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조</strong>를 <strong>객체</strong>라 부른다.
그리고 <strong>객체지향 프로그래밍</strong>은 <strong>독립적인 객체들의 집합으로 프로그램을 표현</strong>하려는 프로그래밍 패러다임이다.</p>
<p>또한, 객체지향 프로그래밍은 객체의 <strong>상태</strong>를 나타내는 데이터와 상태 데이터를 조작할 수 있는 <strong>동작</strong>을 하나의 논리적인 단위로 묶어서 생각한다.
이때, 객체의 상태 데이터를 <strong>프로퍼티</strong>, 동작을 <strong>메서드</strong>라 부른다.</p>
<pre><code class="language-js">const circle = {

  //반지름
  radius: 5,

  //지름: 반지름*2
  getDiameter() {
    return 2 * this.radius;
  }

};</code></pre>
<p>위 예시에서, <code>radius</code>는 프로퍼티이며, <code>getDiameter</code>는 메서드다.</p>
<h2 id="192-상속과-프로토타입">19.2 상속과 프로토타입</h2>
<blockquote>
<p>💡 상속은 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.</p>
</blockquote>
<p>자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다.</p>
<pre><code class="language-js">//생성자 함수
function Circle(radius) {
  this.radius: radius,
  this.getDiameter() {
    return 2 * this.radius;
  }
};

const circle1 = new Circle(10)
const circle2 = new Circle(20)

console.log(circle1.getDiameter === circle2.getDiameter); //false</code></pre>
<p>위 예시에서, <code>Circle</code> 생성자 함수는 여러 개의 인스턴스를 만들어내기에 유용하다.
다만, 인스턴스를 생성할 때마다 <code>getDiameter</code> 메서드가 중복 생성되고 있다.
모든 인스턴스가 동일한 내용의 메서드를 사용하므로 단 하나만 생성하여 모든 인스턴스가 공유하는 것이 바람직하다.</p>
<p>이처럼 메모리를 불필요하게 낭비하며 퍼포먼스에 악영향을 주는 중복을 제거해보자.
자바스크립트는 <strong>프로토타입을 기반으로 상속을 구현</strong>한다.</p>
<pre><code class="language-js">//생성자 함수
function Circle(radius) {
  this.radius: radius,
};

Circle.prototype.getDiameter = function() {
  return 2 * this.radius;
}

const circle1 = new Circle(10)
const circle2 = new Circle(20)

console.log(circle1.getDiameter === circle2.getDiameter); //true</code></pre>
<p><code>Circle</code> <strong>생성자 함수에게는 <code>prototype</code>이라는 프로퍼티가 존재</strong>한다.
이 <code>Circle.prototype</code>는 <code>Circle</code> <strong>생성자 함수가 생성한 모든 인스턴스에게 상속</strong>된다.
따라서, <code>Circle.prototype</code>에 <code>getDiameter</code> 메서드를 할당한다면, 중복 생성 없이 <strong>모든 인스턴스가 하나의 메서드를 공유할 수 있다.</strong></p>
<h2 id="193-프로토타입-객체">19.3 프로토타입 객체</h2>
<blockquote>
<p>💡 프로토타입 객체는 객체지향 프로그래밍의 근간을 이루는 객체 간 상속을 구현하기 위해 사용된다.
aka. 유전자</p>
</blockquote>
<p>프로토타입은 어떤 객체의 상위 객체의 역할을 하는 객체로서 다른 객체에 공유 프로퍼티를 제공한다.
프로토타입을 상속받은 하위 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용할 수 있다.</p>
<p>모든 객체는 <code>[[Prototype]]</code> 이라는 내부 슬롯을 갖는다.
이 <code>[[Prototype]]</code> 이라는 내부 슬롯의 값은 프로토타입의 참조다.
그런데, 이 <code>[[Prototype]]</code>은 객체 생성 방식마다 조금 다르게 결정된다.</p>
<ul>
<li><strong>객체 리터럴</strong>로 생성된 객체의 프로토타입은 <code>Object.prototype</code>이다.</li>
<li><strong>생성자 함수</strong>로 생성된 객체의 프로토타입은 생성자 함수의 <code>prototype</code> 프로퍼티에 바인딩되어 있는 객체다.</li>
</ul>
<p><del>객체 리터럴로 생성해도 결국 <code>new Object</code>로 생성된다는 것을 생각해보면 결국 같은 말임을 알 수 있다.
(라고 적었는데... 이게 맞는 해석인지 잘 모르겠네...)</del>
아니었다. 객체 리터럴에 의해 생성된 객체는 Object 생성자 함수가 생성한 객체와는 다르다.
그러나, 같은 것이라고 생각해도 무리는 없다... ㅋㅋㅋ</p>
<p><code>[[Prototype]]</code> 내부 슬롯에는 직접 접근할 수 없지만, <code>__proto__</code> 접근자 프로퍼티를 통해 자신의 프로토타입에 간접적으로 접근할 수 있다.
즉, 자신의 <code>[[Prototype]]</code> 내부 슬롯이 가리키는 프로토타입에 간접적으로 접근할 수 있다는 말이다.</p>
<h3 id="1931-__proto__-접근자-프로퍼티">19.3.1 <code>__proto__</code> 접근자 프로퍼티</h3>
<blockquote>
<p>💡 모든 객체는 <code>__proto__</code> 접근자 프로퍼티를 통해 자신의 프로토타입, 즉 [[Prototype]]` 내부 슬롯에 간접적으로 접근할 수 있다.</p>
</blockquote>
<h4 id="__proto__-는-접근자-프로퍼티다"><code>__proto__</code> 는 접근자 프로퍼티다.</h4>
<p>내부 슬롯은 프로퍼티가 아니다.
따라서 자바스크립트는 원칙적으로 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않는다.</p>
<p>단, 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공한다.
<code>[[Prototype]]</code> 내부 슬롯에도 직접 접근은 불가능하다.
하지만 <code>__proto__</code> 접근자 프로퍼티를 통해 간접적으로 접근할 수 있다.</p>
<p><strong>접근자 프로퍼티는 자체적으로 값을 갖지 않고, 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성되어 있다.</strong>
즉, 접근자 프로퍼티는 <strong><code>[[Get]]</code> <code>[[Set]]</code> 프로퍼티 어트리뷰트로 구성된 프로퍼티</strong>다.</p>
<pre><code class="language-js">const obj = {};
const parent = {x: 1};

//getter 함수인 get __proto__가 호출되어 obj 객체의 프로토타입을 가져옴
obj.__proto__;

//setter 함수인 set __proto__가 호출되어 obj 객체의 프로토타입을 수정함
obj.__proto__ = parent;
console.log(obj.x); //1</code></pre>
<h4 id="__proto__-접근자-프로퍼티를-통해-프로토타입에-접근하는-이유"><code>__proto__</code> 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유</h4>
<p><code>[[Prototype]]</code> 내부 슬롯의 값, 즉 프로토타입에 접근하기 위해 접근자 프로퍼티를 사용하는 이유는 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서다.</p>
<p>두 객체가 서로를 프로토타입으로 설정하게 되는 비정상적인 프로토타입 체인을 방지하기 위해 <code>__proto__</code> 접근자 프로퍼티는 에러를 발생시킨다.</p>
<pre><code class="language-js">const parent = {};
const child = {};

child.__proto__ = parent;
parent.__proto__ = child; // TypeError: Cyclic __proto__ value</code></pre>
<p>프로토타입 체인은 <strong>단방향 링크드 리스트</strong>로 구현되어야 한다.
즉, <strong>프로퍼티 검색 방향이 한쪽 방향으로만 흘러가야 한다.</strong>
하지만 위 예시의 경우 단방향이 아닌 <strong>순환 참조</strong>하는 비정상적인 프로토타입 체인이 만들어지고, 이 경우 <strong>프로토타입 체인의 종점이 존재하지 않는다.</strong>
프로토타입 체인의 종점이 없다면 <strong>프로퍼티를 검색할 경우 무한 루프에 빠지게 된다.</strong>
따라서 <code>__proto__</code> 접근자 프로퍼티를 통해 이러한 경우를 체크하여 교체하도록 구현되어 있다.</p>
<h4 id="__proto__-접근자-프로퍼티를-코드-내에서-직접-사용하는-것은-권장하지-않는다"><code>__proto__</code> 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.</h4>
<p>아래와 같은 경우는 <code>Object.prototype</code>을 상속받지 않는다.
따라서 <code>__proto__</code> 접근자 프로퍼티를 사용할 수 없다.</p>
<pre><code class="language-js">const obj = Object.create(null);
console.log(obj.__proto__); // undefined</code></pre>
<p>따라서 <code>__proto__</code> 접근자 프로퍼티 대신,
프로토타입의 참조를 취득하고 싶은 경우는 <code>Object.getPrototypeOf</code> 메서드를,
프로토타입을 교체하고 싶은 경우는 <code>Object.setPrototypeOf</code> 메서드를 사용할 것을 권장한다.</p>
<pre><code class="language-js">const obj = {};
const parent = {x: 1};

Object.getPrototypeOf(obj); // obj.__proto__
Object.setPrototypeOf(obj, parent); // obj.__proto__ = parent;

console.log(obj.x); // 1</code></pre>
<h3 id="1932-함수-객체의-prototype-프로퍼티">19.3.2 함수 객체의 prototype 프로퍼티</h3>
<blockquote>
<p>💡 함수 객체만이 소유하는 <code>prototype</code> 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.</p>
</blockquote>
<p>일반 객체 <code>{}</code>는 <code>prototype</code> 프로퍼티를 소유하지 않는다.
하지만 <strong>함수 객체는 <code>prototype</code> 프로퍼티를 소유한다.</strong>
이유는 <strong><code>non-constructor</code>는 <code>prototype</code> 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않기 때문</strong>이다.
같은 이유로, <strong>화살표 함수와 ES6 메서드 축약 표현</strong>으로 정의한 메서드는 <code>non-constructor</code>이기에 <strong><code>prototype</code> 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않는다.</strong></p>
<ul>
<li>위에서 공부했던 <strong><code>__proto__</code> 접근자 프로퍼티</strong>는 <strong>모든 객체가 소유</strong>하고 있으며, 부모의 유전자인 <strong>프로토타입에 접근 또는 교체</strong>하기 위해 사용한다.</li>
<li>반면, <strong><code>prototype</code> 프로퍼티</strong>는 <strong><code>constructor</code>만이 소유</strong>한다. 즉, 생성자 함수가 자신이 <strong>생성할 인스턴스의 프로토타입을 할당</strong>하기 위해 사용하는 것이다.</li>
</ul>
<p>둘 다 프로토타입의 참조를 값으로 갖는다는 점은 동일하다.</p>
<pre><code class="language-js">function Person(name) {
  this.name = name;
};

const me = new Person(&quot;Sangjo&quot;);

console.log(Person.prototype === me.__proto__); // true</code></pre>
<h3 id="1933-프로토타입의-constructor-프로퍼티와-생성자-함수">19.3.3 프로토타입의 <code>constructor</code> 프로퍼티와 생성자 함수</h3>
<p>모든 프로토타입은 <code>constructor</code> 프로퍼티를 갖는다.
이 <code>constructor</code> 프로퍼티는 <code>prototype</code> 프로퍼티로 <strong>자신을 참조하고 있는 생성자 함수를 가리킨다.</strong>
즉, 인스턴스 입장에서 <code>constructor</code> 프로퍼티는 자신을 생성해준 부모(생성자 함수)와 같다.</p>
<pre><code class="language-js">function Person(name) {
  this.name = name;
};

const me = new Person(&quot;Sangjo&quot;);

console.log(me.constructor === Person); // true</code></pre>
<p><code>Person</code> 생성자 함수에는 <code>[[Prototype]]</code> 이라는 내부 슬롯이 있고, 이 슬롯의 값은 프로토타입의 참조다.
이 <code>Person.prototype</code> 안에는 <code>constructor</code> 프로퍼티가 있는데, 이는 생성자 함수 자기 자신을 가리킨다.
이 <code>Person.prototype</code>이 인스턴스인 <code>me</code>객체에게 상속되면서 <code>me</code>객체는 <code>Person.prototype</code>를 사용할 수 있고, <code>constructor</code>도 사용할 수 있게 된다.</p>
<h2 id="194-리터럴-표기법에-의해-생성된-객체의-생성자-함수와-프로토타입">19.4 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입</h2>
<p>리터럴 표기법에 의해 생성된 객체는 실제로는 <code>Object</code> 생성자 함수에 의해 생성된 것이 아닐까? 라는 의문이 있었는데, 이 내용을 통해 해소되었다.</p>
<p>결론적으로, <strong>리터럴 표기법에 의해 생성된 객체는 <code>Object</code> 생성자 함수에 의해 생성된 객체와 다르다.</strong>
<strong>그러나, 같은 것이라고 생각해도 무리는 없다.(?)</strong>
세부적인 생성 과정에 미묘한 차이가 있지만, 결국 특성이 동일하기 때문...</p>
<p>우리가 객체 리터럴을 통해 생성한 객체의 생성자 함수는 무엇일까?</p>
<pre><code class="language-js">const obj = {};
console.log(obj.constructor === Object); // true</code></pre>
<p>바로 <code>Object</code> 생성자 함수다.
그럼 객체 리터럴로 생성한 객체의 생성자 함수는 <code>Object</code> 생성자 함수라는 말 아닌가?
하지만 아니다.</p>
<p>객체 리터럴로 객체를 생성할 경우, 추상 연산 <code>OrdinaryObjectCreate</code>를 호출하여 <code>Object.prototype</code>을 프로토타입으로 갖는 객체를 생성한다.
이것저것 적혀있지만, 결국 생성하는 과정이 미묘하게 다를 뿐... 동일한 것을 생성하고 동일한 특성을 갖게 된다.
따라서, 엄밀히 말하자면 다른게 맞지만... 같다고 생각해도 무리가 없다...</p>
<p>근데 함수 객체의 경우에는 좀 차이가 있다.</p>
<pre><code class="language-js">function foo() {};
console.log(foo.constructor === Function); // true</code></pre>
<p>함수 객체 역시 함수 선언문으로 생성해도 <code>constructor</code> 프로퍼티를 통해 확인해보면 생성자 함수는 <code>Function</code> 생성자 함수라고 나온다.
하지만 <strong><code>Function</code> 생성자 함수로 생성한 함수는 렉시컬 스코프를 만들지 않고 전역 함수인 것처럼 스코프를 생성하며 클로저도 만들지 않는다.</strong>
따라서 함수 선언문이나 함수 표현식을 평가하여 함수 객체를 생성한 것은 <code>Function</code> 생성자 함수가 아니다.</p>
<p>이처럼, 생성자 함수로 생성한 객체가 아니더라도 가상적인 생성자 함수를 갖는다.
프로토타입과 생성자 함수는 뗄 수 없다.
프로토타입은 생성자 함수와 함께 생성되며, <code>prototype</code> <code>constructor</code>프로퍼티에 의해 연결되어 있기 때문이다.
<strong>프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재한다.</strong></p>
<h2 id="195-프로토타입의-생성-시점">19.5 프로토타입의 생성 시점</h2>
<blockquote>
<p>💡 프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성된다. </p>
</blockquote>
<p><strong>프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재한다.</strong>
생성자 함수는 두 가지 종류가 있다.
<code>Object</code> <code>Function</code>과 같은 <strong>빌트인 생성자 함수</strong>와 <code>Person</code> 같은 <strong>사용자 정의 생성자 함수</strong></p>
<h3 id="1951-사용자-정의-생성자-함수와-프로토타입-생성-시점">19.5.1 사용자 정의 생성자 함수와 프로토타입 생성 시점</h3>
<p>내부 메서드 <code>[[Constructor]]</code>를 갖는 함수 객체, 즉 <strong>화살표 함수나 ES6 메서드 축약 표현으로 정의하지 않은 일반 함수는 new 연산자와 함께 생성자 함수로서 호출할 수 있다.</strong></p>
<p>생성자 함수로서 호출할 수 있는 함수, 즉 <code>constructor</code>는 <strong>함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성된다.</strong></p>
<p>생성자 함수로서 호출할 수 없는 함수, 즉 <code>non-constructor</code>는 프로토타입이 생성되지 않는다.</p>
<p><code>constructor</code>인 함수 중에서도 함수 선언문으로 선언된 함수는 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행된다.
따라서 <strong>함수 선언문으로 정의된 함수는 어떤 코드보다 먼저 평가되어 함수 객체가 되며, 이때 프로토타입도 같이 생성된다.</strong>
함수 표현식의 경우는 공간만 확보하고 평가되지는 않기 때문에 실제 평가될 문에 도달했을때 프로토타입이 같이 생성된다.</p>
<h3 id="1952-빌트인-생성자-함수와-프로토타입-생성-시점">19.5.2 빌트인 생성자 함수와 프로토타입 생성 시점</h3>
<p>모든 빌트인 생성자 함수는 전역 객체가 생성되는 시점에 함께 생성된다.</p>
<h2 id="196-객체-생성-방식과-프로토타입의-결정">19.6 객체 생성 방식과 프로토타입의 결정</h2>
<p>객체를 생성하는 방법에는 여러가지가 있다.
그러나 각 방법마다 세부적인 차이는 있으나 추상 연산 <code>OrdinaryObjectCreate</code>에 의해 생성된다는 공통점이 있다.</p>
<p>추상 연산 <code>OrdinaryObjectCreate</code>은 자신이 생성할 객체의 프로토타입을 인수로 전달받는다.
빈 객체를 생성한 후, 인수로 전달받은 프로토타입을 자신이 생성한 객체의 <code>[[Prototype]]</code> 내부 슬롯에 할당한 다음, 생성한 객체를 반환한다.
즉, 프로토타입은 추상 연산 <code>OrdinaryObjectCreate</code>에 전달되는 인수에 의해 결정된다.
이 인수는 객체가 생성되는 시점에 객체 생성 방식에 의해 결정된다.</p>
<h3 id="1961-객체-리터럴에-의해-성성된-객체의-프로토타입">19.6.1 객체 리터럴에 의해 성성된 객체의 프로토타입</h3>
<p>자바스크립트 엔진은 객체 리터럴을 평가하여 추상 연산 <code>OrdinaryObjectCreate</code>를 호출한다.
이때 추상 연산 <code>OrdinaryObjectCreate</code>에 전달되는 프로토타입은 <code>Object.prototype</code>이다.
객체 리터럴로 생성한 객체들이 <code>Object.prototype</code>에 들어있는 각종 메서드를 사용할 수 있다는 것을 생각해보자.</p>
<h3 id="1962-object-생성자-함수에-의해-성성된-객체의-프로토타입">19.6.2 Object 생성자 함수에 의해 성성된 객체의 프로토타입</h3>
<p><code>Object</code> 생성자 함수를 인수 없이 호출하면 빈 객체가 생성된다.
객체 리터럴과 마찬가지로 추상 연산 <code>OrdinaryObjectCreate</code>가 호출되며, 인수로 <code>Object.prototype</code>이 전달된다.
즉... 사실상 객체 리터럴로 생성된 객체와 동일하다.</p>
<p>다른점이 있다면 프로퍼티를 추가하는 방식에 있다.
객체 리터럴 방식은 객체 리터럴 내부에 프로퍼티를 추가하지만, <code>Object</code> 생성자 함수 방식은 일단 빈 객체를 생성한 이후 프로퍼티를 추가해야 한다.</p>
<h3 id="1963-생성자-함수에-의해-성성된-객체의-프로토타입">19.6.3 생성자 함수에 의해 성성된 객체의 프로토타입</h3>
<p>new 연산자와 함께 생성자 함수를 호출하여 인스턴스를 생성하면 역시나 추상 연산 <code>OrdinaryObjectCreate</code>가 호출된다.
전달되는 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체다.</p>
<h2 id="197-프로토타입-체인">19.7 프로토타입 체인</h2>
<pre><code class="language-js">function Person(name) {
  this.name = name;
};

const me = new Person(&quot;Sangjo&quot;);

console.log(Person.prototype === me.__proto__); // true</code></pre>
<p><code>me</code> 객체의 프로토타입은 <code>Person</code> 객체의 <code>Person.prototype</code>이다.
근데 <code>Person</code> 객체의 프로토타입은 <code>Object.prototype</code>이다.
우리는 <code>me</code> 객체에서도 <code>Object.prototype</code>이 갖는 메서드를 사용할 수 있다는 것을 생각해보자.
즉, <code>me</code> 객체는 <code>Object.prototype</code>도 상속받았다는 것을 알 수 있다.</p>
<p>자바스크립트는 객체의 프로퍼티에 접근하려고 할 때, 해당 객체에 그 프로퍼티가 없다면 <strong><code>[[Prototype]]</code> 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색</strong>한다.
이를 <strong>프로토타입 체인</strong>이라 한다.
<strong>프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 메커니즘이다.</strong></p>
<p>위 예시처럼, 프로토타입 체인을 위로 거슬러 올라가며 검색하게 되는데, 프로토타입 체인의 최상위에 위치하는 객체는 언제나 <code>Object.prototype</code>이다.
<code>Object.prototype</code>을 프로토타입 체인의 종점이라 한다.
그리고 <code>Object.prototype</code>의 프로토타입은 <code>null</code>이다.</p>
<p><code>Object.prototype</code>에서도 프로퍼티를 찾을 수 없다면 <code>undefined</code>를 반환한다.
이때, 에러는 발생하지 않는다.</p>
<p>이처럼 <strong>프로토타입 체인은 상속과 프로퍼티 검색을 위한 메커니즘이다.</strong>
비슷하게 거슬러 올라가며 뭔가를 찾는 것이 있는데, 바로 <strong>스코프 체인</strong>이다.
<strong>스코프 체인은 식별자 검색을 위한 메커니즘이다.</strong></p>
<pre><code class="language-js">me.hasOwnProperty(&#39;name&#39;);</code></pre>
<p>위 경우, 먼저 스코프 체인에서 <code>me</code> 식별자를 찾는다.
거슬러 올라가며 찾는데, 찾게되면 그때부터 <code>me</code> 객체의 프로토타입 체인에서 <code>hasOwnProperty</code> 메서드를 찾기 시작한다.</p>
<p>이처럼, <strong>스코프 체인과 프로토타입 체인은 서로 연관없이 별도로 동작하는 것이 아니라 서로 협력하여 식별자와 프로토타입을 검색하는 데 사용된다.</strong></p>
<h2 id="198-오버라이딩과-프로퍼티-섀도잉">19.8 오버라이딩과 프로퍼티 섀도잉</h2>
<ul>
<li><strong>오버라이딩</strong><ul>
<li>상위 클래스가 가지고 있는 메서드를 하위 클래스가 <strong>재정의</strong>하여 사용하는 방식이다.</li>
</ul>
</li>
<li><strong>프로퍼티 섀도잉</strong><ul>
<li>오버라이딩처럼 상속 관계에 의해 프로퍼티가 가려지는 현상</li>
</ul>
</li>
<li><strong>오버로딩</strong><ul>
<li>함수의 이름은 동일하지만, 매개변수의 타입이나 개수가 다른 메서드를 구현하고, <strong>매개변수에 의해 메서드를 구별하여 호출</strong>하는 방식이다.</li>
</ul>
</li>
</ul>
<pre><code class="language-js">function Parent() {};
Parent.prototype.example = function() {
  console.log(&quot;프로토타입 프로퍼티(메서드)&quot;);
};

const child = new Parent();
child.example = function() {
  console.log(&quot;인스턴스 프로퍼티(메서드)&quot;);
};
// 오버라이딩!

child.example() // &quot;인스턴스 프로퍼티(메서드)&quot;

delete child.example;
// 인스턴스 프로퍼티(메서드)를 삭제하고 나면 다시 프로토타입 프로퍼티(메서드)가 호출된다.
child.example() // &quot;프로토타입 프로퍼티(메서드)&quot;

...</code></pre>
<p>이때, 한번 더 <code>delete child.example;</code>로 프로토타입 프로퍼티(메서드)까지 삭제할 수 있을까?</p>
<pre><code class="language-js">... 위에 이어서

delete child.example;
// 프로토타입 체인을 통해서 프로토타입 프로퍼티(메서드)를 삭제할 수 없다.
child.example() // &quot;프로토타입 프로퍼티(메서드)&quot;</code></pre>
<p>이와 같이, <strong>하위 객체를 통해 프로토타입의 프로퍼티를 변경 또는 삭제하는 것은 불가능</strong>하다.
다시 말해, 하위 객체에서 프로토타입에 get 액세스는 허용되나 set 액세스는 허용되지 않는다.
프로토타입 프로퍼티를 변경 또는 삭제하려면 프로토타입에 직접 접근해야 한다.
<code>delete Parent.prototype.example;</code></p>
<h2 id="199-프로토타입의-교체">19.9 프로토타입의 교체</h2>
<p>프로토타입은 임의의 다른 객체로 변경할 수 있다.
이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다.
이를 활용하여 객체 간의 상속 관계를 동적으로 변경할 수 있다.
프로토타입은 생성자 함수 또는 인스턴스에 의해 교체할 수 있다.</p>
<h3 id="1991-생성자-함수에-의한-프로토타입의-교체">19.9.1 생성자 함수에 의한 프로토타입의 교체</h3>
<pre><code class="language-js">function Person() {};

Person.prototype = {
  sayHello() {
    console.log(&quot;Hello&quot;);
  }
};
// 생성자 함수의 prototype 프로퍼티를 통해 프로토타입을 교체

const me = new Person();</code></pre>
<p>위 예시처럼, <code>Person.prototype</code>에 객체 리터럴을 할당하면 프로토타입을 해당 객체로 교체할 수 있다.</p>
<p>원래 <code>Person.prototype</code>에는 무엇이 들어있었을까?
바로 <code>constructor</code> 프로퍼티다.</p>
<p>그런데 교체한 <code>Person.prototype</code>에는 <code>constructor</code>은 없어졌고, <code>sayHello</code> 메서드가 생겼다.</p>
<p>따라서 <code>Person</code>으로 생성한 <code>me</code> 객체의 생성자 함수를 검색하면 <code>Person</code>이 아닌 <code>Object</code>가 나온다.</p>
<p><strong>프로토타입 체인</strong>을 따라 검색한 결과, <code>Person.prototype</code>에는 <code>constructor</code>가 없어서 그 상위 객체인 <code>Object.prototype</code>에 있는 <code>constructor</code>를 가져오기 때문이다.</p>
<p>이처럼 프로토타입을 교체하면 <code>constructor</code> 프로퍼티와 생성자 함수 간의 연결이 파괴된다.
물론, 아래처럼 교체할 객체 리터럴에 <code>constructor</code>를 명시해준다면 연결을 파괴하지 않을 수 있다.</p>
<pre><code class="language-js">function Person() {};

Person.prototype = {
  constructor: Person,
  sayHello() {
    console.log(&quot;Hello&quot;);
  }
};
// 생성자 함수의 prototype 프로퍼티를 통해 프로토타입을 교체

const me = new Person();</code></pre>
<h3 id="1992-인스턴스에-의한-프로토타입의-교체">19.9.2 인스턴스에 의한 프로토타입의 교체</h3>
<p>인스턴스를 통해 프로토타입을 교체한다는 것은 이미 생성된 객체의 프로토타입을 교체하는 것이다.
이는 인스턴스의 <code>__proto__</code> 접근자 프로퍼티를 통해 가능하다.</p>
<pre><code class="language-js">function Person() {};

const me = new Person();

const newPrototype = {
  sayHello() {
    console.log(&quot;Hello&quot;);
  }
};

me.__proto__ = newPrototype
// 인스턴스의 __proto__ 접근자 프로퍼티를 통해 프로토타입을 교체
// Object.setPrototypeOf(me, newPrototype)
// 이렇게 교체할 수도 있다. 더욱 권장되는 방법임.</code></pre>
<p>단, <strong>생성자 함수의 <code>prototype</code> 프로퍼티를 통해 프로토타입을 교체하는 것과는 차이가 있다.</strong></p>
<p>인스턴스에 의해 교체될 경우, 인스턴스의 프로토타입은 교체된 <code>newPrototype</code>을 가리키지만, <strong>인스턴스를 생성한 <code>Person</code>의 프로토타입은 <code>newPrototype</code>를 가리키지 않는다.</strong>
즉, 인스턴스의 프로토타입만 교체된 것이다.</p>
<p>반면, <strong>생성자 함수의 <code>prototype</code> 프로퍼티를 통해 프로토타입을 교체하면 생성자 함수와 그 인스턴스 모두 같은 프로토타입을 공유</strong>한다.</p>
<p>따라서, 인스턴스에 의한 교체를 생성자 함수에 의한 교체처럼 만들고 싶다면.. 아래 방법을 사용하자.</p>
<pre><code class="language-js">function Person() {};

const me = new Person();

const newPrototype = {
  sayHello() {
    console.log(&quot;Hello&quot;);
  }
};

Person.prototype = newPrototype;
me.__proto__ = newPrototype
// 이렇게 생성자 함수와 인스턴스 모두 연결해줘야함</code></pre>
<h2 id="1910-instanceof-연산자">19.10 instanceof 연산자</h2>
<p><code>instanceof</code> 연산자는 <code>객체 instanceof 생성자 함수</code> 형태로, 좌변에 객체를 가리키는 식별자, 우변에 생성자 함수를 가리키는 식별자를 피연산자로 받는 이항 연산자다.</p>
<p>우변의 생성자 함수의 <code>prototype</code>에 바인딩된 객체가 좌변의 프로토타입 체인 상에 존재하면 <code>true</code>로, 없으면 <code>false</code>로 평가된다.</p>
<p>위의 프로토타입 교체에서 살펴본 예시들 처럼, <code>constructor</code> 프로퍼티와 생성자 함수 간의 연결이 파괴된 경우에는 <code>instanceof</code>가 <code>false</code>로 평가된다.</p>
<h2 id="1911-직접-상속">19.11 직접 상속</h2>
<h3 id="19111-objectcreate에-의한-직접-상속">19.11.1 Object.create에 의한 직접 상속</h3>
<p><code>Object.create</code> 메서드는 첫 번째 매개변수에 전달한 객체의 프로토타입 체인에 속하는 객체를 생성한다.
즉, 객체를 생성하면서 직접적으로 상속을 구현하는 것이다.</p>
<pre><code class="language-js">// 첫 번째 매개변수에는 생성할 객체의 프로토타입으로 지정할 객체를 전달한다.

let obj = Object.create(null);
// obj의 프로토타입은 null이다.
// Object.prototype을 상속받지 못한다.

let obj = Object.create(Object.prototype);
// obj = {}와 동일하다.


// 두 번째 매개변수로 생성할 객체의 프로퍼티 키와 프로퍼티 디스크립터 객체로 이루어진 객체를 전달한다.(옵션)
let obj = Object.create(Object.prototype, {
  x: { value: 1, writable: true, enumerable: true, configurable: true}
});
// 위 코드는 아래와 동일하다.
// let obj = Object.create(Object.prototype)
// obj.x = 1

let obj = Object.create({x: 1})
// obj의 프로토타입은 {x: 1}이고, {x: 1}의 프로토타입은 Object.prototype이다.
// 프로토타입 체인에 따라서 obj도 Object.prototype를 상속받는다.

function Person() {};
let obj = Object.create(Person.prototype);
// 위 코드는 아래와 동일하다.
// let obj = new Person();</code></pre>
<p>이렇게 <code>Object.create</code>를 통해 직접 상속하는 방식의 장점은 다음과 같다.</p>
<ul>
<li>new 연산자 없이도 객체를 생성할 수 있다.</li>
<li>프로토타입을 지정하면서 객체를 생성할 수 있다.</li>
<li>객체 리터럴에 의해 생성된 객체도 상속받을 수 있다.</li>
</ul>
<p>하지만 맨 위의 예시처럼, <code>Object.prototype</code>을 상속받지 못하는 객체를 생성할 수 있으니 사용을 자제하자.</p>
<h3 id="19112-객체-리터럴-내부에서-__proto__에-의한-직접-상속">19.11.2 객체 리터럴 내부에서 <code>__proto__</code>에 의한 직접 상속</h3>
<p>ES6에서는 객체 리터럴 내부에서 <code>__proto__</code> 접근자 프로퍼티를 사용하여 직접 상속을 구현할 수 있다.</p>
<pre><code class="language-js">const newPrototype = {x: 10};

const obj = {
  y: 10,
  __proto__: newPrototype
};

console.log(obj.x, obj.y); // 10 10</code></pre>
<h2 id="1912-정적-프로퍼티메서드">19.12 정적 프로퍼티/메서드</h2>
<p>정적 프로퍼티/메서드는 생성자 함수로 인스턴스를 생성하지 않아도 참조/호출할 수 있는 프로퍼티/메서드를 말한다.</p>
<pre><code class="language-js">function Person() {};

//정적 메서드
Person.staticMethod = function() {console.log(&quot;static&quot;);};
Person.staticMethod(); // static

const me = new Person();
me.staticMethod(); //TypeError</code></pre>
<p>위 예시처럼, <strong>생성자 함수 객체가 소유한 정적 프로퍼티/메서드는 생성자 함수로 참조/호출</strong>한다.
인스턴스로는 참조/호출이 불가능한데, 이유는 <strong>프로토타입 체인 상에 존재하지 않기 때문</strong>이다.
즉, 상속되지 않는 것이다.</p>
<p>위에서 사용했던 <code>Object.create</code>는 <code>Object</code>의 정적 메서드이므로 <code>Person.create</code>와 같이 사용할 수 없다.</p>
<p>만약 인스턴스/프로토타입 메서드 내에서 <code>this</code>를 사용하지 않는다면 그 메서드는 정적 메서드로 변경할 수 있다.
인스턴스가 호출한 인스턴스/프로토타입 내에서 <code>this</code>는 인스턴스를 가리킨다.
따라서 이 인스턴스를 참조할 필요가 없다면 정적 메서드로 변경하여도 동작한다.</p>
<pre><code class="language-js">function Foo() {};
Foo.prototype.x = function() {console.log(&quot;x&quot;);};
// 메서드 x를 사용하려면 반드시 인스턴스를 생성해야한다.
const foo = new Foo();
foo.x(); // x

function Bar() {};
Bar.x = function() {console.log(&quot;x&quot;)}
// 정적 메서드 x는 인스턴스를 생성하지 않아도 호출할 수 있다.
Bar.x(); // x</code></pre>
<h2 id="1913-프로퍼티-존재-확인">19.13 프로퍼티 존재 확인</h2>
<h3 id="19131-in-연산자">19.13.1 in 연산자</h3>
<p><code>in</code> 연산자는 객체 내에 특정 프로퍼티가 존재하는지 여부를 확인한다.
<code>key in object</code> 형태로 사용한다.</p>
<pre><code class="language-js">const person = {
  name: &quot;Sangjo&quot;
}

console.log(&quot;name&quot; in person) // true
console.log(&quot;age&quot; in person) // false

console.log(&quot;toString&quot; in person) // true

//console.log(Reflect.has(person, &quot;name&quot;)); // true</code></pre>
<p>단, <code>in</code> 연산자는 프로토타입 체인을 따라 모든 프로토타입에서 검색한다.
따라서 <code>Object.prototype</code>에 존재하는 <code>toString</code> 메서드도 검색됨을 확인할 수 있다.</p>
<p><code>in</code> 연산자는 ES6에서 도입된 <code>Reflect.has</code> 메서드와 동일하게 동작한다.</p>
<h3 id="19132-objectprototypehasownproperty-메서드">19.13.2 Object.prototype.hasOwnProperty 메서드</h3>
<pre><code class="language-js">const person = {
  name: &quot;Sangjo&quot;
}

console.log(person.hasOwnProperty(&quot;name&quot;)); // true
console.log(person.hasOwnProperty(&quot;age&quot;)); // false

console.log(person.hasOwnProperty(&quot;toString&quot;)); // false</code></pre>
<p><code>Object.prototype.hasOwnProperty</code> 메서드는 <code>in</code> 연산자와 다르게, 객체 고유의 프로퍼티 키인 경우에만 <code>true</code>를 반환하고, 상속받은 프로토타입의 프로퍼티 키인 경우 <code>false</code>를 반환한다.</p>
<h2 id="1914-프로퍼티-열거">19.14 프로퍼티 열거</h2>
<h3 id="19141-for--in-문">19.14.1 for ... in 문</h3>
<p>객체의 모든 프로퍼티를 순회하며 열거하려면 <code>for ... in</code>문을 사용한다.</p>
<pre><code class="language-js">const person = {
  name: &quot;Sangjo&quot;,
  age: 29
}

for(const key in person) {
  console.log(key)
}
// name
// age</code></pre>
<p><code>for ... in</code> 문은 <code>in</code> 연산자처럼 순회 대상 객체의 프로퍼티뿐만 아니라 <strong>상속받은 프로토타입의 프로퍼티까지 전부 열거한다.</strong>
하지만 위 예제의 경우 <code>toString</code>과 같은 <code>Object.prototype</code>의 프로퍼티가 열거되지 않았다.
왜일까?</p>
<p>바로 <code>toString</code> 메서드의 <code>[[Enumerable]](열거 가능 여부)</code> 값이 <code>false</code>이기 때문이다.</p>
<p>따라서 <code>for ... in</code> 문에 대해 정확히 표현하자면,
<strong>객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중에서 프로퍼티 어트리뷰트 <code>[[Enumerable]]</code>의 값이 <code>true</code>인 프로퍼티를 순회하며 열거한다.</strong></p>
<p><code>for ... in</code> 문은 프로퍼티를 열거할 때 순서를 보장하지 않는다.
하지만 대부분의 브라우저는 순서를 보장한다.</p>
<h3 id="19142-objectkeysvaluesentries-메서드">19.14.2 Object.keys/values/entries 메서드</h3>
<p><code>for ... in</code> 문은 상속받은 프로토타입의 프로퍼티까지 전부 열거한다.
따라서 객체 자신의 프로퍼티만 열거하기 위해서는 <code>for ... in</code> 문 내부에 <code>Object.prototype.hasOwnProperty</code> 메서드를 사용하는 추가 작업이 필요하다.</p>
<p>객체 자신의 고유 프로퍼티만 열거하기 위해서는 <code>Object.keys/values/entries</code> 메서드를 사용하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 18. 함수와 일급 객체]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-18.-%ED%95%A8%EC%88%98%EC%99%80-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-18.-%ED%95%A8%EC%88%98%EC%99%80-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Tue, 18 Oct 2022 17:33:19 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="18-함수와-일급-객체">18. 함수와 일급 객체</h1>
<h2 id="181-일급-객체">18.1 일급 객체</h2>
<blockquote>
<p>💡 <strong>일급 객체란?</strong></p>
</blockquote>
<ul>
<li>무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.</li>
<li>변수나 자료구조(객체, 배열 등)에 저장할 수 있다.</li>
<li>함수의 매개변수에 전달할 수 있다.</li>
<li>함수의 반환값으로 사용할 수 있다.</li>
</ul>
<p>함수는 위 조건을 모두 만족하는 <strong>일급 객체</strong>다. 객체는 값이므로 <strong>함수는 값과 동일하게 취급</strong>할 수 있다.
변수 할당문, 객체의 프로퍼티 값, 배열의 요소, 함수 호출의 인수, 함수 반환문 등 <strong>값을 사용하는 곳이라면 어디서든지 리터럴로 정의할 수 있으며 런타임에 함수 객체로 평가된다.</strong></p>
<p>함수를 함수의 <strong>매개변수</strong>에 전달할 수 있으며 함수의 <strong>반환값</strong>으로 사용할 수 있다는 점은 <strong>함수형 프로그래밍</strong>을 가능케 하는 자바스크립트의 장점 중 하나이다.</p>
<h2 id="182-함수-객체의-프로퍼티">18.2 함수 객체의 프로퍼티</h2>
<p>함수를 선언하고 <code>console.dir</code>을 통해 어떤 프로퍼티를 가지는지 살펴보면 아래와 같은 프로퍼티를 가지고 있음을 알 수 있다.</p>
<ul>
<li><code>arguments</code></li>
<li><code>caller</code></li>
<li><code>length</code></li>
<li><code>name</code></li>
<li><code>__proto__</code></li>
<li><code>prototype</code></li>
</ul>
<p>이 프로퍼티들은 일반 객체에는 없는 <strong>함수 객체 고유의 프로퍼티</strong>다.</p>
<h3 id="1821-arguments-프로퍼티">18.2.1 arguments 프로퍼티</h3>
<blockquote>
<p>💡 함수 객체의 <code>arguments</code> 프로퍼티 값은 <code>arguments</code> 객체다.
<code>arguments</code> 객체는 함수 호출 시 전달된 인수들의 정보를 담고 있는 순회 가능한 유사 배열 객체다.
함수 내부에서 지역 변수처럼 사용된다. 즉, 함수 외부에서는 참조할 수 없다.</p>
</blockquote>
<p>함수 객체의 arguments 프로퍼티는 ES3부터 표준에서 폐지되었다.
따라서 <code>Function.arguments</code>와 같은 사용법은 권장되지 않으며, <code>arguments</code> 객체를 참조하도록 한다.</p>
<p>자바스크립트는 함수의 매개변수와 인수의 개수가 일치하는지 확인하지 않는다.
매개변수의 개수보다 인수가 많든 적든 에러가 발생하지 않는다.</p>
<pre><code class="language-js">function add(a, b) {
  console.log(arguments);
  return a + b;
}

console.log(add()); // NaN
console.log(add(1)); // NaN
console.log(add(1, 2)); // 3
console.log(add(1, 2, 3)); // 3</code></pre>
<p>함수의 매개변수는 함수 몸체 내부에서 변수와 동일하게 취급된다.
즉, 함수가 호출되면 변수처럼 매개변수 역시 선언 및 <code>undefined</code>로 초기화 과정을 거치고 인수가 할당된다.
만약 매개변수의 개수보다 인수가 적을 경우, <strong>인수가 전달되지 않은 매개변수는 초기화된 상태를 유지</strong>한다.
만약 매개변수의 개수보다 인수가 많을 경우, <strong>초과된 인수는 무시</strong>된다.</p>
<p>하지만, 초과된 인수가 그냥 버려지는 것은 아니고 모든 인수는 암묵적으로 <code>arguments</code> 객체의 프로퍼티로 보관된다.
위 예시 중에서 마지막 <code>console.log</code>로 출력된 결과다.
<img src="https://velog.velcdn.com/images/sjoleee_/post/b516c6d0-327a-4540-ace2-84b9103b196c/image.png" alt=""></p>
<p>이처럼 <code>arguments</code> 객체는 <strong>인수를 프로퍼티 값으로 소유</strong>하며, <strong>프로퍼티 키는 인수의 순서</strong>를 나타낸다.
<code>callee</code> 프로퍼티는 <code>arguments</code> 객체를 생성한 함수, 즉 <strong>함수 자신</strong>을 가리킨다.
<code>length</code> 프로퍼티는 <strong>인수의 개수</strong>를 나타낸다.
<code>Symbol(Symbol.iterator)</code> 프로퍼티는 <code>arguments</code> 객체를 순회 가능한 자료구조인 <strong>이터러블</strong>로 만든다.</p>
<p><code>arguments</code> 객체를 통해 <strong>인수 개수에 따라 다른 동작을 하는 함수</strong>를 만들 수 있으며, 매개변수 개수를 확정할 수 없는 <strong>가변 인자 함수</strong>를 구현할 때 유용하다.</p>
<pre><code class="language-js">function sum() {
  let res = 0;

  for (let i = 0; i &lt; arguments.length; i++) {
    res += arguments[i]
  }

  return res;
}

console.log(sum()); // 0
console.log(sum(1, 2, 3)); // 6
console.log(sum(100, 101, 102)); // 303</code></pre>
<p><strong>유사 배열 객체는 배열이 아니다.</strong>
따라서 배열 메서드를 사용하려면 <code>Function.prototype.call</code> <code>Function.prototype.apply</code>를 사용해 간접 호출해야 하는 번거로움이 있다.</p>
<p>이를 해결하기 위해 ES6에서는 <strong>Rest 파라미터</strong>를 도입했다.</p>
<pre><code class="language-js">function sum1() {
  // arguments 객체를 배열로 변환
  const arr = Array.prototype.slice.call(arguments);
  return arr.reduce((acc, cur) =&gt; acc + cur, 0)
}

function sum2(...args) {
  return args.reduce((acc, cur) =&gt; acc + cur, 0)
}

// 둘은 동일한 기능을 한다.</code></pre>
<p>ES6를 사용할 수 있다면 Rest 파라미터를 사용하도록 하자.</p>
<h3 id="1822-caller-프로퍼티">18.2.2 caller 프로퍼티</h3>
<blockquote>
<p>💡 함수 객체의 <code>caller</code> 프로퍼티는 함수 자신을 호출한 함수를 가리킨다.</p>
</blockquote>
<p>caller 프로퍼티는 ECMAScript 사양에 포함되지 않은 비표준 프로퍼티다.
참고로만 알아두자.</p>
<pre><code class="language-js">function foo(func) {
  return func();
}

function bar() {
  return bar.caller;
}

console.log(foo(bar)) // function foo(func) {...}
// bar.caller는 자신(bar)을 호출한 foo 함수를 가리킨다.

console.log(bar()) // null
// bar를 호출한 함수가 없음</code></pre>
<h3 id="1823-length-프로퍼티">18.2.3 length 프로퍼티</h3>
<blockquote>
<p>💡 함수 객체의 <code>length</code> 프로퍼티는 함수를 정의할 때 선언한 매개변수의 개수를 가리킨다.</p>
</blockquote>
<pre><code class="language-js">function foo() {}
console.log(foo.length); // 0

function bar(a, b ,c) {}
console.log(bar.length); // 3</code></pre>
<p>헷갈림 주의!
함수 객체의 <code>length</code> 프로퍼티는 <strong>함수를 정의할 때 선언한 매개변수의 개수</strong>
함수 객체의 <code>arguments</code> 프로퍼티의 값인 <strong><code>arguments</code> 객체의 <code>length</code> 프로퍼티는 인자의 개수</strong></p>
<h3 id="1824-name-프로퍼티">18.2.4 name 프로퍼티</h3>
<blockquote>
<p>💡 함수 객체의 <code>name</code> 프로퍼티는 함수 이름을 나타낸다.
익명 함수표현식의 경우, 함수 객체를 가리키는 식별자를 값으로 갖는다.</p>
</blockquote>
<pre><code class="language-js">// 함수 선언문
function foo() {}
console.log(foo.name); // foo

// 기명 함수 표현식
const bar = function func() {}
console.log(bar.name); // func

// 익명 함수 표현식
const baz = function() {}
console.log(baz.name); // baz</code></pre>
<h3 id="1825-proto-접근자-프로퍼티">18.2.5 <strong>proto</strong> 접근자 프로퍼티</h3>
<blockquote>
<p>💡 함수 객체의 <code>__proto__</code> 접근자 프로퍼티는 [[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티다.</p>
</blockquote>
<p>부모에게 상속받은 부모의 유전자를 담고 있다.</p>
<h3 id="1826-prototype-프로퍼티">18.2.6 prototype 프로퍼티</h3>
<blockquote>
<p>💡 함수 객체의 <code>prototype</code> 프로퍼티는 생성자 함수로 호출할 수 있는 객체, 즉 <code>constructor</code>만이 소유하는 프로퍼티다.</p>
</blockquote>
<p>자식에게 물려줄 유전자를 담고 있다.
즉, 생성자 함수로 호출될 때 생성자 함수가 생성할 <strong>인스턴스의 프로토타입 객체</strong>를 가리킨다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[class와 생성자 함수의 차이]]></title>
            <link>https://velog.io/@sjoleee_/class%EC%99%80-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B0%A8%EC%9D%B4-nfhwxkux</link>
            <guid>https://velog.io/@sjoleee_/class%EC%99%80-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B0%A8%EC%9D%B4-nfhwxkux</guid>
            <pubDate>Tue, 18 Oct 2022 12:48:18 GMT</pubDate>
            <description><![CDATA[<p><code>생성자 함수</code>와 <code>class</code>는 <strong>객체를 찍어내는 공장</strong>같은 역할을 한다는 점에서 비슷하다.
똑같지는 않은데, 생성자 함수에 비해 <code>class</code>가 갖는 다른 점을 정리해보았다. </p>
<h2 id="1-new-연산자-없이-호출이-불가능함">1. new 연산자 없이 호출이 불가능함</h2>
<pre><code class="language-js">// class 생성
class exampleClass {
  constructor(){}
}

// 생성자 함수 생성
function exampleFunc(){}


// 호출
exampleClass(); // Error
exampleFunc();</code></pre>
<h2 id="2-메서드-열거-불가능-non-enumerable">2. 메서드 열거 불가능 (non-enumerable)</h2>
<p>class가 가지는 모든 메서드는 프로퍼티 어트리뷰트 <code>[[Enumerable]]</code>이 <code>false</code>이다.
즉, <code>for...in</code>으로 객체를 순회할 때, 메서드가 대상에서 제외된다.</p>
<pre><code class="language-js">class example{

  constructor(first, second){
    this.first = first;
    this.second = second;
  }

  test(){}
}

const exampleClass = new example(1, 2)

for(let key in exampleClass){
  console.log(key) // first, second만 출력
}
</code></pre>
<h2 id="3-엄격모드-적용">3. 엄격모드 적용</h2>
<p>클래스 생성자 안 코드 전체에는 자동으로 엄격모드가 적용된다.(use strict)</p>
<h2 id="4-상속을-위한-키워드-extends와-super">4. 상속을 위한 키워드 extends와 super</h2>
<pre><code class="language-js">class parent{
  constructor(name){
    this.name = name;
  }

  hi(){
    console.log(&quot;Hi&quot;); // Hi
  }
}

class child extends parent{
  constructor(name){
    super(name);
  }

  hello(){
    console.log(&quot;Hello&quot;); // Hello
    super.hi(); // Hi
  }
}</code></pre>
<h2 id="5-정적-메서드를-위한-키워드-static">5. 정적 메서드를 위한 키워드 static</h2>
<p>정적 메서드는 <strong>클래스 이름으로 호출하기 때문에 클래스의 인스턴스를 생성하지 않아도 사용할 수 있다.</strong> 
단, 정적 메서드는 <code>this</code>를 사용할 수 없으며, 인스턴스로 호출할 수 없다.</p>
<p>정적 메서드는 <code>Math</code> 객체의 메서드처럼 애플리케이션 전역에서 사용할 유틸리티(utility) 함수를 생성할 때 주로 사용한다.</p>
<pre><code class="language-js">class example{
  constructor(name){
    this.name = name;
  }

  static hi(){
    console.log(&quot;Hi&quot;);
  }
}

example.hi(); // Hi

const exampleClass = new example(&quot;sangjo&quot;)
exampleClass.hi(); // Error</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 17. 생성자 함수에 의한 객체 생성]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-17.-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EC%9D%98%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-17.-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EC%9D%98%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Tue, 18 Oct 2022 12:32:28 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="17-생성자-함수에-의한-객체-생성">17. 생성자 함수에 의한 객체 생성</h1>
<p>객체 리터럴에 의한 객체 생성 방식은 가장 일반적이고 간단한 객체 생성 방식이다.
객체는 객체 리터럴 이외에도 다양한 방법으로 생성할 수 있는데, 생성자 함수를 사용하여 객체를 생성하는 방식을 살펴보자.</p>
<h2 id="171-object-생성자-함수">17.1 Object 생성자 함수</h2>
<blockquote>
<p>💡 new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환한다.
이후 프로퍼티 또는 메서드를 추가하여 객체를 완성할 수 있다.</p>
</blockquote>
<pre><code class="language-js">const person = new Object();
person.name = &quot;sangjo&quot;;

console.log(person); // { name: &quot;sangjo&quot; }</code></pre>
<p>생성자 함수란 <code>new</code> 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수를 말한다.
생성자 함수에 의해 생성된 객체를 <strong>인스턴스</strong>라 한다.</p>
<h2 id="172-생성자-함수">17.2 생성자 함수</h2>
<h3 id="1721-객체-리터럴에-의한-객체-생성-방식의-문제점">17.2.1 객체 리터럴에 의한 객체 생성 방식의 문제점</h3>
<p>객체 리터럴에 의한 객체 생성 방식은 직관적이고 간편하지만 하나의 객체만 생성한다.
동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야 하기 때문에 비효율적이다.</p>
<pre><code class="language-js">const person1 = {
  name: &quot;sangjo1&quot;,
  sayHi(){ console.log(`Hi ${this.name}`); }
}

const person2 = {
  name: &quot;sangjo2&quot;,
  sayHi(){ console.log(`Hi ${this.name}`); }
}

const person1 = {
  name: &quot;sangjo3&quot;,
  sayHi(){ console.log(`Hi ${this.name}`); }
}</code></pre>
<p>이처럼 객체 리터럴에 의해 객체를 생성하는 경우 프로퍼티 구조가 동일함에도 불구하고 매번 같은 프로퍼티와 메서드를 기술해야 한다.</p>
<h3 id="1722-생성자-함수에-의한-객체-생성-방식의-장점">17.2.2 생성자 함수에 의한 객체 생성 방식의 장점</h3>
<p>생성자 함수에 의한 객체 생성 방식은 마치 <strong>인스턴스를 생성하기 위한 템플릿처럼 활용</strong>할 수 있다는 장점이 있다.</p>
<pre><code class="language-js">function Person(name) {
  this.name = name;
  this.sayHi = function(){
    console.log(`Hi ${this.name}`);
  }
}

const person1 = new Person(&quot;sangjo1&quot;);
const person2 = new Person(&quot;sangjo2&quot;);
const person3 = new Person(&quot;sangjo3&quot;);

const person4 = Person(&quot;sangjo4&quot;)
console.log(person4); // undefined
// new 연산자 없이 일반 함수로 호출될 경우, 반환문이 없으므로 암묵적으로 undefined를 반환한다.
console.log(name); // sangjo4
// 일반 함수로 호출된 Person의 this는 전역 객체를 가리킨다.</code></pre>
<p>생성자 함수는 객체(인스턴스)를 생성하는 함수다. 
하지만 일반 함수와 동일한 방법으로 정의하고, <code>new</code> 연산자와 함께 호출하면 생성자 함수로 동작하게 된다.
<code>new</code> 연산자와 함께 호출하지 않으면 일반 함수로 동작한다.</p>
<h3 id="1723-생성자-함수의-인스턴스-생성-과정">17.2.3 생성자 함수의 인스턴스 생성 과정</h3>
<p>new 연산자와 함께 생성자 함수를 호출하면 자바스크립트 엔진은 다음과 같은 과정을 거친다.</p>
<p><strong>1. 인스턴스 생성과 this 바인딩</strong></p>
<pre><code class="language-js">function Person(str) {
  // 1. 인스턴스(빈 객체) 생성, this에 바인딩
  this.name = str;
}</code></pre>
<p>암묵적으로 <strong>빈 객체</strong>가 생성된다.
이 객체가 바로 <strong>생성자 함수가 생성한 인스턴스</strong>다.
그리고 이 인스턴스는 <code>this</code>에 바인딩된다.
생성자 함수 내부의 <code>this</code>가 생성할 인스턴스를 가리키는 이유가 바로 이것이다.
이 처리는 함수 몸체의 코드가 실행되는 런타임 이전에 실행된다.</p>
<p><strong>2. 인스턴스 초기화</strong></p>
<pre><code class="language-js">function Person(str) {
  // 1. 인스턴스(빈 객체) 생성, this에 바인딩

  // 2. 생성자 함수에 기술되어있는 코드 실행. 인스턴스 초기화.
  this.name = str;
}</code></pre>
<p>생성자 함수의 코드가 실행되어 <code>this</code>에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 인수로 전달받은 값을 프로퍼티에 할당하여 초기화하거나 고정값을 할당한다.</p>
<p><strong>3. 인스턴스 반환</strong></p>
<pre><code class="language-js">function Person(str) {
  // 1. 인스턴스(빈 객체) 생성, this에 바인딩

  // 2. 생성자 함수에 기술되어있는 코드 실행. 인스턴스 초기화.
  this.name = str;

  // 3. 완성된 인스턴스가 바인딩된 this를 암묵적으로 반환한다.
}</code></pre>
<p>생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 <code>this가</code> 암묵적으로 반환된다.
만약, <code>return문</code>을 통해 반환 값을 명시하면 다른 결과를 얻을 수 있으니 <code>return</code>을 꼭 생략하자.</p>
<pre><code class="language-js">function Person1(str) {
  this.name = str;

  // 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시된다.
  return {};
}

function Person2(str) {
  this.name = str;

  // 명시적으로 원시 값을 반환하면 무시되며 암묵적인 this 반환이 이루어진다.
  return 1;
}

const person1 = new Person1(&quot;sangjo&quot;);
console.log(person1); // {}

const person1 = new Person2(&quot;sangjo&quot;);
console.log(person2); // Person2{ name: &quot;sangjo&quot; }</code></pre>
<h3 id="1724-내부-메서드-call과-construct">17.2.4 내부 메서드 [[Call]]과 [[Construct]]</h3>
<p>함수는 객체이므로 일반 객체와 동일하게 내부 슬롯와 내부 메서드를 가지고 있다.
그러나 일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.
<em>(함수 선언문 또는 함수 표현식으로 정의한 함수는 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있다.)</em></p>
<p>따라서 함수 객체는 함수로서 동작하기 위해 <code>[[Environment]]</code> <code>[[FormalParameters]]</code> 등의 내부 슬롯과 <code>[[Call]]</code> <code>[[Construct]]</code> 같은 내부 메서드를 추가로 갖고 있다.</p>
<p>내부 메서드 <code>[[Call]]</code>을 갖는 함수 객체를 <code>callable</code>이라 하며, 내부 메서드 <code>[[Construct]]</code>를 갖는 함수 객체를 <code>constructor</code>, 갖지 않는 함수 객체를 <code>non-constructor</code>이라 부른다.</p>
<p>호출할 수 없는 객체는 함수 객체가 아니므로 모든 함수 객체는 반드시 <code>callable</code>이어야 한다.
하지만 모든 함수 객체가 <code>constructor</code>는 아니다.</p>
<h3 id="1725-constructor와-non-constructor의-구분">17.2.5 constructor와 non-constructor의 구분</h3>
<blockquote>
<ul>
<li>constructor: 함수 선언문, 함수 표현식, 클래스</li>
</ul>
</blockquote>
<ul>
<li>non-constructor: 메서드(ES6 메서드 축약 표현), 화살표 함수</li>
</ul>
<p><code>non-constructor</code>를 생성자 함수로서 호출하면 에러가 발생한다.</p>
<h3 id="1726-new-연산자">17.2.6 new 연산자</h3>
<p>일반 함수와 생성자 함수의 형식에 차이는 없다.
<code>new</code> 연산자와 함께 사용하면 생성자 함수로 동작하는 것이다.</p>
<pre><code class="language-js">// 일반 함수
function add(a, b) {
  return a + b;
}

const result = new add(1, 2);
console.log(result); // {}
// 생성자 함수로서 정의하지 않은 일반 함수를 new 연산자와 함께 호출해도 생성자 함수로 동작한다.
// 함수의 반환값이 객체가 아니므로 this에 바인딩된 인스턴스를 반환한다. 이 경우는 빈 객체.</code></pre>
<p>생성자 함수로서 정의한 함수 객체를 <code>new</code> 연산자 없이 호출하면 일반 함수로 호출된다.</p>
<pre><code class="language-js">// 생성자 함수
function Person(str) {
  this.name = str;
}

const result = Person(&quot;sangjo&quot;);
console.log(result); // undefined
// 함수 내부에서 반환값을 설정해주지 않았기 때문에 undefined가 출력된다.

console.log(name); // sangjo
// 일반 함수로서 호출하면 함수 내부의 this는 window를 가리키게 되므로 name 프로퍼티는 전역 객체의 프로퍼티가 된다.</code></pre>
<p>일반 함수는 카멜 케이스, 생성자 함수는 파스칼 케이스를 사용하여 구별하자.</p>
<h3 id="1727-newtarget">17.2.7 new.target</h3>
<p>식별자 명명 컨벤션을 통해 구분하려고 해도 실수는 언제나 발생할 수 있다.
함수 내부에서 new.target을 사용하면 생성자 함수로서 호출되었는지 확인할 수 있다.
<code>new.target</code>은 <code>this</code>와 유사하게 <code>constructor</code>인 모든 함수 내부에서 암묵적인 지역 변수와 같이 사용되며 <strong>메타 프로퍼티</strong>라고 부른다.</p>
<p><strong>생성자 함수</strong>로서 호출되면 함수 내부의 <code>new.target</code>은 <strong>함수 자신</strong>을 가리킨다.
<strong>일반 함수</strong>로서 호출되면 함수 내부의 <code>new.target</code>은 <strong><code>undefined</code></strong>이다.`</p>
<pre><code class="language-js">// 생성자 함수
function Person(str) {
  // 생성자 함수가 일반 함수로 호출될 때
  if(!new.target){
    return new Person(str); // new 연산자와 함께 생성자 함수를 재귀 호출한다.
  }

  this.name = str;
}

const result = Person(&quot;sangjo&quot;);
console.log(result); // Person{ name: &quot;sangjo&quot; }
// 일반 함수로서 호출해도 내부 로직에 따라 생성자 함수로 호출된다.</code></pre>
<p><code>Object</code>와 <code>Function</code> 생성자 함수는 <code>new</code> 연산자 없이 호출해도 <code>new</code> 연산자와 함께 호출했을 때와 동일하게 동작한다.
<code>String</code> <code>Number</code> <code>Boolean</code> 생성자 함수는 <code>new</code> 연산자와 함께 호출하면 String, Number, Boolean 객체를 생성하여 반환하지만, <code>new</code> 연산자 없이 호출하면 문자열, 숫자, 불리언 값을 반환한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 16. 프로퍼티 어트리뷰트]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-16.-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%96%B4%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B8</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-16.-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%96%B4%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B8</guid>
            <pubDate>Sun, 16 Oct 2022 12:57:22 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="16-프로퍼티-어트리뷰트">16. 프로퍼티 어트리뷰트</h1>
<h2 id="161-내부-슬롯과-내부-메서드">16.1 내부 슬롯과 내부 메서드</h2>
<blockquote>
<p>💡 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드.</p>
</blockquote>
<p>실제로 동작하지만, 개발자가 직접 접근할 수 있도록 외부로 공개된 객체의 프로퍼티는 아니다.
원칙적으로 자바스크립트는 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않는다.
단, 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공하기는 한다.</p>
<p>모든 객체는 <code>[[Prototype]]</code>이라는 내부 슬롯을 갖는다.
내부 슬롯은 원칙적으로 직접 접근할 수 없으나, <code>[[Prototype]]</code>의 경우, <code>__proto__</code>를 통해 간접적으로 접근할 수 있다.</p>
<pre><code class="language-js">const obj = {};

obj.[[Prototype]] // Error
obj.__proto__ //Object.prototype</code></pre>
<h2 id="162-프로퍼티-어트리뷰트와-프로퍼티-디스크립터-객체">16.2 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체</h2>
<blockquote>
<p>💡 프로퍼티 어트리뷰트란, 자바스크립트 엔진이 관리하는 내부 상태 값인 내부 슬롯 <code>[[Value]]</code> <code>[[Writable]]</code> <code>[[Enumerable]]</code> <code>[[Configurable]]</code>를 말한다.
프로퍼티를 생성할 때 이러한 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다.</p>
</blockquote>
<p>위에서 말했듯, <code>내부 슬롯</code>은 직접 접근할 수 없으나 간접적으로 <code>Object.getOwnPropertyDescriptor</code>를 사용하여 확인할 수 있다.</p>
<p><code>Object.getOwnPropertyDescriptor</code>는 두개의 매개변수를 받는다.
<code>Object.getOwnPropertyDescriptor(객체의 참조, 확인할 프로퍼티 키)</code></p>
<p>만약 하나의 프로퍼티 키가 아닌 모든 프로퍼티 키에 대해 프로퍼티 어트리뷰트 정보를 받으려면 <code>Object.getOwnPropertyDescriptors(객체의 참조)</code>를 사용하면 된다.</p>
<h2 id="163-데이터-프로퍼티와-접근자-프로퍼티">16.3 데이터 프로퍼티와 접근자 프로퍼티</h2>
<blockquote>
<p>💡 <strong>데이터 프로퍼티</strong> : 키와 값으로 구성된 일반적인 프로퍼티
<strong>접근자 프로퍼티</strong> : 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수로 구성된 프로퍼티.(<code>get</code>, <code>set</code>)</p>
</blockquote>
<h3 id="1631-데이터-프로퍼티">16.3.1 데이터 프로퍼티</h3>
<p>데이터 프로퍼티는 다음과 같은 프로퍼티 어트리뷰트를 갖는다.</p>
<ul>
<li><code>[[Value]]</code><ul>
<li>프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값이다.</li>
<li>프로퍼티 키를 통해 프로퍼티 값을 변경하면 <code>[[Value]]</code>에 값을 재할당한다.</li>
<li>이때 프로퍼티가 없으면 프로퍼티를 동적 생성하고 생성된 프로퍼티의 <code>[[Value]]</code>에 값을 할당한다.</li>
</ul>
</li>
<li><code>[[Writable]]</code><ul>
<li>프로퍼티 값의 변경 가능 여부를 나타내며 <code>boolean</code>값을 갖는다.</li>
</ul>
</li>
<li><code>[[Enumerable]]</code><ul>
<li>프로퍼티의 열거 가능 여부를 나타내며 <code>boolean</code>값을 갖는다.</li>
<li><code>[[Enumerable]]</code>이 <code>false</code>인 경우, 해당 프로퍼티는 <code>for...in</code> <code>Object.keys</code> 등으로 열거할 수 없다.</li>
</ul>
</li>
<li><code>[[Configurable]]</code><ul>
<li>프로퍼티 재정의 가능 여부를 나타내며 <code>boolean</code>값을 갖는다.</li>
<li><code>[[Configurable]]</code>이 <code>false</code>인 경우, 해당 프로퍼티의 삭제, 프로퍼티 어트리뷰트 값의 변경이 금지된다.</li>
</ul>
</li>
</ul>
<h3 id="1632-접근자-프로퍼티">16.3.2 접근자 프로퍼티</h3>
<p>접근자 프로퍼티는 다음과 같은 프로퍼티 어트리뷰트를 갖는다.</p>
<ul>
<li><code>[[Get]]</code><ul>
<li>접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수다</li>
<li>접근자 프로퍼티 키로 프로퍼티 값에 접근하면 프로퍼티 어트리뷰트 <code>[[Get]]</code>의 값, 즉 <code>getter</code>함수가 호출되고 그 결과가 프로퍼티 값으로 반환된다.</li>
</ul>
</li>
<li><code>[[Set]]</code><ul>
<li>접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수다</li>
<li>접근자 프로퍼티 키로 프로퍼티 값을 저장하면 프로퍼티 어트리뷰트 <code>[[Set]]</code>의 값, 즉 <code>Setter</code>함수가 호출되고 그 결과가 프로퍼티 값으로 저장된다.</li>
<li><code>[[Enumerable]]</code></li>
<li>프로퍼티의 열거 가능 여부를 나타내며 <code>boolean</code>값을 갖는다.</li>
<li><code>[[Enumerable]]</code>이 <code>false</code>인 경우, 해당 프로퍼티는 <code>for...in</code> <code>Object.keys</code> 등으로 열거할 수 없다.</li>
</ul>
</li>
<li><code>[[Configurable]]</code><ul>
<li>프로퍼티 재정의 가능 여부를 나타내며 <code>boolean</code>값을 갖는다.</li>
<li><code>[[Configurable]]</code>이 <code>false</code>인 경우, 해당 프로퍼티의 삭제, 프로퍼티 어트리뷰트 값의 변경이 금지된다.</li>
</ul>
</li>
</ul>
<pre><code class="language-js">const person = {
  firstName: &quot;Sangjo&quot;,
  lastName: &quot;Lee&quot;,

  get fullName(){
    return `${this.firstName} ${this.lastName}`;
  }

  set fullName(name){
    [this.firstName, this.lastName] = name.split(&quot; &quot;);
  }
}</code></pre>
<p>위 예시에서<code>get</code> <code>set</code>이 붙은 <code>fullName</code>이 접근자 프로퍼티이다.
접근자 프로퍼티는 <code>[[Value]]</code>를 갖지 않는다.</p>
<h2 id="164-프로퍼티-정의">16.4 프로퍼티 정의</h2>
<blockquote>
<p>💡 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것</p>
</blockquote>
<p><code>Object.defineProperty</code> 메서드를 사용하여 프로퍼티의 어트리뷰트를 정의할 수 있다.
인수로 <code>객체의 참조</code>, <code>데이터 프로퍼티의 키인 문자열</code>, <code>프로퍼티 디스크립터 객체</code>를 전달한다.</p>
<pre><code class="language-js">const person = {};

// 데이터 프로퍼티 정의
Object.defineProperty(person, &#39;name&#39;, {
  value: &quot;sangjo&quot;,
  writable: true,
  enumerable: true,
  configurable: true
})

// 접근자 프로퍼티 정의
Object.defineProperty(person, &#39;yourName&#39;, {

  get(){
    return `${this.name}`
  }

  set(newName){
    this.name = newName;
  }

  enumerable: true,
  configurable: true
})</code></pre>
<p><code>Object.defineProperty</code> 메서드는 하나의 프로퍼티만 정의할 수 있는데, 여러 프로퍼티를 정의하려면 <code>Object.definePropertys</code> 메서드를 사용하면 된다.</p>
<h2 id="165-객체-변경-방지">16.5 객체 변경 방지</h2>
<p>객체는 변경 가능한 값이므로 재할당 없이 직접 변경할 수 있다.</p>
<h3 id="1651-객체-확장-금지">16.5.1 객체 확장 금지</h3>
<blockquote>
<p>💡 <code>Object.perventExtensions</code> 메서드는 객체의 확장을 금지한다.
확장이 금지된 객체는 프로퍼티 추가가 금지된다.
<code>Object.isExtensible</code> 메서드로 확인할 수 있다.</p>
</blockquote>
<h3 id="1652-객체-밀봉">16.5.2 객체 밀봉</h3>
<blockquote>
<p>💡 <code>Object.seal</code> 메서드는 객체를 밀봉한다.
밀봉된 객체는 프로퍼티 추가 및 삭제, 프로퍼티 어트리뷰트 재정의가 금지된다.
즉, <strong>읽기와 쓰기만 가능</strong>하다.
<code>Object.isSealed</code> 메서드로 확인할 수 있다.</p>
</blockquote>
<h3 id="1653-객체-동결">16.5.3 객체 동결</h3>
<blockquote>
<p>💡 <code>Object.freeze</code> 메서드는 객체를 동결한다.
동결된 객체는 <strong>읽기만 가능</strong>하다.
<code>Object.isFrozen</code> 메서드로 확인할 수 있다.</p>
</blockquote>
<h3 id="1654-불변-객체">16.5.4 불변 객체</h3>
<blockquote>
<p>💡 위 변경 방지 메서드들은 얕은 병경 방지로, 직속 프로퍼티만 변경이 방지되고 중첩 객체까지 영향을 주지는 못한다.
<code>Object.freeze</code> 메서드를 모든 프로퍼티에 대해 재귀적으로 호출하여 읽기 전용의 불변 객체를 구현할 수 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 15. let, const 키워드와 블록 레벨 스코프]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-15.-let-const</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-15.-let-const</guid>
            <pubDate>Sun, 16 Oct 2022 11:55:01 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="15-let-const-키워드와-블록-레벨-스코프">15. let, const 키워드와 블록 레벨 스코프</h1>
<h2 id="151-var-키워드로-선언한-변수의-문제점">15.1 var 키워드로 선언한 변수의 문제점</h2>
<h3 id="1511-변수-중복-선언-허용">15.1.1 변수 중복 선언 허용</h3>
<p><code>var</code> 키워드로 선언한 변수는 <strong>중복 선언이 가능</strong>하다.</p>
<pre><code class="language-js">var x = 1;
var y = 1;

// 중복 선언 허용. 초기화문이 있는 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작
var x = 100;
var y; // 초기화문이 없는 변수 선언문은 무시된다.

console.log(x); // 100
console.log(y); // 1</code></pre>
<h3 id="1512-함수-레벨-스코프">15.1.2 함수 레벨 스코프</h3>
<p><code>var</code> 키워드로 선언한 변수는 <strong>함수 레벨 스코프</strong>를 따른다.
따라서 함수 외부에서 선언했다면 <code>for문</code>이나 <code>if문</code> 등의 코드 블록 내에서 선언해도 전역 변수가 된다.
이는 의도치 않게 변수의 값이 변경되는 부작용을 발생시킨다.</p>
<pre><code class="language-js">var x = 1;

if(true) {
  var x = 10;
}

console.log(x); // 10</code></pre>
<h3 id="1513-변수-호이스팅">15.1.3 변수 호이스팅</h3>
<p><code>var</code> 키워드로 선언한 변수는 변수 호이스팅에 의해 <strong>변수 선언문 이전에 참조</strong>할 수 있다.
단, 할당문 이전에 참조하면 <code>undefined</code>를 반환한다.</p>
<p>프로그램의 흐름상 맞지 않으며 가독성을 떨어뜨리고 오류를 발생시킬 여지를 남긴다.</p>
<h2 id="152-let-키워드">15.2 let 키워드</h2>
<h3 id="1521-변수-중복-선언-금지">15.2.1 변수 중복 선언 금지</h3>
<p><code>let</code> 키워드로 이름이 동일한 변수를 <strong>중복 선언하면 문법 에러가 발생</strong>한다.</p>
<h3 id="1522-블록-레벨-스코프">15.2.2 블록 레벨 스코프</h3>
<p><code>let</code> 키워드로 선언한 변수는 모든 코드 블록(<code>함수</code> <code>if문</code> <code>for문</code> <code>while문</code> <code>try/catch문</code> 등)을 지역스코프로 인정하는 <strong>블록 레벨 스코프</strong>를 따른다.</p>
<h3 id="1523-변수-호이스팅">15.2.3 변수 호이스팅</h3>
<p><code>let</code> 키워드로 선언한 변수는 <strong>변수 호이스팅이 발생하지 않는 것처럼 동작</strong>한다.
<code>let</code> 키워드는 <strong>변수 선언</strong>과 <strong>초기화</strong>를 나누어 실행하며, 자바스크립트 엔진에 의해 <strong>암묵적으로 선언 단계가 먼저 실행</strong>되지만 <strong>초기화 단계는 변수 선언문에 도달했을 때 실행</strong>된다.
만약 초기화 단계가 실행되기 이전에 변수에 접근하려고 하면 참조 에러가 발생한다.</p>
<p>스코프의 시작 지점부터 초기화 단계 시작 지점(변수 선언문)까지 변수를 참조할 수 없는 구간을 <strong>일시적 사각지대(TDZ)</strong>라고 부른다.</p>
<p>반면 <code>var</code> 키워드는 <strong>선언과 초기화가 한번에 진행</strong>되어 <strong>선언문 이전에 변수를 참조할 수 있게 된다.</strong></p>
<p>단, <code>let</code> 키워드로 선언한 변수는 변수 호이스팅이 <strong>발생하지 않는 것처럼 동작</strong>하는 것임을 명심하자.</p>
<pre><code class="language-js">let foo = 1; //전역 변수

{
  console.log(foo); // ReferenceError
  let foo = 2; //지역 변수
}</code></pre>
<p>위 예시에서, 만약 호이스팅이 발생하지 않는다면 <code>console.log(foo)</code>는 1을 출력해야 한다.
하지만 코드 블록 내에서 호이스팅을 통해 암묵적으로 <code>foo</code>의 선언이 먼저 진행되기 때문에 참조 에러가 발생한다.</p>
<h3 id="1524-전역-객체와-let">15.2.4 전역 객체와 let</h3>
<p>전역 변수, 전역 함수, 그리고 선언하지 않은 변수에 값을 할당한 암묵적 전역은 전영 객체 <code>window</code>의 프로퍼티가 된다.
전역 객체의 프로퍼티를 참조할 때 <code>window</code>를 생략할 수 있다.</p>
<blockquote>
<p><strong>암묵적 전역?</strong></p>
</blockquote>
<pre><code class="language-js">const x = 1;
y = 1;
console.log(x, y); // 1, 1</code></pre>
<p>선언되지 않은 <code>y</code>에 값을 할당할 수 있다.
<strong>선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되기 때문이다.</strong>
<strong><code>y</code>는 변수가 아니다.</strong> 변수 선언 없이 단지 전역 객체의 프로퍼티로 추가되었을 뿐이다.
따라서 <strong>변수 호이스팅이 발생하지 않는다.</strong></p>
<p>하지만 <strong><code>let</code> 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다.</strong>
즉, <code>window.foo</code>와 같이 접근할 수 없다.
<code>let</code> 전역 변수는 보이지 않는 개념적인 블록(전역 렉시컬 환경의 선언적 환경 레코드) 내에 존재하게 된다.</p>
<h2 id="153-const-키워드">15.3 const 키워드</h2>
<h3 id="1531-선언과-초기화">15.3.1 선언과 초기화</h3>
<p><code>const</code> 키워드로 선언한 변수는 반드시 <strong>선언과 동시에 초기화해야 한다.</strong></p>
<pre><code class="language-js">const foo = 1;
const bar; // SyntaxError</code></pre>
<p><code>let</code> 키워드로 선언한 변수와 마찬가지로 <strong>블록 레벨 스코프</strong>를 가지며 <strong>호이스팅이 발생하지 않는 것처럼 동작</strong>한다.</p>
<h3 id="1532-재할당-금지">15.3.2 재할당 금지</h3>
<p><code>const</code> 키워드로 선언한 변수는 <strong>재할당이 금지된다.</strong></p>
<h3 id="1533-상수">15.3.3 상수</h3>
<p><code>const</code> 키워드로 선언한 변수에 <strong>원시 값을 할당한 경우 변수 값을 변경할 수 없다.</strong>
원시 값은 변경이 불가능한 값이므로 재할당 없이 값을 변경할 수 있는 방법이 없기 때문이다.
이 특징을 이용해 <code>const</code> 키워드를 상수를 표현하는 데 사용하기도 한다.</p>
<h3 id="1534-const-키워드와-객체">15.3.4 const 키워드와 객체</h3>
<p><code>const</code> 키워드로 선언한 변수에 <strong>객체를 할당한 경우 값을 변경할 수 있다.</strong>
객체는 변경 가능한 값이기 때문에 재할당 없이도 직접 변경이 가능하다.</p>
<p>이처럼 <code>const</code> 키워드는 재할당을 금지할 뿐 <strong>불변을 의미하지는 않는다.</strong>
새로운 값을 재할당하는 것은 불가능하지만 프로퍼티 동적 생성, 삭제, 프로퍼티 값의 변경을 통해 <strong>객체를 변경하는 것은 가능하다.</strong></p>
<h2 id="154-var-vs-let-vs-const">15.4 var vs. let vs. const</h2>
<p><code>var</code> 키워드는 사용하지 않는다.
재할당이 필요한 경우에 한정해 <code>let</code> 키워드를 사용한다. 이때 변수의 스코프는 최대한 좁게 만든다.
읽기 전용으로 사용하는 원시 값과 객체에는 <code>const</code> 키워드를 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study] JS DeepDive : 14. 전역 변수의 문제점]]></title>
            <link>https://velog.io/@sjoleee_/Study-JS-DeepDive-14.-%EC%A0%84%EC%97%AD%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@sjoleee_/Study-JS-DeepDive-14.-%EC%A0%84%EC%97%AD%EB%B3%80%EC%88%98</guid>
            <pubDate>Sun, 16 Oct 2022 11:54:54 GMT</pubDate>
            <description><![CDATA[<p>모던 자바스크립트 Deep Dive 스터디</p>
<h1 id="14-전역-변수의-문제점">14. 전역 변수의 문제점</h1>
<h2 id="141-변수의-생명-주기">14.1 변수의 생명 주기</h2>
<h3 id="1411-지역-변수의-생명-주기">14.1.1 지역 변수의 생명 주기</h3>
<blockquote>
<p>💡 지역 변수의 생명 주기는 함수의 생명 주기와 일치한다.</p>
</blockquote>
<pre><code class="language-js">function foo() { // 함수의 생명주기 = 변수 x의 생명주기
  //변수 x 생성

  //변수x에 값 할당
  const x = &#39;local&#39;;
  console.log(x);
  return x;
  //변수 x 소멸
}

foo();
console.log(x); // ReferenceError</code></pre>
<p>변수 선언은 선언문이 어디에 있든 상관 없이 가장 먼저 실행된다.
그러나 이는 전역 변수에 해당하는 설명이고, 함수 내부에서 선언한 변수는 <strong>함수가 호출된 직후, 함수 몸체의 코드가 실행되기 이전에 선언된다.</strong></p>
<p>변수는 값을 저장하기 위해 확보한 메모리 공간 자체 또는 메모리 공간을 식별하기 위해 붙인 이름이다.
따라서 <strong>변수의 생명 주기는 메모리 공간이 확보된 시점부터 메모리 공간이 해제되어 가용 메모리 풀에 반환되는 시점까지다.</strong></p>
<p>지역 변수는 함수가 생성한 스코프에 등록되고, 스코프가 소멸될 때까지 유효하다.
할당된 메모리 공간은 더 이상 누구도 참조하지 않을 때 가비지 콜렉터에 의해 해제되어 가용 메모리 풀에 반환된다.
즉, 누군가가 메모리 공간을 참조하고 있으면 해제되지 않고 확보된 상태로 남아있게 된다.
이는 스코프도 마찬가지로, <strong>함수가 종료한 이후에도 누군가가 스코프를 참조하고 있어서 생존하게 된다면 이를 클로저라 한다.</strong></p>
<h3 id="1412-전역-변수의-생명-주기">14.1.2 전역 변수의 생명 주기</h3>
<blockquote>
<p>💡 전역 변수의 생명 주기는 전역 객체의 생명 주기와 일치한다.</p>
</blockquote>
<p>전역 코드는 명시적인 호출 없이 실행되고, 마지막 문이 실행되어 더 이상 실행할 문이 없을 때 종료한다.</p>
<h2 id="142-전역-변수의-문제점">14.2 전역 변수의 문제점</h2>
<h3 id="암묵적-결합">암묵적 결합</h3>
<p>모든 코드가 전역 변수를 참조하고 변경할 수 있는 암묵적 결합을 허용하게 된다.
변수의 유효 범위가 클수록 코드의 가독성은 나빠지고 의도치 않게 상태가 변경될 위험도 커진다.</p>
<h3 id="긴-생명-주기">긴 생명 주기</h3>
<p>전역 변수는 생명 주기가 길기 때문에 메모리 리소스도 오랜 기간 소비한다.
또한 전역 변수의 상태를 변경할 수 있는 시간도 길고 기회도 많다.</p>
<h3 id="스코프-체인-상에서-종점에-존재">스코프 체인 상에서 종점에 존재</h3>
<p>변수를 검색할 때 전역 변수가 가장 마지막에 검색되므로 전역 변수의 검색 속도가 가장 느리다는 뜻이다.</p>
<h3 id="네임스페이스-오염">네임스페이스 오염</h3>
<p>다른 파일 내에서 동일한 이름으로 명명된 전역 변수나 전역 함수가 같은 스코프 내에 존재할 경우 예상치 못한 결과를 가져올 수 있다.</p>
<h2 id="143-전역-변수의-사용을-억제하는-방법">14.3 전역 변수의 사용을 억제하는 방법</h2>
<p>전역 변수를 반드시 사용해야 할 이유를 찾지 못한다면 지역 변수를 사용해야 한다.
<strong>변수의 스코프는 좁을수록 좋다.</strong></p>
<h3 id="1431-즉시-실행-함수">14.3.1 즉시 실행 함수</h3>
<p>즉시 실행 함수<code>()()</code>는 함수 정의와 함께 단 한 번만 호출된다.
모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 된다.</p>
<pre><code class="language-js">(function(){
  const foo = 10;
})();

console.log(foo); // ReferenceError</code></pre>
<p>이 방법은 라이브러리에 자주 사용된다.</p>
<h3 id="1432-네임스페이스-객체">14.3.2 네임스페이스 객체</h3>
<p>전역에 네임스페이스 역할을 담당할 객체를 생성하고 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법이다.</p>
<pre><code class="language-js">const MYAPP = {}; // 전역 네임스페이스 객체

MYAPP.name = &#39;Lee&#39;;

console.log(MYAPP.name) // Lee</code></pre>
<h3 id="1433-모듈-패턴">14.3.3 모듈 패턴</h3>
<p>모듈 패턴은 클래스를 모방하여 <strong>관련있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만든다.</strong>
<strong>클로저</strong>를 기반으로 동작하며, 전역 변수의 억제는 물론 캡슐화까지 구현할 수 있다.</p>
<p><strong>캡슐화</strong>는 객체의 상태를 나타내는 <strong>프로퍼티</strong>와, 프로퍼티를 참조하고 조작할 수 있는 동작인 <strong>메서드</strong>를 <strong>하나로 묶는 것</strong>을 말한다.
객체의 <strong>특정 프로퍼티나 메서드를 감출 목적</strong>으로 사용하기도 하는데, 이를 <strong>정보 은닉</strong>이라 한다.</p>
<pre><code class="language-js">const Counter = (function(){
  // private 변수 (프라이빗 멤버)
  let num = 0;

  // 외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체를 반환한다.(퍼블릭 멤버)
  return {
    increase(){
      return ++num;
    },
    decrease(){
      return --num;
    }
  }
})();

// private 변수는 외부로 노출되지 않는다.
console.log(Counter.num); // undefined

console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.decrease()); // 1</code></pre>
<h3 id="1434-es6-모듈">14.3.4 ES6 모듈</h3>
<p>ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다.
<code>script</code> 태그에 <code>type=&quot;module&quot;</code> 어트리뷰트를 추가하면 로드된 자바스크립트 파일은 모듈러서 동작한다.
모듈의 파일 확장자는 <code>mjs</code>를 권장한다.</p>
]]></description>
        </item>
    </channel>
</rss>