<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>y__baam.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 05 Mar 2024 05:56:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. y__baam.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/y__baam" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Javascript - Set()]]></title>
            <link>https://velog.io/@y__baam/JavaScript-Set</link>
            <guid>https://velog.io/@y__baam/JavaScript-Set</guid>
            <pubDate>Tue, 05 Mar 2024 05:56:07 GMT</pubDate>
            <description><![CDATA[<h1 id="set">Set?</h1>
<p>순서가 없는 중복되지 않은 데이터의 집합입니다.</p>
<p>Set은 배열과 차이점을 이해하는 것이 중요합니다.</p>
<p>배열은 데이터를 순서있게 저장합니다. 그래서 인덱스를 통해서 특정 위치에 저장되어 있는 데이터에 접근이 가능합니다. 그리고 배열에는 동일한 값을 여러 번 저장할 수 있습니다. 값이 동일하더라도 인덱스가 틀리기 때문에 데이터의 중복에 문제가 되지 않습니다.</p>
<p>반면 Set은 얼핏 보기엔 배열과 비슷해 보일 수 있지만 사실 결이 아주 다른 자료구조입니다.
우선 Set은 데이터를 순서 없이 저장합니다. 따라서 배열처럼 인덱스를 통해서 접근할 수가 없습니다.</p>
<p>그리고 Set은 중복된 데이터를 허용하지 않습니다. 즉, 기존에 세트에 있는 값을 또 추가하면 아무 효력이 발생하지 않습니다.</p>
<h4 id="정리해보자면">정리해보자면</h4>
<ul>
<li>순서가 없고 중복되지 않은 데이터들의 집합이다. </li>
<li>집합 개념에서 파생된 자료구조로, 각 요소가 고유해야 한다는 특징이 있다.</li>
</ul>
<h2 id="자바스크립트에서-set-사용하기">자바스크립트에서 Set() 사용하기</h2>
<h3 id="1-세트-생성">1. 세트 생성</h3>
<p>자바스크립트에서 <code>Set</code>은 클래스이기 때문에, <code>new</code> 키워드와 생성자를 활용해 인스턴스를 생성할 수 있습니다.</p>
<pre><code class="language-jsx">const mySet = new Set(); // Set(0) {}</code></pre>
<h3 id="2-값-추가">2. 값 추가</h3>
<p><code>.add()</code> 메서드를 사용하여 <code>Set</code>에 새로운 요소를 추가합니다.
<code>Set</code>은 중복된 값을 저장하지 않으므로, 이미 존재하는 값을 추가하려고 하면 그 값은 무시됩니다.</p>
<pre><code class="language-jsx">mySet.add(1);
mySet.add(5);
mySet.add(&#39;text&#39;);

console.log(mySet) // Set(3) {1, 5, &#39;text&#39;}</code></pre>
<h3 id="3-값-삭제">3. 값 삭제</h3>
<p><code>.delete</code> 메서드를 사용하여 특정 값을 삭제합니다.
해당 값이 <code>Set</code>내에 존재하면 삭제 후 <code>true</code>를 반환하고, 존재하지 않으면 <code>false</code>를 반환합니다.</p>
<pre><code class="language-jsx">mySet.delete(5);</code></pre>
<h3 id="4-존재-여부-확인">4. 존재 여부 확인</h3>
<p>세트에 특정 값이 존재하는지 확인하려면 <code>has()</code> 메서드를 사용합니다. 보통 <code>if</code> 조건문이나 3항 연산자(ternary operator)와 많이 사용됩니다.</p>
<pre><code class="language-jsx">console.log(mySet.has(1)); // true
console.log(mySet.has(3)); // false</code></pre>
<h3 id="5-set-갯수-확인">5. Set 갯수 확인</h3>
<p><code>.size</code> 속성을 사용하여 <code>Set</code> 내 요소의 개수(크기)를 확인합니다.</p>
<pre><code class="language-jsx">console.log(mySet.size); // 2</code></pre>
<h3 id="6-세트의-요소-순회">6. 세트의 요소 순회</h3>
<p>for...of 루프를 사용하여 <code>Set</code>의 모든 요소를 순회합니다.</p>
<pre><code class="language-jsx">for (let item of mySet) {
  console.log(item)
}</code></pre>
<h3 id="7-foreach-사용하기">7. forEach 사용하기</h3>
<p><code>Set</code>은 <code>.forEach()</code> 메서드도 지원합니다. 이 메서드를 사용하여 <code>Set</code>의 모든 요소를 순회할 수 있습니다. 콜백 함수는 각 요소에 대해 실행됩니다.</p>
<pre><code class="language-jsx">mySet.forEach((value) =&gt; {
  console.log(value)
}</code></pre>
<h3 id="8-배열을-세트로-변환">8. 배열을 세트로 변환</h3>
<p>실제로 코딩을 하다보면 기존에 존재하는 배열로 부터 새로운 세트를 만들어야 하는 경우가 빈번한데, 이 때는 Set() 생성자의 인자로 해당 배열이 할당된 변수를 넘기면 됩니다.</p>
<pre><code class="language-jsx">const array = [1, 2, 2, 3, 3, 3];
const mySet = new Set(array); // Set(3) {1, 2, 3}</code></pre>
<h3 id="9-세트를-배열로-변환">9. 세트를 배열로 변환</h3>
<p>반대로 세트를 배열로 변환할 수도 있습니다.
가장 쉬운 방법은 대괄호 안에서 세트를 상대로 전개(spread) 연산자를 사용하는 것입니다.</p>
<pre><code class="language-jsx">const array = [...mySet]; // [1, 2, 3]</code></pre>
<p>전개 연산자를 별로 안 좋아하신다면 Array.from() 함수를 사용하는 방법도 있습니다.</p>
<pre><code class="language-jsx">const array = Array.from(set); // [1, 2, 3]</code></pre>
<h3 id="10-배열에서-중복-값-제거">10. 배열에서 중복 값 제거</h3>
<p>세트는 중복 값을 허용하지 않기 때문에, 배열이나 문자열에서 중복된 값을 제거하는 데에 매우 유용하게 활용될 수 있습니다.</p>
<pre><code class="language-jsx">const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]</code></pre>
<h2 id="set의-단점">Set의 단점:</h2>
<ul>
<li>순서 없음, Set 내의 요소들은 순서를 갖지 않습니다.</li>
<li>요소를 삭제하려면 해당 요소를 찾아야 하기 때문에 성능이 약간 저하될 수 있습니다.</li>
<li>메모리 사용량, 일부 구현에서 Set은 리스트나 배열에 비해 더 많은 메모리를 사용할 수 있습니다. 내부적으로 해시 테이블을 사용하기 때문!</li>
</ul>
<hr>
<h2 id="결론-">결론 ...</h2>
<p>데이터의 유일성을 보장해야 하고, 순서가 중요하지 않으며, 빠른 검색과 집합 연산이 필요한 경우에 Set을 사용하는 것이 좋습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - Memoization]]></title>
            <link>https://velog.io/@y__baam/React-Memoization</link>
            <guid>https://velog.io/@y__baam/React-Memoization</guid>
            <pubDate>Fri, 26 Jan 2024 08:06:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="what-is-memoization">What is Memoization?</h3>
<p>메모이제이션(memoization)은 불필요한 렌더링을 방지하고, 성능을 최적화하기 위한 기술입니다.
주로 중복 연산을 피하기 위해 사용되며, 특히 재귀 함수나 연산이 많이 드는 계산에서 유용합니다.</p>
</blockquote>
<h1 id="react-에서-memoization">React 에서 Memoization</h1>
<p>React에서는 불필요한 렌더링이 일어날 때 Memoization 을 활용하여 렌더링을 최적화 하고, 렌더링 성능을 개선할 수 있게 도와줍니다. </p>
<p>먼저 React에서 불필요한 렌더링이 발생할 때를 알아봅시다.</p>
<h2 id="불필요한-렌더링">불필요한 렌더링?</h2>
<h3 id="1-props-변화-없이-부모-컴포넌트가-렌더링될-때">1. Props 변화 없이 부모 컴포넌트가 렌더링될 때</h3>
<p>부모 컴포넌트가 렌더링되면 기본적으로 자식 컴포넌트도 렌더링됩니다. 자식 컴포넌트의 props나 state에 변화가 없더라도 말이죠.</p>
<h3 id="2-객체나-배열과-같은-참조-타입의-props">2. 객체나 배열과 같은 참조 타입의 props</h3>
<p>참조 타입의 props가 매번 새로운 참조로 전달되면, 컴포넌트는 새로운 props로 인식하고 불필요하게 렌더링될 수 있습니다.</p>
<h3 id="3-복잡한-계산이-포함된-컴포넌트">3. 복잡한 계산이 포함된 컴포넌트</h3>
<p>컴포넌트 내에서 복잡한 계산이 이루어지는 경우, 이러한 연산들은 컴포넌트가 렌더링될 때마다 반복되어 성능 저하를 일으킬 수 있습니다.</p>
<h3 id="4-컴포넌트의-상태-업데이트">4. 컴포넌트의 상태 업데이트</h3>
<p>컴포넌트는 상태를 업데이트하지만, 실제로 렌더링할 새로운 결과가 없는 경우에도 업데이트되어 불필요한 렌더링이 발생할 수 있습니다.</p>
<h3 id="5-함수-컴포넌트-내에서-선언된-함수들">5. 함수 컴포넌트 내에서 선언된 함수들</h3>
<p>함수 컴포넌트가 렌더링될 때마다 내부에서 정의된 함수들도 새로 생성되므로, 이 함수들을 자식 컴포넌트에 props로 전달하면 자식 컴포넌트도 불필요하게 렌더링될 수 있습니다.</p>
<hr>
<h2 id="1-reactmemo">1. React.memo</h2>
<p>React.memo는 고차 컴포넌트 입니다. React.memo를 이용해서 감싸는 방식으로 자식 컴포넌트가 받는 props에 변화가 있다면 리렌더링을 하고 변화가 없다면 기존에 저장되어 있던 내용을 재사용합니다.</p>
<blockquote>
<p>고차 컴포넌트 (HOC, Higher Order Component)란?
고차 컴포넌트는 컴포넌트 로직을 재사용하기 위해 사용되고 컴포넌트를 매개변수로 받아 새로운 컴포넌트르 반환하는 함수를 의미합니다.</p>
</blockquote>
<h3 id="reactmemo-활용-예시">React.memo 활용 예시</h3>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;

// ProfileComponent는 사용자 프로필을 표시하는 컴포넌트입니다.
// React.memo를 사용하여 props가 변경되지 않으면 리렌더링을 방지합니다.
const ProfileComponent = React.memo(({ user }) =&gt; {
  console.log(&#39;ProfileComponent 렌더링&#39;);
  return (
    &lt;div&gt;
      &lt;p&gt;Name: {user.name}&lt;/p&gt;
      &lt;p&gt;Age: {user.age}&lt;/p&gt;
    &lt;/div&gt;
  );
});

const App = () =&gt; {
  const [count, setCount] = useState(0);
  const [user, setUser] = useState({ name: &#39;John Doe&#39;, age: 30 });

  return (
    &lt;div&gt;
      &lt;button onClick={() =&gt; setCount(c =&gt; c + 1)}&gt;Count: {count}&lt;/button&gt;
      &lt;Profile user={user} /&gt;
    &lt;/div&gt;
  );
};

export default App;
</code></pre>
<ul>
<li>이 컴포넌트는 React.memo로 감싸져 있어, user 객체의 내용이 변경되지 않는 한 리렌더링되지 않습니다. </li>
<li>App 컴포넌트에서 count를 업데이트해도 user 객체는 변경되지 않으므로, ProfileComponent의 불필요한 리렌더링을 방지하여 성능을 최적화합니다.</li>
</ul>
<h3 id="reactmemo-언제-사용해야-될까">React.memo 언제 사용해야 될까?</h3>
<ul>
<li>함수 컴포넌트에서만 적용이 가능합니다.</li>
<li>props로 리렌더링 자주 일어나는 컴포넌트에 유용합니다. 하지만 React.memo()는 props를 비교하여 메모이제이션을 하는 함수기 때문에 props가 계속해서 바뀌는 컴포넌트에서는 성능최적화를 기대하기 어렵습니다.</li>
<li>컴포넌트의 렌더링 비용(무겁고 복잡한 연산)이 큰 경우에 사용됩니다.</li>
</ul>
<h3 id="reactmemo-주의사항">React.memo 주의사항</h3>
<p><strong>1. 얕은 비교 (Shallow Comparison)</strong>
React.memo는 props의 얕은 비교를 수행합니다. 즉, 객체 내부의 깊은 값의 변화를 감지하지 못할 수 있으므로, 객체 내부의 값이 변경될 경우에도 컴포넌트가 업데이트되지 않을 수 있습니다.</p>
<p>해당 부분을 방지하려면 useMemo, useCallback 등의 hook을 사용할 수 있고, React.memo(Component, areEqual)에 두번째 인자값으로 이전 props와 새로운 props를 비교하여 true/false를 반환하고 리렌더링을 결정할 수 있습니다.</p>
<pre><code class="language-jsx">const customEqual = (prevProps, nextProps) =&gt; {
  // 깊은 비교 로직 구현
  return deepEqual(prevProps, nextProps); // 예시로 deepEqual 함수 사용
};

const MyComponent = React.memo(Component, customEqual);
</code></pre>
<p><strong>2. 컴포넌트의 복잡성</strong>
단순한 컴포넌트에 React.memo를 사용하는 것은 오히려 성능에 부정적인 영향을 미칠 수 있습니다. props 비교에 드는 비용이 렌더링 비용보다 클 수 있기 때문입니다.</p>
<hr>
<h2 id="2-usememo">2. useMemo</h2>
<p>useMemo는 계산 비용이 많이 드는 연산의 결과 값을 메모이제이션하여, 같은 입력에 대해 여러 번 재계산하지 않도록 하고, 렌더링 성능을 최적화하는 데 사용됩니다. </p>
<h3 id="usememo-활용-예시">useMemo 활용 예시</h3>
<pre><code class="language-jsx">import React, { useState, useMemo } from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;

const MyComponent = () =&gt; {
  const [count, setCount] = useState(0);
  const [inputValue, setInputValue] = useState(&#39;&#39;);

  // calculateMemoizedCount 함수는 count 값을 인자로 받아 복잡한 계산을 수행합니다.
  const calculateMemoizedCount = (count) =&gt; {
    let result = count;
    for (let i = 0; i &lt; 100; i++) {
      console.log(&#39;테스트 :: &#39;, i);
      result += i;
    }
    console.log(&#39;끝&#39;);
    return result;
  };

  // useMemo를 사용하여 calculateMemoizedCount 함수의 결과를 메모이제이션합니다.
  // 이 함수는 count 값이 변경될 때만 재계산됩니다.
  const memoizedCount = useMemo(() =&gt; calculateMemoizedCount(count), [count]);

  return (
    &lt;div&gt;
      &lt;p&gt;Count: {count}&lt;/p&gt;
      &lt;p&gt;Memo Count: {memoizedCount}&lt;/p&gt;
      &lt;input
        value={inputValue}
        onChange={(e) =&gt; setInputValue(e.target.value)}
      /&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Count&lt;/button&gt;
    &lt;/div&gt;
  );
};

const App = () =&gt; {
  return &lt;MyComponent /&gt;;
};

export default App;
</code></pre>
<h3 id="usememo-언제-사용해야-될까">useMemo 언제 사용해야 될까?</h3>
<p><strong>1. 계산 비용이 높은 함수의 결과를 캐싱할 때</strong>
함수가 복잡하고 계산 비용이 많이 드는 경우, 입력 값이 변경될 때만 함수를 다시 실행하고, 그렇지 않은 경우 이전 결과를 재사용하고 싶을 때 사용합니다.</p>
<p><strong>2. 렌더링 최적화가 필요할 때</strong>
특히 리스트와 같이 큰 양의 데이터를 렌더링하거나, 자식 컴포넌트가 불필요하게 자주 렌더링되는 경우, 자식 컴포넌트에 전달되는 props를 메모이제이션하여 성능을 향상시킬 수 있습니다.</p>
<p><strong>3. 참조 동일성을 유지할 때</strong>
객체나 배열과 같은 참조 타입의 값을 props로 전달할 때, 부모 컴포넌트가 렌더링될 때마다 새로운 참조가 생성되는 것을 방지하고, 자식 컴포넌트가 불필요하게 리렌더링되는 것을 방지하기 위해 useMemo를 사용합니다.</p>
<p><strong>4. 의존성 배열에 있는 값들에 의해서만 계산이 이루어져야 할 때</strong>
useMemo의 두 번째 인자인 의존성 배열을 사용하여, 배열 내의 값들이 변경되었을 때만 메모이제이션된 값을 다시 계산하도록 할 수 있습니다.</p>
<h3 id="usememo-주의사항">useMemo 주의사항</h3>
<p><strong>1. 필요성 검토</strong>
useMemo를 사용하기 전에 실제로 성능 개선이 필요한지 검토해야 합니다. 메모이제이션은 리소스를 사용하므로, 모든 값을 메모이제이션하는 것이 항상 좋은 것은 아닙니다.</p>
<p><strong>2. 복잡한 객체 참조</strong>
useMemo는 참조 타입의 값을 메모이제이션할 때 특히 유용하지만, 복잡한 객체나 배열을 메모이제이션하는 경우에는 깊은 비교가 필요할 수 있으며, 이는 추가적인 계산 비용을 발생시킬 수 있습니다.</p>
<p><strong>3. 의존성 배열 관리</strong>
useMemo의 의존성 배열을 정확하게 관리해야 합니다. 배열 내의 값이 변경될 때만 메모이제이션된 값을 다시 계산해야 하며, 잘못된 값이 배열에 포함되면 예상치 못한 버그를 일으킬 수 있습니다.</p>
<p><strong>4. sideEffect 방지</strong> 
useMemo는 순수 계산을 위한 것이므로, sideEffect을 발생시키는 연산에는 사용하지 않아야 합니다. sideEffect가 필요한 경우 useEffect를 사용해야 합니다.</p>
<p><strong>5. 성능 최적화 도구로서의 사용</strong>
useMemo는 성능 최적화를 위한 도구이지만, 모든 성능 문제를 해결할 수 있는 만병통치약은 아닙니다. 성능 문제가 발생했을 때 프로파일링을 통해 실제 병목 현상을 파악하고 그에 맞춰 최적화를 진행해야 합니다.</p>
<p><strong>6. 메모이제이션된 값의 안정성</strong>
React는 메모리를 관리하기 위해 useMemo로 생성된 값을 버릴 수 있습니다. 따라서 리렌더링 사이에 메모이제이션된 값이 반드시 유지되어야 한다는 보장이 없습니다. 이는 언제나 안정적인 상태 저장소로 사용할 수 없다는 것을 의미합니다.</p>
<hr>
<h2 id="3-usecallback">3. useCallback</h2>
<p>useCallback은 함수를 메모이제이션하는 데 사용됩니다. 특정 함수를 컴포넌트의 렌더링 사이에 재사용할 수 있도록 하여, 컴포넌트의 불필요한 리렌더링을 방지하고 성능을 최적화합니다. </p>
<p>이는 주로 자식 컴포넌트에 함수를 props로 전달할 때 유용하며, 자식 컴포넌트가 불필요하게 렌더링되는 것을 방지할 수 있습니다.</p>
<blockquote>
<p>useCallback과 useMemo의 차이점은 함수를 재사용하느냐, 값을 재사용하느냐의 차이입니다.</p>
</blockquote>
<h3 id="usecallback-활용-예시">useCallback 활용 예시</h3>
<pre><code class="language-jsx">import React, { useState, useEffect } from &#39;react&#39;;

function Profile({ userId }) {
  const [user, setUser] = useState(null);

  // 이 함수는 컴포넌트가 리렌더링될 때마다 새로운 참조를 갖게 됩니다.
  const fetchUser = () =&gt; {
    fetch(`https://api.example.com/users/${userId}`)
      .then(response =&gt; response.json())
      .then(data =&gt; setUser(data));
  };

  useEffect(() =&gt; {
    fetchUser();
  }, [fetchUser]); // fetchUser가 변경될 때마다 useEffect가 실행됩니다.

  // ...
}</code></pre>
<p>위의 fetchUser 함수는 컴포넌트가 리렌더링될 때마다 새로운 참조를 가지게 됩니다.</p>
<p>useEffect의 의존성 배열에 fetchUser가 포함되어 있기 때문에, 함수가 변경될 때마다 useEffect가 다시 실행됩니다. </p>
<p>이로 인해 fetchUser는 계속해서 새로운 데이터를 가져오고 setUser를 호출하여 상태를 업데이트하고, 이 상태 업데이트는 컴포넌트를 다시 리렌더링시키게 되어 무한 루프에 빠지게 됩니다.</p>
<p>이제     <code>useCallback</code> 을 활용하여 무한 루프를 해결하는 방법을 살펴봅니다.</p>
<pre><code class="language-jsx">import React, { useState, useEffect, useCallback } from &#39;react&#39;;

function Profile({ userId }) {
  const [user, setUser] = useState(null);

  // useCallback을 사용하여 함수를 메모이제이션합니다.
  const fetchUser = useCallback(() =&gt; {
    fetch(`https://api.example.com/users/${userId}`)
      .then(response =&gt; response.json())
      .then(data =&gt; setUser(data));
  }, [userId]); // userId가 변경될 때만 fetchUser 함수를 새로 생성합니다.

  useEffect(() =&gt; {
    fetchUser();
  }, [fetchUser]); // fetchUser가 변경될 때만 useEffect가 실행됩니다.

  // ...
}</code></pre>
<p>이제 fetchUser는 userId가 변경될 때만 새로 생성됩니다.</p>
<p>userId가 바뀌지 않는 한 useEffect는 fetchUser 함수로 인해 재실행되지 않으므로 무한 루프에 빠지지 않게 됩니다.</p>
<h3 id="usecallback-언제-사용해야-될까">useCallback 언제 사용해야 될까?</h3>
<p><strong>1. 자식 컴포넌트에 함수를 props로 전달할 때</strong>
특히 React.memo로 최적화된 자식 컴포넌트에 함수를 전달할 경우, 리렌더링을 방지하기 위해 useCallback으로 함수를 메모이제이션합니다.</p>
<p><strong>2. 의존성 배열을 사용하는 훅에 함수를 포함할 때</strong>
useEffect, useMemo, useCallback 등에서 의존성 배열을 사용하고, 배열 내의 함수가 불필요한 재생성으로 인해 무한 루프나 추가 리렌더링을 발생시킬 수 있는 경우에 useCallback을 사용합니다.</p>
<p><strong>3. 렌더링 사이에 동일한 참조를 유지해야 할 때</strong>
동일한 함수 참조를 유지해야 하는 경우 (이벤트 핸들러나 콜백 함수)에 useCallback을 사용하여 함수가 불필요하게 재생성되는 것을 방지합니다.</p>
<h3 id="usecallback-주의사항">useCallback 주의사항</h3>
<p><strong>1. 필요성 검토</strong> 
모든 함수에 useCallback을 사용할 필요는 없습니다. 특히 함수가 자주 변경되지 않거나, 메모이제이션의 이득이 크지 않은 경우에는 useCallback 사용을 피하는 것이 좋습니다.</p>
<p><strong>2. 의존성 배열 관리</strong>
useCallback의 의존성 배열에는 함수 실행에 필요한 모든 외부 변수와 상태를 포함시켜야 합니다. 배열에 포함되지 않은 변수가 함수 내에서 사용되면 예상치 못한 오류가 발생할 수 있습니다.</p>
<p><strong>3. 메모리 사용 증가</strong>
useCallback은 메모리에 함수를 저장합니다. 함수가 크거나 복잡할 경우, 불필요하게 메모리 사용량을 증가시킬 수 있으므로 신중하게 사용해야 합니다.</p>
<p><strong>4. 렌더링 최적화</strong>
useCallback은 주로 자식 컴포넌트에 함수를 props로 전달할 때 사용됩니다. 이는 자식 컴포넌트의 불필요한 리렌더링을 방지할 수 있지만, 자식 컴포넌트가 React.memo로 감싸져 있지 않다면 큰 효과를 보기 어려울 수 있습니다.</p>
<hr>
<h2 id="마치며">마치며</h2>
<p>React.memo, useMemo, useCallback 등은 적절히 사용될 때, 렌더링 성능을 상당히 개선할 수 있습니다. 특히 불필요한 리렌더링을 방지하고 계산 비용이 높은 작업을 효율적으로 관리하는 데 큰 도움이 됩니다.</p>
<p>하지만 이러한 방법들을 사용할 때 주의가 필요합니다. 무분별한 사용은 오히려 성능 저하나 예상치 못한 버그를 발생시킬 수 있기 때문입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript - prototype]]></title>
            <link>https://velog.io/@y__baam/Javascript-prototype</link>
            <guid>https://velog.io/@y__baam/Javascript-prototype</guid>
            <pubDate>Mon, 20 Nov 2023 01:29:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트는 프로토타입 기반 언어입니다. </p>
</blockquote>
<p>클래스 기반 언어에서는 &#39;상속&#39;을 사용하지만 프로토타입 기반 언어에서는 어떤 객체를 원형으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻습니다.</p>
<h2 id="프로토타입의-개념-이해">프로토타입의 개념 이해</h2>
<h3 id="constructor-prototype-instance">constructor, prototype, instance</h3>
<p><img src="https://velog.velcdn.com/images/y__baam/post/9081be3c-6f51-4572-845c-bcef67c0025a/image.png" alt=""></p>
<pre><code class="language-jsx">let instance = new Constructor();</code></pre>
<ul>
<li>어떤 생성자 함수를 new 연산자와 함께 호출하면</li>
<li>Constructor 에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성됩니다.</li>
<li>이때 instance에는 <code>__proto__</code> 라는 프로퍼티가 자동으로 부여되는데,</li>
<li>이 프로퍼티는 Constructor 의 prototype 이라는 프로퍼티를 참조합니다.</li>
</ul>
<p>prototype이라는 프로퍼티와 <code>__proto__</code> 라는 프로퍼티가 새로 등장했는데, 이 둘의 관계가 프로토타입 개념의 핵심입니다. prototype은 객체입니다. 이를 참조하는 <code>__proto__</code> 역시 당연히 객체입니다. prototype 객체 내부에는 인스턴스가 사용할 메서드를 저장합니다. 그러면 인스턴스에서도 숨겨진 프로퍼티인 <code>__proto__</code> 를 통해 이 메서드들에 접근할 수 있게 됩니다.</p>
<p>Person 이라는 생성자 함수의 prototype에 getName 이라는 메서드를 지정했습니다.</p>
<pre><code class="language-jsx">let Person = function (name) {
  this._name = name;
};

Person.prototype.getName = function() {
  return this._name;
};
</code></pre>
<p>이제 Person 의 인스턴스는 <code>__proto__</code> 프로퍼티를 통해 getName 을 호출할 수 있습니다.</p>
<p>왜냐하면 instance 의 <code>__proto__</code> 가 Constructor 의 prototype 프로퍼티를 참조하므로 결국 둘은 같은 객체를 바라보기 때문입니다.</p>
<pre><code class="language-jsx">let suzi = new Person(&#39;Suzi&#39;);
suzi.__proto__.getName();                // undefined

Person.prototype === suzi.__proto__     // true</code></pre>
<p>메서드 호출 결과로 undefined 가 나왔습니다. <code>Suzi</code> 라는 값이 나오지 않은 것보다 <strong>&quot;에러가 발생하지 않았다&quot;</strong> 는 점을 주목해 봅시다. 어떤 변수를 실행하 <code>undefined</code> 가 나왔다는 것은 이 변수가 <code>호출할 수 있는 함수</code> 에 해당한다는 것을 의미합니다. 즉 함수가 아닌 다른 데이터 타입이었다면 TypeError 가 발생했을 것입니다.</p>
<p>다음으로 함수 내부에서 어떤 값을 반환하는지 살펴봅시다. <code>this.name</code> 값을 리턴하는 내용으로 구성돼 있는데, this에 원래의 의도와는 다른 값이 할당된 것이 아닐까라고 생각할 수 있습니다. 문제는 this에 바인딩된 대상이 잘못 지정됐다는 것입니다.</p>
<p>어떤 함수를 &#39;메서드로서&#39; 호출할 때는 메서드명 바로 앞의 객체가 곧 this가 되는데, 이 객체 내부에는 name 프로퍼티가 없으므로 &#39;찾고자 하는 식별자가 정의돼 있지 않있지 않을 때는 Error 대신 undefined 를 반환한다&#39;라는 자바스크립트 규약에 의해 undefined 가 반환된 것입니다.</p>
<p><code>__proto__</code> 는 생략 가능한 프로퍼티 입니다. this를 인스턴스로 사용하고 싶다면 <code>__proto__</code> 를 생략하면 됩니다. </p>
<pre><code class="language-jsx">suzi.__proto__.getName
-&gt; suzi(.__proto__).getName
-&gt; suzi.getName</code></pre>
<p><code>__proto__</code> 를 생략하지 않으면 this 는 <code>suzi.__proto__</code> 를 가리키지만, 이를 생략하면 <code>suzi</code> 를 가리킵니다. <code>suzi.__proto__</code> 에 있는 메서드인 getName을 실행하지만 this는 suzi를 바라보게 할 수 있게 된 것입니다. </p>
<p>도식으로 보면 다음과 같습니다.
<img src="https://velog.velcdn.com/images/y__baam/post/09cc4760-fc51-4081-a96f-363f0034a0ea/image.png" alt="">
여기까지 요약하자면 </p>
<blockquote>
<p> new 연산자로 Constructor 를 호출하면 instance 가 만들어지는데, 이 instance 의 생략 가능한 프로퍼티인 <code>__proto__</code> 는 Constructor 의 prototype 을 참조한다.</p>
</blockquote>
<p>더 상세히 설명하자면</p>
<blockquote>
</blockquote>
<p>자바스크립트는 함수에 자동으로 객체인 prototype 프로퍼티를 생성해놓는데, 해당 함수를 생성자 함수로서 사용할 경우, 즉 new 연산자와 함께 함수를 호출할 경우, 그로부터 생성된 인스턴스에는 숨겨진 프로퍼티인 <code>__proto__</code> 가 자동으로 생성되며, 이 프로퍼티는 생성자 함수의 prototype 프로퍼티를 참조합니다. <code>__proto__</code> 프로퍼티는 생략 가능하도록 구현돼 있기 때문에 <strong>생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 됩니다.</strong></p>
<p>한편 Array 의 prototype 프로퍼티 내부에 있지 않은 from, isArray 등의 메서드들은 인스턴스가 직접 호출할 수 없기 때문에 이들은 Array 생성자 함수에서 직접 접근해야 실행이 가능합니다.</p>
<pre><code class="language-jsx">let arr = [1, 2];
arr.forEach(function() {});            // (o)
Array.isArray(arr);                    // (o) true
arr.isArray();                        // (X) TypeError: arr.isArray is not a function
</code></pre>
<h3 id="constructor-프로퍼티">constructor 프로퍼티</h3>
<p>생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor 라는 프로퍼티가 있습니다. 인스턴스의 <code>__proto__</code> 객체 내부에도 마찬가지입니다. 이 프로퍼티는 단어 그대로 원래의 생성자 함수(자기 자신)를 참조합니다. 이 역시 인스턴스와의 관계가 있어서 필요한 정보입니다. 인스턴스로부터 그 원형이 무엇인지를 알 수 있는 수단이기 때문입니다.</p>
<pre><code class="language-jsx">let arr = [1, 2];
Array.prototype.constructor === Array     // true
arr.__proto__.constructor === Array        // true
arr.constructor === Array                // true

let arr2 = new arr.constcutor(3, 4);
console.log(arr2);                        // [3, 4]
</code></pre>
<p>한편 construtcot 는 읽기 전용 속성이 부여된 예외적인 경우(기본형 리터럴 변수 - number, string, boolean) 를 제외하고는 값을 바꿀 수 있습니다.</p>
<pre><code class="language-jsx">let NewConstructor = function () {
  console.log(&#39;this is new constructor!&#39;);
};
let dataTypes = [
  1, // Number &amp; false
  &#39;test&#39;, // String &amp; false
  true, // Boolean &amp; false
  {}, // NewConstructor &amp; false
  [], // NewConstructor &amp; false
  function () {}, // NewConstructor &amp; false
  /test/, // NewConstructor &amp; false
  new Number(), // NewConstructor &amp; false
  new String(), // NewConstructor &amp; false
  new Boolean, // NewConstructor &amp; false
  new Object(), // NewConstructor &amp; false
  new Array(), // NewConstructor &amp; false
  new Function(), // NewConstructor &amp; false
  new RegExp(), // NewConstructor &amp; false
  new Date(), // NewConstructor &amp; false
  new Error() // NewConstructor &amp; false
];

dataTypes.forEach(function(d) {
  d.constructor = NewConstructor;
  console.log(d.constructor.name, &#39;&amp;&#39;, d instanceof NewConstructor);
});</code></pre>
<blockquote>
<p>모든 데이터가 d instanceof NewConstructor 명령에 대해 false를 반환합니다. 이로부터 <strong><em>constructor 를 변경하더라도 참조하는 대상이 변경될 뿐 이미 만들어진 인스턴스의 원형이 바뀐다거나 데이터 타입이 변하는 것은 아님을 알 수 있습니다.</em></strong> </p>
</blockquote>
<p>어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는 게 항상 안전한 것은 아닌겁니다.</p>
<h2 id="프로토타입-체인">프로토타입 체인</h2>
<h3 id="메서드-오버라이드">메서드 오버라이드</h3>
<p>인스턴스가 동일한 이름의 프로퍼티 또는 메서드를 가지고 있다면?</p>
<pre><code class="language-jsx">let Person = function (name) {
  this.name = name;
};

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

let iu = new Person(&#39;지금&#39;);
iu.getName = function() {
  return &#39;바로&#39; + this.name;  
};

console.log(iu.getName());                // 바로 지금</code></pre>
<p><code>iu.__proto__.getName</code> 이 아닌 iu 객체에 있는 getName 메서드가 호출됐습니다.
혼란스러울 수 있지만 여기서 일어난 현상을 <code>메서드 오버라이드</code> 라고 합니다. 메서드 위에 메서드를 덮어씌웠다는 표현입니다.</p>
<p>원본을 제거하고 다른 대상으로 교체하는 것이 아니라 원본이 그대로 있는 상태에서 다른 대상을 그 위에 얹는 이미지를 떠올리면 됩니다.</p>
<p>자바스크립트 엔진이 getName 이라는 메서드를 찾는 방식은 가장 가까운 대상인 자신의 프로퍼티를 검색하고, 없으면 그다음으로 가까운 대상인 <code>__proto__</code> 를 검색하는 순서로 진행됩니다. 그러니까 <code>__proto__</code> 에 있는 메서드는 자신에게 있는 메서드보다 검색 순서에서 밀려 호출되지 않은 것입니다.</p>
<p>그렇다면 메서드 오버라이딩이 이뤄져 있는 상황에서 prototype 에 있는 메서드에 접근하려면 어떻게 하면 될까요?</p>
<pre><code class="language-jsx">console.log(iu.__proto__.getName());        // undefined</code></pre>
<p>undefined 가 출력됐습니다. this가 prototype 객체 <code>iu.__proto__</code> 를 가리키는데 prototype 상에는 name 프로퍼티가 없기 때문입니다. 만약 prototype 에 name 프로퍼티가 있다면 그 값을 출력할 것입니다.</p>
<pre><code class="language-jsx">Person.prototype.name = &#39;이지금&#39;
console.log(iu.__proto__.getName());        // 이지금</code></pre>
<p>원하는 메서드가 호출되고 있습니다.
다만 this가 prototype을 바라보고 있는데 이걸 인스턴스를 바라보도록 바꿔주면 되겠습니다.</p>
<p>call이나 apply로 해결하면 됩니다.</p>
<pre><code class="language-jsx">console.log(iu.__proto__.getName.call(iu));    // 지금</code></pre>
<h3 id="프로토타입-체인-1">프로토타입 체인</h3>
<p>프로토타입 체인을 설명하기에 앞서 이번에는 객체의 내부 구조를 살펴보겠습니다.
<img src="https://velog.velcdn.com/images/y__baam/post/11645dfe-6623-4a6f-92d5-6c6b6e997ebd/image.png" alt=""></p>
<p>첫 줄을 통해 Object의 인스턴스임을 알 수 있고, 프로퍼티 a의 값이 1이 보이고, <code>__proto__</code> 내부에는 hasOwnProperty, isPrototypeOf, toLocaleString, toString, valueOf 등의 메서드가 보입니다. constructor는 생성자 함수인 Object를 가리키고 있습니다. </p>
<p>이번에는 배열의 구조를 살펴보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/y__baam/post/0e47d481-dc8d-4c61-9d85-80327a7cdee6/image.png" alt=""></p>
<p>배열의 <code>__proto__</code> 안에는 또다시 <code>__proto__</code> 가 등장합니다. 열어보니 앞에서 살펴본 객체의 <code>__proto__</code> 와 동일한 내용으로 이뤄져 있습니다. 왜냐면 prototype 객체가 &#39;객체&#39;이기 때문입니다. 기본적으로 모든 객체의 <code>__proto__</code> 에는 Object.prototype이 연결됩니다. prototype 객체도 예외가 아닙니다. </p>
<p>이 관계를 그림으로 표현하면 다음과 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/y__baam/post/3f4a214a-6d77-4591-a54c-cd966f4f0383/image.png" alt=""></p>
<p><code>__proto__</code> 는 생략이 가능하기 때문에, 배열은 Array.prototype 내부의 메서드를 자신의 것처럼 사용할 수 있습니다. 즉, 객체 메서드로 실행이 가능한 것입니다.</p>
<blockquote>
<p>어떤 데이터의 <code>__proto__</code> 프로퍼티 내부에서 다시 <code>__proto__</code> 프로퍼티가 연쇄적으로 이어진 것을 <strong>프로토타입 체인</strong> 이라 하고, 이 체인을 따라가며 검색하는 것을 <strong>프로토타입 체이닝</strong>이라고 합니다.</p>
</blockquote>
<p>프로토타입 체이닝은 메서드 오버라이드와 동일한 맥락입니다. 어떤 메서드를 호출하면 자바스크립트 엔진은 데이터 자신의 프로퍼티들을 검색해서 원하는 메서드가 있으면 그 메서드를 실행하고, 없으면 <code>__proto__</code> 를 검색해서 있으면 그 메서드를 실행하고, 없으면 다시 <code>__proto__</code> 를 검색해서 실행하는 식으로 진행합니다.</p>
<h4 id="메서드-오버라이드와-프로토타입-체이닝">메서드 오버라이드와 프로토타입 체이닝</h4>
<pre><code class="language-jsx">let arr = [1, 2];
Array.prototype.toString.call(arr);        // 1, 2
Object.prototype.toString.call(arr);    // [object Array]
arr.toString();                            // 1, 2

arr.toString = function() {
  return this.join(&#39;_&#39;);
};

arr.toString();                            //1_2</code></pre>
<h3 id="객체-전용-메서드의-예외사항">객체 전용 메서드의 예외사항</h3>
<p>어떤 생성자 함수이든 prototype 은 반드시 객체이기 때문에 Object.prototype 이 언제나 프로토타입 체인의 최상단에 존재하게 됩니다. 따라서 객체에서만 사용할 메서드는 다르 여느 데이터 타입처럼 프로토타입 객체 안에 정의할 수가 없습니다. 객체에서만 사용할 메서드를 Object.prototype 내부에서 정의한다면 다른 데이터 타입도 해당 메서드를 사용할 수 있게 되기 때문입니다.</p>
<pre><code class="language-jsx">Object.prototype.getEntries = function() {
  let res = [];
  for (let prop in this) {
    if (this.hasOwnProperty(prop)) {
      res.push([prop, this[prop]]);
    }
  }
  return res;
};
let data = [
  [&#39;object&#39;, { a: 1, b: 2, c: 3 }], //[[&quot;a&quot;,1], [&quot;b&quot;,2],[&quot;c&quot;,3]]
  [&#39;number&#39;, 345], // []
  [&#39;string&#39;, &#39;abc&#39;], //[[&quot;0&quot;,&quot;a&quot;], [&quot;1&quot;,&quot;b&quot;], [&quot;2&quot;,&quot;c&quot;]]
  [&#39;boolean&#39;, false], //[]
  [&#39;func&#39;, function () {}], //[]
  [&#39;array&#39;, [1, 2, 3]]
 // [[&quot;0&quot;, 1], [&quot;1&quot;, 2], [&quot;2&quot;, 3]]
  ];
data.forEach(function(datum) {
  console.log(datum[1].getEntries())
});
</code></pre>
<p>원래 의도대로라면 객체가 아닌 다른 데이터 타입에 대해서는 오류를 던지게끔 돼야 할 텐데, 어느 데이터 타입이건 거의 무조건 프로토타입 체이닝을 통해 getEntries 메서드에 접근할 수 있으니 그렇게 동작하지 않는 것입니다.</p>
<p>이와 같은 이유로 객체만을 대상으로 동작하는 객체 전용 메서드들은 부득이 Object.prototype이 아닌 Object 에 스태틱 메서드로 부여할 수밖에 없었습니다. 또한 생성자 함수인 Object 와 인스턴스인 객체 리터럴 사이에는 this 를 통한 연결이 불가능하기 때문에 여느 전용 메서드처럼 &#39;메서드 앞의 대상이 곧 this&#39;가 되는 방식 대신 this 사용을 포기하고 대상 인스턴스를 인자로 직접 주입해야 하는 방식으로 구현돼 있습니다.</p>
<p>객체 한정 메서드들을 Object.prototype 이 아닌 Object 에 직접 부여할 수밖에 없었던 이유를 다시 강조하자면, Object.prototype 이 참조형 데이터뿐 아니라 기본형 데이터조차 <code>__proto__</code> 에 반복 접근함으로써 도달할 수 있는 최상위 존재이기 때문입니다.</p>
<h2 id="정리">정리</h2>
<blockquote>
<p>어떤 생성자 함수를 new 연산자와 함께 호출하면 Constructor 에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성되는데, 이 인스턴스에는 <code>__proto__</code> 라는, Constructor 의 prototype 프로퍼티를 참조하는 프로퍼티가 자동으로 부여됩니다. <code>__proto__</code> 는 생략 가능한 속성이라서, 인스턴스는 Constructor.prototype 의 메서드를 마치 자신의 메서드인 것처럼 호출할 수 있습니다.</p>
</blockquote>
<p>Constructor.prototype 에는 constructor 라는 프로퍼티가 있는데, 이는 다시 생성자 함수 자신을 가리킵니다. 이 프로퍼티는 인스턴스가 자신의 생성자 함수가 무엇인지를 알고자 할 때 필요한 수단입니다.</p>
<blockquote>
</blockquote>
<p>직각삼각형의 대각선 방향, 즉 <code>__proto__</code>  방향을 계속 찾아가면 최종적으로 Object.prototype 에 당도하게 됩니다. 이런식으로 <code>__proto__</code>  안에 다시 <code>__proto__</code>  를 찾아가는 과정을 프로토타입 체이닝이라고 하며, 이 프로토타입 체이닝을 통해 각 프로토타입 메서드를 자신의 것처럼 호출할 수 있습니다. 이때 접근 방식은 자신으로부터 가장 가까운 대상부터 점차 먼 대상으로 나아가며, 원하는 값을 찾으면 검색을 중단합니다.</p>
<blockquote>
</blockquote>
<p>Object.prototype 에는 모든 데이터 타입에서 사용할 수 있는 범용적인 메서드만이 존재하며, 객체 전용 메서드는 여느 데이터 타입과 달리 Object 생성자 함수에 스태틱하게 담겨있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript - 클로저 (Closure)]]></title>
            <link>https://velog.io/@y__baam/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%A1%9C%EC%A0%80-Closure</link>
            <guid>https://velog.io/@y__baam/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%A1%9C%EC%A0%80-Closure</guid>
            <pubDate>Sat, 18 Nov 2023 07:55:33 GMT</pubDate>
            <description><![CDATA[<h2 id="클로저의-의미">클로저의 의미</h2>
<ul>
<li>클로저는 여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성입니다.</li>
<li>클로저는 함수와 함수가 선언된 Lexical Environment 의 조합입니다. </li>
<li>클로저는 함수가 선언될 때의 스코프를 기억하고, 함수가 해당 스코프 외부에서 호출될 때에도 그 스코프에 접근할 수 있도록 하는 개념입니다.</li>
<li>클로저는 어떤 함수에서 선언한 변수를 참조하는 내부 함수에서만 발생하는 현상입니다.</li>
</ul>
<blockquote>
<p>A closure is the combination of a function and the lexical environment within which that function was declared - MDN</p>
</blockquote>
<p>&quot;클로저는 함수와 그 함수가 선언될 당시의 lexical environment 의 상호관계에 따른 현상&quot;</p>
<pre><code class="language-jsx">const outer = function() {
  const a = 1;

  const inner = function() {
    return ++a;
  };
  return inner;
};

const outer2 = outer();
console.log(outer2());        // 2
console.log(outer2());        // 3</code></pre>
<p>위의 코드에서 outer 함수는 inner 함수를 반환하고 있습니다. 이렇게 반환된 inner 함수는 외부 변수 a를 참조하고 있고, outer 함수의 실행이 끝난 이후에도 호출됩니다.</p>
<p>이런 경우, outer 함수가 실행될 때 생성된 Lexical Environment가 inner 함수의 스코프 체인에 포함되어 있어서 inner 함수는 외부 변수 a에 접근할 수 있습니다. 그래서 outer2를 호출할 때마다 a가 유지되어 2와 3 같은 결과를 얻을 수 있습니다.</p>
<p>함수의 실행 컨텍스트가 종료된 이후에도 Lexical Environment가 가비지 컬렉터의 수집 대상에서 제외되는 상황입니다. 이는 지역 변수를 참조하는 내부 함수가 외부로 전달되었기 때문에 발생합니다.</p>
<p>이처럼 함수의 실행 컨텍스트가 종료된 후에도 LexicalEnvironment 가 가비지 컬렉터의 수집 대상에서 제외되는 경우는 지역변수를 참조하는 내부함수가 외부로 전달된 경우가 유일합니다. 
그러니까 &quot;어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상&quot;이란 &quot;외부 함수의 LexicalEnvironment가 가비지 컬렉팅 되지 않는 현상&quot;을 말하는 것입니다.</p>
<hr>
<h2 id="메모리-관리">메모리 관리</h2>
<p>_메모리 누수의 위험을 이유로 클로저 사용을 조심해야 한다거나 심지어 지양해야 한다고 주장하는 사람들도 있지만 메모리 소모는 클로저의 본질적인 특성일 뿐입니다. 오히려 이러한 특성을 정확히 이해하고 잘 활용하도록 노력해야 합니다.
_</p>
<p>&#39;메모리 소모&#39;에 대한 관리법만 잘 파악해서 적용해야겠습니다.</p>
<p>관리 방법은 간단합니다.</p>
<p>클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 메모리를 소모하도록 함으로써 발생합니다. 그렇다면 그 필요성이 사라진 시점엔 더 이상 메모리를 소모하지 않게 해주면 됩니다. 참조 카운트를 0으로 만들면 언젠가 가비지 컬렉터가 수거해 갈 것이고, 이때 소모됐던 메모리가 회수될 것입니다. </p>
<p>방법은 식별자에 참조형이 아닌 기본형 데이터<code>(null 이나 undefined)</code>를 할당하면 됩니다.</p>
<pre><code class="language-jsx">const outer (function() {
  const a = 1;
  const inner = function() {
    return ++a;
  }
  return inner;
})();

console.log(outer());
console.log(outer());
outer = null;                     // outer 식별자의 inner 함수 참조를 끊음
</code></pre>
<hr>
<h2 id="클로저-활용-사례">클로저 활용 사례</h2>
<h3 id="콜백-함수-내부에서-외부-데이터를-사용하고자-할-때">콜백 함수 내부에서 외부 데이터를 사용하고자 할 때</h3>
<pre><code class="language-jsx">const fruits = [&#39;apple&#39;, &#39;banana&#39;, &#39;peach&#39;];
const $ul = document.createElement(&#39;ul&#39;);

fruits.forEach(function(fruit) {                // (A)
  const $li = document.createElement(&#39;li&#39;));
  $li.innerText = fruit;
  $li.addEventListiener(&#39;click&#39;, function() {    // (B)
    alert(&#39;your choice is&#39; + fruit);
  });
  $ul.appendChild($li);
});
document.body.appendChild($ul);</code></pre>
<p>콜백 함수 (A)는 그 내부에서 외부 변수를 사용하지 않고 있으므로 클로저가 없지만, addEventListener에 넘겨준 콜백함수 (B)는 fruit 이라는 외부 변수를 참조하고 있으므로 클로저가 있습니다. (A)는 fruits의 개수만큼 실행되며, 그때마다 새로운 실행 컨텍스트가 활성화될 것입니다. A의 실행 종료 여부와 무관하게 클릭 이벤트에 의해 각 컨텍스트의 (B)가 실행될 때는 (B)의 outerEnvironmentReference가 (A) LexicalEnvironment를 참조하게 됩니다. 따라서 최소한 (B)함수가 참조할 예정인 변수 fruit 에 대해서는 (A)가 종료된 후에도 GC 대상에서 제외되어 계속 참조 가능할 것입니다.</p>
<p>그런데 (B)의 함수의 쓰임새가 콜백 함수에 국한되지 않는 경우라면 반복을 줄이기 위해 (B)를 외부로 분리하는 편이 낫겠습니다.</p>
<pre><code class="language-jsx">const fruits = [&#39;apple&#39;, &#39;banana&#39;, &#39;peach&#39;];
const $ul = document.createElement(&#39;ul&#39;);

const alertFruit = function (fruit) {
  alert(&#39;your choice is&#39; + fruit);
};

fruits.forEach(function (fruit) {
  const $li = document.createElement(&#39;li&#39;);
  $li.innerText = fruit;
  $li.addEventListener(&#39;click&#39;, alertFruit.bind(null,fruit));
  $ul.appendChild($li);
});

document.body.appendChild($ul);
alertFruit(fruits[1]);
</code></pre>
<p>bind 메서드로 값을 직접 넘겨주고 외부로 분리 (클로저x)
그냥 alertFruit를 넘기면 인자에 대한 제어권을 addEventListener가 가지게 되고, 첫번째 인자에 이벤트 객체를 주입하기 때문에 <code>[object MouseEvent]</code> 가 출력됩니다. 따라서 bind 메서드를 사용했습니다. 다만 이렇게 하면 이벤트 객체가 인자로 넘어오는 순서가 바뀌는 점 및 함수 내부에서의 this가 원래의 그것과 달라지는 점은 감안해야 합니다. 이런 점을 보완하기 위해 <strong>고차함수</strong> 를 활용할 수 있습니다.</p>
<pre><code class="language-jsx">const fruits = [&#39;apple&#39;, &#39;banana&#39;, &#39;peach&#39;];
const $ul = document.createElement(&#39;ul&#39;);

const alertFruitBuilder = function (fruit) {
    return function () {
      alert(&#39;your choice is&#39; + fruit);
    }
};

fruits.forEach(function (fruit) {
  const $li = document.createElement(&#39;li&#39;);
  $li.innerText = fruit;
  $li.addEventListener(&#39;click&#39;, alertFruitBuilder(fruit)
  $ul.appendChild($li);
});

document.body.appendChild($ul);
alertFruit(fruits[1]);</code></pre>
<p>alertFruitBuilder 함수를 실행하면서 fruit 값을 인자로 전달했습니다.</p>
<p>그러면 이 함수의 실행 결과가 다시 함수가 되며, 이렇게 반환된 함수를 리스너에 콜백 함수로써 전달할 것입니다. 이후 언젠가 클릭 이벤트가 발생하면 비로소 이 함수의 실행 컨텍스트가 열리면서 alertFruitBuilder의 인자로 넘어온 fruit를 outerEnvironmentReference에 의해 참조할 수 있겠죠. </p>
<p><code>alertFruitBuilder의 실행 결과로 반환된 함수에는 클로저가 존재합니다.</code></p>
<h3 id="접근-권한-제어-정보-은닉">접근 권한 제어 (정보 은닉)</h3>
<blockquote>
<p>정보 은닉은 어떤 모듈의 내부 로직에 대해 외부로의 노출을 최소화해서 모듈간의 결합도를 낮추고 유연성을 높이고자 하는 현대 프로그래밍 언어의 중요한 개념중 하나입니다.</p>
</blockquote>
<p>흔히 접근 권한에는 <code>public</code>, <code>private</code>, <code>protected</code> 세 종류가 있습니다.
<code>public</code>은 외부에서 접근 가능한 것이고, <code>private</code> 는 내부에서만 사용 가능하며 외부에 노출되지 않는 것을 의미합니다.</p>
<p>클로저를 이용하면 함수 차원에서 public한 값과 private한 값을 구분하는 것이 가능합니다.
바로 <code>return</code> 을 활용하면 됩니다.</p>
<p>외부에 제공하고자 하는 정보들을 모아서 <code>return</code> 하고, 내부에서만 사용할 정보들은 <code>return</code> 하지 않는 것으로 접근 권한 제어가 가능한 것입니다.</p>
<pre><code class="language-jsx">function createCounter() {
  // private 변수
  let count = 0;

  // public 함수
  function increment() {
    count++;
    console.log(count);
  }

  // 클로저: 외부에서는 increment만 접근 가능
  return increment;
}

const counter = createCounter();
counter(); // 출력 결과: 1
counter(); // 출력 결과: 2
</code></pre>
<p>이 예제에서 createCounter 함수는 클로저를 반환합니다. 클로저에는 count라는 private한 변수와 increment라는 public한 함수가 포함되어 있습니다. 외부에서는 count에 직접 접근할 방법이 없으며, 오직 increment 함수만을 통해서만 count를 조작할 수 있습니다. 이로써 count는 함수 외부로부터 감춰진(private) 상태가 되고, 함수 외부에서는 모듈의 일부로서 필요한 부분만을 노출할 수 있게 됩니다.</p>
<h3 id="부분-적용-함수">부분 적용 함수</h3>
<blockquote>
<p>부분 적용 함수란 n개의 인자를 받는 함수에 미리 m개의 인자만 남겨 기억시켰다가, 나중에 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수입니다.</p>
</blockquote>
<p>this를 바인딩해야 하는 점을 제외하면 앞서 살펴본 bind 메서드의 실행 결과가 바로 부분 적용 함수입니다.</p>
<h3 id="bind-메서드를-활용한-부분-적용-함수">bind 메서드를 활용한 부분 적용 함수</h3>
<pre><code class="language-jsx">const add = function () {
  const result = 0;
  for (var i = 0; i &lt; arguments.length; i++) {
    result += arguments[i];
  }
  return result;
};

const addPartial = add.bind(null, 1, 2, 3, 4, 5);
console.log(addPartial(6, 7, 8, 9, 10));            // 55
</code></pre>
<h4 id="부분-적용-함수-구현">부분 적용 함수 구현</h4>
<pre><code class="language-jsx">const partial = function () {
  const originalPartialArgs = arguments;
  const func = originalPartialArgs[0];
  if(typeof func !== &#39;function&#39;) {
    throw new Error(&#39;첫 번째 인자가 함수가 아닙니다.&#39;);
  }
  return function () {
    const partialArgs = Array.prototype.slice.call(originalPartialArgs, 1);
    const restArgs = Array.prototype.slice.call(arguments);
    return func.apply(this,partialArgs.concat(restArgs));
  };
};

const add = function () {
  const result = 0;
  for(let i = 0; i &lt; arguments.length; i++) {
    result += arguments[i];
  }
  return result;
};

const addPartial = partial(add, 1, 2, 3, 4, 5);
console.log(addPartial(6, 7, 8, 9, 10));                 // 55

const dog = {
  name: &#39;강아지&#39;,
  greet: partial(function(prefix, suffix) {
    return prefix + this.name + suffix;
  }, &#39;멍멍, &#39;)
};
dog.greet(&#39;입니다 !&#39;);                                    // 멍멍, 강아지입니다.</code></pre>
<p>첫 번째 인자에는 원본 함수를, 두 번째 인자 이후부터는 미리 적용할 인자들을 전달하고, 반환할 함수 <code>부분 적용 함수</code> 에서는 다시 나머지 인자들을 받아 이들을 한데 모아 <code>concat</code> 원본 함수를 호출<code>apply</code> 합니다. 또한 실행 시점의 this를 그대로 반영함으로써 this에는 아무런 영향을 주지 않게 됐습니다.</p>
<h3 id="디바운스">디바운스</h3>
<p>디바운스는 짧은 시간 동안 동일한 이벤트가 많이 발생할 경우 이를 전부 처리하지 않고 처음 또는 마지막에 발생한 이벤트에 대해 한 번만 처리하는 것으로, 프런트엔드 성능 최적화에 큰 도움을 주는 기능 중 하나입니다. scroll, wheel, mousemove, resize 등에 적용하기 좋습니다.</p>
<pre><code class="language-jsx">const debounce = function (eventName, func, wait) {
  const timeoutId = null;
  return function (event) {
    const self = this;
    console.log(eventName, &#39;event발생&#39;);
    clearTimeout(timeoutId);
    timeoutId = setTimeout(func.bind(self,event),wait);
  };
};

const moveHandler = function(e) {
  console.log(&#39;move event 처리&#39;);
};

const wheelHandler = function(e) {
  console.log(&#39;wheel event 처리&#39;);
};

document.body.addEventListener(&#39;mousemove&#39;, debounce(&#39;move&#39;, moveHandler,500));

document.body.addEventListener(&#39;mousewheel&#39;, debounce(&#39;wheel&#39;, wheelHandler,700));</code></pre>
<p>디바운스 함수에서 클로저로 처리되는 변수에는 <code>eventName</code>, <code>func</code>, <code>wait</code>, <code>timeoutId</code>가 있습니다.</p>
<h3 id="커링-함수">커링 함수</h3>
<p>커링 함수란 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것을 말합니다.</p>
<p>커링은 한 번에 하나의 인자만 전달하는 것을 원칙으로 합니다. 또한 중간 과정상의 함수를 실행한 결과는 그다음 인자를 받기 위해 대기만 할 뿐으로, 마지막 인자가 전달되기 전까지는 원본 함수가 실행되지 않습니다. </p>
<p>부분 적용 함수와는 다르게 여러 개의 인자를 전달할 수 있고, 실행 결과를 재실행 할 때 원본 함수가 무조건 실행됩니다.</p>
<pre><code class="language-jsx">const curry3 = function (func) {
  return function (a) {
    return function (b) {
      return func(a, b);
    };
  };
};

const getMaxWith10 = curry3(Math.max)(10);
console.log(getMaxWith10(8));                // 10
console.log(getMaxWith10(25));                // 25

const get getMinWith10 = curry3(Math.min)(10);
console.log(getMinWith(8));                    // 8
console.log(getMinWith(25));                // 10</code></pre>
<p>부분 적용 함수와 달리 커링 함수는 필요한 상황에 직접 만들어 쓰기 용이합니다. 필요한 인자 개수만큼 함수를 만들어 계속 리턴해 주다가 마지막에 조합해서 리턴해주면 됩니다. 다만 인자가 많아질수록 가독성이 떨어진다는 단점이 있습니다.</p>
<p>ES6에서는 화살표 함수를 써서 한 줄에 표기할 수 있습니다.</p>
<pre><code class="language-jsx">const curry5 = func =&gt; a =&gt; b =&gt; c =&gt; d =&gt; e =&gt; func(a, b, c, d, e);</code></pre>
<p>각 단계에서 받은 인자들을 모두 마지막 단계에서 참조할 것이므로 GC되지 않고 메모리에 차곡차곡 쌓였다가, 마지막 호출로 실행 컨텍스트가 종료된 후에야 비로소 한꺼번에 GC의 수거 대상이 됩니다.</p>
<p>커링함수가 유용할 때가 있는데, 당장 필요한 정보만 받아서 전달하고 또 필요한 정보가 들어오면 전달하는 식으로 하면 결국 마지막 인자가 넘어갈 때까지 함수 실행을 미루는 셈이 됩니다. 이를 함수형 프로그래밍에서는 지연실행이라고 칭합니다.</p>
<hr>
<h2 id="정리">정리</h2>
<blockquote>
<p><em>클로저란 어떤 함수에서 선언한 변수를 참조하는 내부함수를 외부로 전달할 경우, 함수의 실행 컨텍스트가 종료된 후에도 해당 변수가 사라지지 않는 현상입니다.</em></p>
</blockquote>
<p><em>내부함수를 외부로 전달하는 방법에는 함수를 return 하는 경우뿐 아니라 콜백으로 전달하는 경우도 포함됩니다.</em></p>
<blockquote>
</blockquote>
<p><em>클로저는 그 본질이 메모리를 계속 차지하는 개념이므로 더는 사용하지 않게 된 클로저에 대해서는 메모리를 차지하지 않도록 관리해줄 필요가 있습니다.</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript - this]]></title>
            <link>https://velog.io/@y__baam/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-this</link>
            <guid>https://velog.io/@y__baam/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-this</guid>
            <pubDate>Thu, 16 Nov 2023 04:07:01 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트에서 this 는 어디서든 사용할 수 있습니다.
상황에 따라 this 가 바라보는 대상이 달라지는데, 어떤 이유로 그렇게 되는지 파악하기 힘든 경우도 있고 예상과 다르게 엉뚱한 대상을 바라보는 경우도 있습니다.</p>
<p>함수와 객체의 구분이 느슨한 자바스크립트에서 this 는 실질적으로 이 둘을 구분하는 거의 유일한 기능입니다.
상황별로 this 가 어떻게 달라지는지, 왜 그렇게 되는지, 예상과 다른 대상을 바라보고 있을 경우 그 원인을 효과적으로 추적하는 방법을을 알아보겠습니다.</p>
<h2 id="상황에-따라-달라지는-this">상황에 따라 달라지는 this</h2>
<p>자바스크립트에서 this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정됩니다. 실행 컨텍스트는 함수를 호출할 때 생성되므로, 바꿔 말하면 this는 함수를 호출할 때 결정된다고 볼 수 있습니다. 함수를 어떤 방식으로 호출하느냐에 따라 값이 달라지는 것입니다.</p>
<h3 id="전역-공간에서의-this">전역 공간에서의 this</h3>
<p>전역 공간에서 this는 전역 객체를 가리킵니다. 개념상 전역 컨텍스트를 생성하는 주체가 바로 전역 객체이기 때문입니다. 전역 객체는 자바스크립트 런타임 환경에 따라 다른 이름과 정보를 가지고 있습니다. 브라우저 환경에서 전역 객체는 window 이고 Node.js 환경에서는 global입니다.
전역 변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당합니다.</p>
<pre><code class="language-jsx">// 브라우저
console.log(this); // window

// node.js
console.log(this); // global</code></pre>
<h3 id="메서드로서-호출할-때-그-메서드-내부에서의-this">메서드로서 호출할 때 그 메서드 내부에서의 this</h3>
<h4 id="함수-vs-메서드">함수 vs 메서드</h4>
<p>프로그래밍 언어에서 함수와 메서드는 미리 정의한 동작을 수행하는 코드 뭉치로, 이 둘을 구분하는 유일한 차이는 독립성에 있습니다.</p>
<p>함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행합니다. 자바스크립트는 상황별로 this 키워드에 다른 값을 부여하게 함으로써 이를 구현했습니다</p>
<pre><code class="language-jsx">const func = function(x) {
    console.log(this, x);
};

const obj = {
    method: func
};

func(1);                    // Window { ... } 1     함수 호출
obj.method(2);                // { method: f } 2        메서드 호출
</code></pre>
<p>obj 의 method 프로퍼티에 할당한 값과 func 변수에 할당한 값은 모두 1번째 줄에서 선언한 함수를 참조합니다. 이를 변수에 담아 호출한 경우와 obj 객체의 프로퍼티에 할당해서 호출한 경우 this 가 달라지는 것입니다.</p>
<p>함수 호출과 메서드 호출 어떻게 구분할까요?
함수 앞에 점( .)이 있는지 여부만으로 간단하게 구분할 수 있습니다.
func(1) 은 앞에 점이 없으므로 함수로서 호출한 것이고
obj.method 에서 method 앞에 점이 있으니 메서드로서 호출한 것입니다.</p>
<h4 id="메서드-내부에서의-this">메서드 내부에서의 this</h4>
<p>this에는 호출한 주체에 대한 정보가 담깁니다. 어떤 함수를 메서드로서 호출하는 경우 호출 주체는 바로 함수명 앞의 객체입니다.</p>
<pre><code class="language-jsx">const obj = {
  methodA: function () {
    console.log(this);
  },
  inner: {
    methodB: function() {
      console.log(this);
    }
  }
};

obj.methodA();                // { methodA: f, inner {...} }
obj.inner.methodB();        // { methodB: f }

</code></pre>
<h3 id="함수로서-호출할-때-그-함수-내부에서의-this">함수로서 호출할 때 그 함수 내부에서의 this</h3>
<h4 id="함수-내부에서의-this">함수 내부에서의 this</h4>
<p>어떤 함수를 함수로서 호출할 경우 this가 지정되지 않습니다. this에는 호출한 주체에 대한 정보가 담깁니다. 그런데 함수로서 호출하는 것은 호출 주체를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이기 때문에 호출 주체의 정보를 알 수 없는 것입니다. 실행 컨텍스트를 활성화 할 당시에 this가 지정되지 않을 경우 this는 전역 객체를 바라봅니다. 따라서 함수에서의 this는 전역 객체를 가리킵니다.</p>
<h4 id="메서드의-내부함수에서의-this">메서드의 내부함수에서의 this</h4>
<p>우리는 이미 어떤 함수를 메서드로서 호출할 때와 함수로서 호출할 때 this가 무엇을 가리키는지를 알고 있습니다. 내부함수 역시 이를 함수로서 호출했는지 메서드로서 호출했는지만 파악하면 this의 값을 정확히 맞출 수 있습니다.</p>
<pre><code class="language-jsx">const obj1 = {
  outer: function () {
    console.log(this);                    // (1)
    const innerFunc = function () {
      console.log(this);                // (2) (3)
    }
    innerFunc();

    const obj2 = {
      innerMethod: innerFunc
    };
    obj2.innerMethod();
  }
};
obj1.outer();</code></pre>
<p>(1): obj1, (2): 전역객체(Window), (3):obj2입니다.</p>
<h4 id="메서드의-내부-함수에서의-this를-우회하는-방법">메서드의 내부 함수에서의 this를 우회하는 방법</h4>
<pre><code class="language-jsx">const obj = {
    outer: function() {
        console.log(this);                    // { outer: f }
        const innerFunc1 = function() {
            console.log(this);                // Window { ... }
        }
        innerFunc1();

        const self = this;
        const innerFunc2 = function() {
            console.log(self);                // { outer: f }
        }
        innerFunc2();
    }
}
obj.outer();
</code></pre>
<p>innerFunc1 내부에서 this는 전역객체를 가리킵니다. 한편 outer 스코프에서 self 라는 변수에 this 를 저장한 상태에서 호출한 innerFunc2 의 경우 self에는 객체 obj 가 출력됩니다.</p>
<h4 id="this를-바인딩-하지-않는-함수">this를 바인딩 하지 않는 함수</h4>
<p>ES6에서는 함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자, this를 바인딩 하지 않는 화살표 함수를 새로 도입했습니다. 화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용할 수 있습니다. </p>
<blockquote>
<p><strong><em>내부함수를 화살표 함수로 바꾸면 우회법이 불필요해집니다.</em></strong></p>
</blockquote>
<pre><code class="language-jsx">const obj = {
  outer: function() {
    console.log(this);            // { outer: f }
    const innerFunc = () =&gt; {
      console.log(this);        // { outer: f }
    };

    innerFunc();
  }
};
obj.outer();
</code></pre>
<p>그 밖에도 call, apply 등의 메서드를 활용해 함수를 호출할 때 명시적으로 this를 지정하는 방법이 있습니다.</p>
<h3 id="콜백-함수-호출-시-그-함수-내부에서의-this">콜백 함수 호출 시 그 함수 내부에서의 this</h3>
<p>함수 A의 제어권을 다른 함수 B에게 넘겨주는 경우 함수 A를 콜백 함수라 합니다. 이때 함수 A는 함수 B의 내부 로직에 따라 실행되며, this 역시 함수 B 내부로직에서 정한 규칙에 따라 값이 결정됩니다.</p>
<p>콜백 함수도 함수이기 때문에 기본적으로 this가 전역객체를 참조하지만, 제어권을 받은 함수에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조하게 됩니다.</p>
<pre><code class="language-jsx">setTimeout(function() { console.log(this); }, 300);

[1, 2, 3, 4, 5].forEach(function(x) {
  console.log(this, x);
});

document.body.innerHTML += &#39;&lt;button id=&quot;a&quot;&gt;클릭&lt;/button&gt;&#39;;
document.body.querySelector(&#39;#a&#39;)
  .addEventListener(&#39;click&#39;, function (e) { console.log(this, e); });
</code></pre>
<p>setTimeout 함수와 forEach 메서드는 그 내부에서 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않습니다. 따라서 콜백 함수 내부에서의 this는 전역객체를 참조합니다. 한편 addEventListener 메서드는 콜백 함수를 호출할 때 자신의 this를 상속하도록 정의돼 있습니다. </p>
<p>그러니까 메서드명의 점(.) 앞부분이 곧 this가 되는 것이죠.</p>
<p>콜백 함수의 제어권을 가지는 함수(메서드)가 콜백 함수에서의 this를 무엇으로 할지를 결정하며, 특별히 정의하지 않은 경우에는 기본적으로 함수와 마찬가지로 전역객체를 바라봅니다.</p>
<h3 id="생성자-함수-내부에서의-this">생성자 함수 내부에서의 this</h3>
<p>생성자 함수를 호출하면 우선 생성자의 prototype 프로퍼티를 참조하는 <strong>proto</strong> 라는 프로퍼티가 있는 객체(인스턴스)를 만들고, 미리 준비된 공통 속성 및 개성을 해당 객체(this)에 부여합니다. 이렇게 해서 구체적인 인스턴스가 만들어집니다.</p>
<pre><code class="language-jsx">const Cat = function(name, age) {
  this.bark = &#39;야옹&#39;
  this.name = name;
  this.age = age;
}
const cho = new Cat(&#39;쵸&#39;, 7);

console.log(cho);
// Cat { bark : &#39;야옹&#39;, name: &#39;쵸&#39;, age: 7 }</code></pre>
<p>Cat 이란 변수에 익명 함수를 할당했습니다. 이 함수는 내부에서 this에 접근해서 bark, name, age 프로퍼티에 각각 값을 대입합니다. new 명령어와 함께 Cat 함수를 호출해서 변수 cho 에 할당했습니다. 출력해보니 Cat 클래스의 인스턴스 객체가 출력됩니다. 즉 생성자 함수 내부에서의 this 는 cho 를 가리킴을 알 수 있습니다.</p>
<hr>
<h2 id="명시적으로-this를-바인딩-하는-방법">명시적으로 this를 바인딩 하는 방법</h2>
<h3 id="call--apply">call / apply</h3>
<h4 id="call-메서드">call 메서드</h4>
<pre><code class="language-jsx">Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
</code></pre>
<p>call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령입니다.</p>
<p>call 메서드의 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 합니다.</p>
<pre><code class="language-jsx">const func = function(a, b, c) {
  console.log(this, a, b, c);
};

func(1, 2, 3)                        // Window { ... } 1 2 3
func.call({ x: 1 },4, 5, 6);            // { x: 1 } 4 5 6
</code></pre>
<blockquote>
<p>함수를 그냥 실행하면 this는 전역객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있습니다.</p>
</blockquote>
<h4 id="apply-메서드">apply 메서드</h4>
<pre><code class="language-jsx">Function.prototype.apply(thisArg, [, argsArray])</code></pre>
<p>apply 메서드는 call 메서드와 기능적으로 완전히 동일합니다.
다만 apply 메서드는 <strong>두 번째 인자를 배열</strong>로 받아 그 <strong>배열의 요소들을 호출할 함수의 매개변수</strong>로 지정한다는 점에서만 차이가 있습니다.</p>
<h4 id="생성자-내부에서-다른-생성자-호출">생성자 내부에서 다른 생성자 호출</h4>
<p>생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하면 간단하게 반복을 줄일 수 있습니다.</p>
<pre><code class="language-jsx">function Person(name, gender) {
  this.name = name
  this.gender = gender
}

function Student(name, gender, school) {
  Person.call(this,name,gender);
  this.school = school;
}

function Employee(name, gender, company) {
  Person.apply(this,[name, gender]);
  this.company = company;
}

const a = new Student (&#39;a&#39;, &#39;female&#39;, &#39;서울대&#39;);
const b = new Employee (&#39;b&#39;, &#39;male&#39;, &#39;네이버&#39;);
</code></pre>
<h3 id="bind">bind</h3>
<pre><code class="language-jsx">Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])</code></pre>
<p>ES5에서 추가된 기능으로, call과 비슷하지만 즉시 호출하지는 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드입니다.</p>
<p>다시 새로운 함수를 호출할 때 인수를 넘기면 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록됩니다. 즉 bind 메서드는 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 모두 지닙니다.</p>
<pre><code class="language-jsx">const func = function(a, b, c, d) {
  console.log(this, a, b, c, d);
};

func(1, 2, 3, 4);                    // Window{ ... } 1 2 3 4

const bindFunc1 = func.bind({ x : 1 });
bindFunc1(5, 6, 7, 8);                // { x: 1 } 5 6 7 8

const bindFunc2 = func.bind({ x : 1 }, 4, 5);
bindFunc2(6, 7);                    // { x: 1 } 4 5 6 7
bindFunc2(8, 9);                    // { x: 1 } 4 5 8 9
</code></pre>
<h3 id="화살표-함수">화살표 함수</h3>
<p>ES6에 새롭게 도입된 화살표 함수는 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외됐습니다. 즉 이 함수 내부에는 this가 아예 없으며, 접근하고자 하면 스코프체인상 <strong>가장 가까운 this에 접근</strong>하게 됩니다.</p>
<pre><code class="language-jsx">const obj = {
  outer: function() {
    console.log(this);                // { outer: f }
    const innerFunc = () =&gt; {        
      console.log(this);            // { outer: f }
    };
    innerFunc();
  }
}
obj.outer();</code></pre>
<h3 id="별도의-인자로-this를-받는경우">별도의 인자로 this를 받는경우</h3>
<p>콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체를 인자로 지정할 수 있는 경우가 있습니다. 이러한 메서드의 thisArg값을 지정 하면 콜백 함수 내부에서 this 값을 원하는 대로 변경할 수 있습니다.</p>
<p>이런 형태는 여러 내부 요소에 대해 같은 동작을 반복 수행해야하는 <strong>배열 메서드</strong>에 많이 포진돼 있으며, 같은 이유로 ES6에서 새로 등장한 Set, Map 등의 메서드에도 일부 존재합니다.
배열 메서드인 forEach의 예를 살펴보겠습니다.</p>
<pre><code class="language-jsx">const report = {
  sum: 0,
  count: 0,
  add: function() {
    const args = Array.prototype.slice.call(arguments);
    args.forEach(function (entry) {
      this.sum += entry;
      ++this.count;
    }, this);
  },

  average: function() {
    return this.sum / this.count;
  }
};
report.add(60, 85, 95);
console.log(report.sum, report.count, report.average());     // 240 3 80
</code></pre>
<p>report 객체에는 sum, count 프로퍼티가 있고, add, average 메서드가 있습니다.</p>
<p>add 메서드는 arguments를 배열로 변환해서 args 변수에 담고, 이 배열을 순회하면서 콜백 함수를 실행하는데, 이때 콜백 함수 내부에서의 this는 forEach 함수의 두 번째 인자로 전달해준 this가 바인딩 됩니다.</p>
<p>60, 85, 95를 인자로 삼아 add 메서드를 호출하면 이 세 인자를 배열로 만들어 forEach 메서드가 실행됩니다. 콜백 함수 내부에서의 this는 add 메서드에서의 this가 전달된 상태이므로 add 메서드의 this(report)를 그대로 가리키고 있습니다. 따라서 배열의 세 요소를 순회하면서 report.sum 값 및 report.count 값이 차례로 바뀌고, 순회를 마친 결과 report.sum에는 240이, report.count 에는 3이 담기게 됩니다.</p>
<h2 id="정리">정리</h2>
<blockquote>
</blockquote>
<h4 id="다음-규칙은-명시적-this-바인딩이-없는-한-늘-성립합니다">다음 규칙은 명시적 this 바인딩이 없는 한 늘 성립합니다.</h4>
<blockquote>
</blockquote>
<ul>
<li><em>전역공간에서의 this는 전역객체를 참조합니다.</em></li>
<li><em>어떤 함수를 메서드로서 호출한 경우 this는 메서드 호출 주체를 참조합니다.</em></li>
<li><em>어떤 함수를 함수로서 호출한 경우 this는 전역객체를 참조합니다. 메서드 내부함수에서도 같습니다.</em></li>
<li><em>콜백 함수 내부에서의 this는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않은 경우에는 전역객체를 참조합니다.</em></li>
<li><em>생성자 함수에서의 this는 생성될 인스턴스를 참조합니다.</em><blockquote>
</blockquote>
<h4 id="다음은-명시적-this-바인딩입니다">다음은 명시적 this 바인딩입니다.</h4>
</li>
<li><em>call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출합니다.</em></li>
<li><em>bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만듭니다.</em></li>
<li><em>요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 합니다.</em></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Event Loop, Call Stack, Task Queue의 역할과 동작 원리]]></title>
            <link>https://velog.io/@y__baam/Event-Loop-Call-Stack-Task-Queue%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@y__baam/Event-Loop-Call-Stack-Task-Queue%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Thu, 08 Jun 2023 06:10:05 GMT</pubDate>
            <description><![CDATA[<p>이벤트 루프(Event Loop), 호출 스택(Call Stack), 태스크 큐(Task Queue)는 JavaScript와 같은 프로그래밍 언어에서 비동기 작업을 처리하기 위해 함께 동작하는 요소이다. </p>
<p>이 세 가지 요소는 비동기 작업을 조율하고 실행하는 데 중요한 역할을 한다.</p>
<h3 id="콜-스택call-stack">콜 스택(Call Stack):</h3>
<p>Call Stack은 함수 호출의 실행 컨텍스트를 관리하는 자료구조이다.</p>
<p>함수가 호출되면 해당 함수의 실행 컨텍스트가 Call Stack에 추가된다.
함수가 실행을 완료하면 Call Stack에서 제거된다. 
이 때, 함수 안에서 다른 함수를 호출하면 새로운 실행 컨텍스트가 Call Stack에 추가되고, 호출된 함수의 실행이 완료되면 이전 실행 컨텍스트로 돌아간다.
Call Stack은 싱글 스레드 환경에서 동작하며, 함수의 실행 순서를 추적한다.</p>
<h3 id="태스크-큐task-queue">태스크 큐(Task Queue):</h3>
<p>태스크 큐는 선입선출 방식(FIFO)으로 실행된다.</p>
<p>태스크 큐는 비동기 작업의 완료 또는 이벤트 발생과 같은 작업을 대기하는 큐이다. 
비동기 작업이 발생하면 해당 작업은 태스크 큐에 넣어진다. 대표적인 비동기 작업으로는 타이머, 이벤트 처리, 비동기 함수 호출 등이 있다.
태스크 큐는 Call Stack이 비어있을 때 비동기 작업을 순서대로 Call Stack으로 이동시키는 역할을 한다.</p>
<h3 id="이벤트-루프event-loop">이벤트 루프(Event Loop):</h3>
<p>이벤트 루프는 Call Stack과 태스크 큐를 모니터링하고, Call Stack이 비어있을 때 태스크 큐에 있는 작업을 Call Stack으로 이동시킨다. 
이벤트 루프는 Call Stack이 비어질 때마다 태스크 큐를 확인하고 작업을 호출 스택으로 옮기는 역할을 반복적으로 수행. 
이를 통해 비동기 작업이 순차적으로 실행되고, 동시에 다른 작업을 처리할 수 있다.</p>
<hr>
<h4 id="일반적인-비동기-작업-흐름은-다음과-같다">일반적인 비동기 작업 흐름은 다음과 같다.</h4>
<ol>
<li>동기 코드가 Call Stack에 추가</li>
<li>비동기 작업이 발생하면 해당 작업은 백그라운드에서 실행되고, 그 결과는 태스크 큐에 추가</li>
<li>Call Stack이 비어지면 이벤트 루프가 태스크 큐를 확인하고 작업을 호출 스택으로 이동</li>
<li>Call Stack에 추가된 작업이 실행되고, 이러한 과정 반복</li>
</ol>
<p>이러한 방식으로 Call Stack, 태스크 큐, 이벤트 루프가 협력하여 비동기 작업을 처리하고 원활한 실행 한다.</p>
]]></description>
        </item>
    </channel>
</rss>