[JS] 핵심 개념 및 문법(원시-참조자료형/스코프/클로저)

Updated:

Categories:

Tags: ,

📌 개인적인 공간으로 공부를 기록하고 복습하기 위해 사용하는 블로그입니다.
정확하지 않은 정보가 있을 수 있으니 참고바랍니다 :😸
[틀린 내용은 댓글로 남겨주시면 복받으실거에요]

원시자료형과 참조자료형

원시자료형 VS 참조자료형

JavaScript에서 자료형(type)이란 값(value)의 종류

자료형은 크게 두 가지로 구분

원시 자료형(primitive type)참조 자료형(reference type)

  1. 원시자료형
    1. Number, boolean, undefined, null, string, symbol이 여기에 해당됨.
    2. 원시 자료형은 변경 불가능한 값(immutable value) 즉, 한 번 생성된 원시 자료형은 읽기 전용(read only) 값이다.
    3. stack
      1. 변수 선언 시 stack에 할당 됨.

      2. 변수에 값을 할당하면 값이 저장됨. 변수에 할당하면 메모리 공간에 값 자체가 저장

      3. 원시형 stack에 할당

      4. 원시형을 재할당 할 경우 아래와 같이 변수와 값은 다른 곳에 저장되고 기존 변수는 사라지고 값만 남게 됨

        • 참조할 변수가 없어서 값을 불러낼 수 없음. 사용하지 않아서 나중에 가비지컬렉터에 의해 삭제 됨.
      5. 원시값 자체 복사

        1
        2
        
         let num = 20;
         let copiedNum = num;
        
        • 원시 자료형은 값 자체가 복사됨. 즉, 변수 num과 변수 copiedNum은 동일하게 20

  2. 참조자료형
    1. 원시 자료형이 아닌 모든 자료형은 참조 자료형
    2. 여러 데이터를 한 번에 다룰 수 있는 배열, 객체가 대표적인 참조 자료형, 함수도 참조 자료형
    3. stack
      1. 참조형인 배열 선언 및 할당
      2. 참조 자료형을 변수에 할당하면 메모리 공간에 주소값이 저장

      3. 다양한 참조형

        • d.pop()에 의해 배열의 마지막 요소가 삭제되었음.
    4. 참조 값을 갖는 변수를 다른 변수에 할당하면 주소 값이 복사되어 전달
      1. 배열의 요소 각각이 하나의 값이기 때문에 하나의 공간에 배열 자체를 저장하는 것은 불가능
      2. 힙에 참조 자료형을 저장한 후 저장 공간을 참조할 수 있는 주소 값을 변수에 저장, 변수는 stack에 저장됨.

    5. 참조 자료형은 변경이 가능한 값(mutable value)
    6. 참조 자료형을 복사할 경우 = 같은 주소 값을 가르킴

      1
      2
      
       let arr = [0, 1, 2, 3];
       let copiedArr = arr;
      

얕은 복사와 깊은 복사

summary

  • 원시 자료형이 할당된 변수를 다른 변수에 할당하면 값 자체의 복사가 일어남, 따라서 원본과 복사본 중 하나를 변경해도 다른 하나에 영향을 미치지 않음
  • 참조 자료형이 할당된 변수를 다른 변수에 할당하면 주소가 복사되어 원본과 복사본이 같은 주소를 참조
  • 참조 자료형의 주소값을 복사한 변수에 요소를 추가하면 같은 주소를 참조하고 있는 원본에도 영향을 미친다.
  • 참조 자료형이 저장된 변수를 다른 변수에 할당할 경우, 두 변수는 같은 주소를 참조하고 있을 뿐 값 자체가 복사되었다고 볼 수 없다. 얕은 복사(shallow copy)
  • 절대 =을 통해서 복사하면 안됨!!!!! 참조형은 = 으로 할당 절대 하지말고 새로 만드는 것을 권장.

  1. 배열 복사 : slice() 메서드 또는 spread syntax 등의 방법 =얕은복사
    1. slice()

      1
      2
      3
      4
      
       let arr = [0, 1, 2, 3];
       let copiedArr = arr.slice();
       console.log(copiedArr); // [0, 1, 2, 3]
       console.log(arr === copiedArr); // false
      
      1. 결과

    2. spread syntax
      1. spread syntax는 ES6에서 새롭게 추가된 문법으로, spread라는 단어의 뜻처럼 배열을 펼칠 수 있음

        1
        2
        3
        
         let arr = [0, 1, 2, 3];
                    
         console.log(...arr); // 0 1 2 3
        
    3. for문으로 복사 - 깊은 복사 가능
      1. 일차원

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        
         let arr =[1,2,3,4,5,6,7,8,9,10];
         let arr2=[];
         for(let i=0;i<arr.length;i++){
             arr2.push(arr[i]);
         }
         console.log("두 배열의 상태 :");
         console.log(arr);
         console.log(arr2);
                    
         arr.pop();
         console.log("arr.pop() 이후 두 배열의 상태 :");
         console.log(arr);
         console.log(arr2);
        

      2. 다차원 - 얕은 복사

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        
         let arr =[[1,2,3],[4,5,6],[7,8,9],[10,11,12]];
         let arr2=[];
         for(let i=0;i<arr.length;i++){
             arr2.push(arr[i]);
         }
                    
         console.log("두 배열의 상태 :");
         console.log(arr);
         console.log(arr2);
                    
         arr2[2].pop();
         console.log("arr.pop() 이후 두 배열의 상태 :");
         console.log(arr);
         console.log(arr2);
        

        • 다차원 배열은 배열 내 존재하는 배열은 주소값을 참조만 하게 됨.
        • 이럴 경우 이중반복문 으로 해결 가능.
  2. 객체 복사 : Object.assign() 또는 spread syntax 등의 방법으로 복사 = 얕은복사
    1. Object.assign()

      1
      2
      3
      4
      5
      
       let obj = { firstName: "coding", lastName: "kim" };
       let copiedObj = Object.assign({}, obj);
              
       console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
       console.log(obj === copiedObj) // false
      
    2. spread sytax

      1. spread syntax는 배열뿐만 아니라 객체를 복사할 때도 사용가능
      1
      2
      3
      4
      5
      
       let obj = { firstName: "coding", lastName: "kim" };
       let copiedObj = {...obj};
              
       console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
       console.log(obj === copiedObj) // false
      
  3. 깊은 복사 : 대표적인 JSON.stringify()JSON.parse()를 사용하는 방법이 있지만, 예외의 케이스가 존재 ( 참조자료형에 함수가 있는 경우)
  4. 완전한 깊은 복사를 반드시 해야 하는 경우, node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 사용하면 됨.


스코프

  • 예제1.

  • 예제 2


스코프의 정의와 규칙

  • 바깥 쪽에 선언한 변수는 안쪽 스코프에서 사용 가능
  • 안쪽 스코프에서 선언한 변수는 바깥쪽에서 사용 불가능
  1. 변수 접근 규칙에 따른 유효범위
    1. 안쪽 스코프에서 바깥쪽 스코프로는 접근할 수 있지만 반대는 불가능
    2. 스코프는 중첩가능
    3. 가장 바깥의 스코프는 특별히 전역 스코프 (Global scope)라고 부름 ↔ 지역스코프(Local scope)

    4. 지역변수는 전역변수보다 더 높은 우선순위를 가짐.

      //김코딩 , 박해커 , 김코딩

  2. 스코프의 종류
    1. block scope : 중괄호로 둘러싼 범위
    2. fuction scope: 함수로 둘러싼 범위
    3. var 키워드는 블록 스코프를 무시하고 함수 스코프만 따름

  3. var/let/const
    1. var
      1. 블록스코프는 무시하고 함수스코프를 따름
      2. 재선언을 해도 에러내지 않음 = 버그유발
      3. var로 선언된 전역변수 및 전역함수는 window 객체에 속하게 됨
        • 브라우저에는 window라는 객체가 존재
        • 브라우저 창을 대표하는 객체지만 브라우저 창과 관계없이 전역항목도 담고 있음
    2. let
      1. 블록스코프를 따름
      2. 재선언 방지
    3. const
      1. 블록스코프를 따름
      2. 값을 새롭게 할당할 일이 없다면 const키워드 사용 권장
      3. 값을 재할당하는 경우 TypeError를 냄

클로저

클로저 : 외부 함수의 변수에 접근할 수 있는 내부 함수를 클로저 함수

MDN의 클로저 정의

“함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.”

  • 클로저는 함수와 그 함수 주변의 상태의 주소 조합이다.
  • 클로저의 함수는 어디에서 호출되느냐와 무관하게 선언된 함수 주변 환경에 따라 접근할 수 있는 변수가 정해진다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    const globalVar = '전역 변수';
      
    function outerFn() {
    const outerFnVar = 'outer 함수 내의 변수';
    const innerFn = function() **{**
        return 'innerFn은 ' + outerFnVar + '' + globalVar + '에 접근할 수 있습니다.';
    **}**
        return innerFn;
    }
      
    const innerFnOnGlobal =outerFn();
    const message = innerFnOnGlobal();
    console.log(message)
    //'innerFn은 outer 함수 내의 변수와 전역 변수에 접근할 수 있습니다.'라는 문자열이 리턴
    

​ InnerFn();이 아닌 innerFn;을 리턴하여 실행을 지연시킨후

​ 변수에 함수 자체를 담고 있음 ⇒ 실행가능

​ 결국 안에있는 함수를 밖으로 끌 어 온 것.

​ 그래서 js에서도 캡슐화를 구현하는 것. 외부와 단절시킴.

​ js에서는 변수에 함수를 담을 수 있고 변수를 실행하면 함수를 실행할 수 있다

  1. 클로저 활용
    1. 데이터를 보존하는 함수

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      
              function createFoodRecipe (foodName) {
              let ingredient1 = '탄산수';
              let ingredient2 = '위스키';
              const getFoodRecipe = function () {
                  return `${ingredient1} + ${ingredient2} = ${foodName}!`;
              }
              return getFoodRecipe;
              }
                     
              const recipe = createFoodRecipe('하이볼');
              recipe(); // '탄산수 + 위스키 = 하이볼!'
      

highballRecipe 함수는 문자열 ‘하이볼’을 보존하고 있어서 (=주소값을 보고있어서)

​ 전달인자를 추가로 전달할 필요가 없고, 다양한 하이볼 레시피를 하나의 함수로 제작할 수 있음

  1. 커링

    1) 커링은 여러 전달인자를 가진 함수함수를 연속적으로 리턴하는 함수로 변경하는 행위

    2) 예제1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    ```jsx
      function sum(a, b) {
         return a + b;
         }
       
         function currySum(a) {
             const innerSum = function(b) {
                 return a + b;
             };
             return innerSum;
         }
       
         console.log(sum(10, 20) === currySum(10)(20)) // true
    ```
    

    3) 예제 2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
    ```jsx
    function makePancake(powder) {
    return function (sugar) {
            return function (pan) {
                return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`;
            }
        }
    }
       
    // 위 함수는 아래 기존 패턴의 함수와 같은 코드입니다.
    function makePancake(powder) {
    let innerFn1 = function (sugar) {
            let innerFn2 = function (pan) {
                return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`;
            }
            return innerFn2;
        }
        return innerFn1;
    }
       
    const addSugar = makePancake('팬케이크가루');
    const cookPancake = addSugar('백설탕');
    const morningPancake = cookPancake('후라이팬');
       
    // 잠깐 낮잠 자고 일어나서 ...
    const lunchPancake = cookPancake('후라이팬');
    ```
    
    ```jsx
    function makePancakeAtOnce (powder, sugar, pan) {
    return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`;
    }
       
    const morningPancake = makePancakeAtOnce('팬케이크가루', '백설탕', '후라이팬')
    // 잠깐 낮잠 자고 일어나서 만든 팬케이크를 표현할 방법이 없다.
    

comment

클로저랑 커링함수는 대충 뭔가 알것 같은데 실제로 코드를 작성해보라고 하면 못할 것 같다…

프론트 쪽은 수업기간이 짧아서 그런지 할 건 많은데 호다닥 지나가는 것 같아서 이걸 공부를 제대로 혼자라도 알고 넘어가야할지

자바쪽을 더 공부해야할지 고민이 되는데 이렇게 짧은데 복습이라도 조금하면 안될 것 같아서

다른 것 보다 클로저함수는 더 보고 지나가야 할 것 같다..!

javascript 카테고리 내 다른 글 보러가기

Leave a comment