13. 자바스크립트 자료형/ 스코프/ 클로저

Lia·2023년 4월 26일
0

자료형

원시 자료형

  • 할당될 때는 변수에 값(value) 자체가 담김 (number, string, boolean, undefined, null, symbol)
  • 원시 값을 갖는 변수를 다른 변수에 할당하면 원시 값 자체가 복사되어 전달
    -원시 자료형은 변경 불가능한 값(immutable value)이다. 즉, 한 번 생성된 원시 자료형은 읽기 전용(read only) 값
    a = 10
    b = a
    a = 20
    print(b) # 10 출력
  • 원시 자료형은 변경 불가능한 값(immutable value)이다. 즉, 한 번 생성된 원시 자료형은 읽기 전용(read only) 값

참조 자료형

  • 할당될 때는 보관함의 주소(reference)가 담긴다는 개념 (배열, 객체가 대표적인 참조 자료형 +함수)

  • 참조 값을 갖는 변수를 다른 변수에 할당하면 주소값이 복사되어 전달

  • 참조 자료형은 변경이 가능한 값(mutable value)

    똑같은 요소와 프로퍼티를 가지지만 원본과 복사본이 서로 영향을 미치지 않도록 할 수는 없을까?
    밑에서 확인해보자

복사하기

배열 복사하기

  • slice()사용

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

    새롭게 생성된 배열은 원본 배열과 같은 요소를 갖지만 참조하고 있는 주소는 다름
    주소가 다르기 때문에 복사한 배열에 요소를 추가해도 원본 배열에는 추가되지 않음

    copiedArr.push(4);
    console.log(copiedArr); // [0, 1, 2, 3, 4]
    console.log(arr); // [0, 1, 2, 3]
  • spread syntaxn 문법 사용
    배열이 할당된 변수명 앞에 ...을 붙여주면 배열이 펼쳐침

    let arr = [0, 1, 2, 3];
    
    console.log(...arr); // 0 1 2 3

    새로운 배열 안에 원본 배열을 펼쳐서 전달하면 원본 배열과 같은 요소를 가지고 있지만 각각 다른 주소를 참조하게 됨 결과적으로 slice() 메서드와 동일함.

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

객체 복사하기

  • Object.assign()을 사용

    let obj = { firstName: "coding", lastName: "kim" };
    let copiedObj = Object.assign({}, obj);
    
    console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
    console.log(obj === copiedObj) // false
  • spread syntax 문법 사용

    let obj = { firstName: "coding", lastName: "kim" };
    let copiedObj = {...obj};
    
    console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
    console.log(obj === copiedObj) // false

    이런 복사들은 배열안에 배열이 있는 즉, 참조자료형 내부에 존재하는 참조자료형이 중첩된 구조들은 가장 외부에있는 참조자료형만 복사함. 이것을 얕은복사 라고함. 음. . 예를들자면
    변수 a,b는 다른 주소를 참조하지만 a와 b가 참조하는 배열 요소들은 같은 참조를 가지게 됨. 따라서, 내부에 존재하는 참조자료형은 같은 주소를 참조함.

    let a = [[1,2,3],[4,5,6]]; //참조자료형 내부에 존재하는 참조자료형이 중첩된 구조임.
    let b = [...a]; 
    console.log(a === b); // false
    console.log(a[0] === b[0]); // true

깊은복사

참조 자료형 내부에 중첩되어 있는 모든 참조 자료형을 복사하는 것은 깊은 깊은복사
JavaScript 내부적으로는 깊은 복사를 수행할 수 있는 방법이 없으나 JavaScript의 다른 문법을 응용하면 깊은 복사와 같은 결과물을 만들어 낼 수 있음

JSON.stringify()와 JSON.parse()

JSON.stringify()는 참조 자료형을 문자열 형태로 변환하여 반환하고, JSON.parse()는 문자열의 형태를 객체로 변환하여 반환. 먼저 1. 중첩된 참조 자료형을 JSON.stringify()를 사용하여 문자열의 형태로 변환하고, 2. 반환된 값에 다시 JSON.parse()를 사용하면, 3. 깊은 복사와 같은 결과물을 반환.

const arr = [1, 2, [3, 4]];
const copiedArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

하지만 이런 노력에도.. 깊은복사가 되지않는 이. 고집이 있었는데...
중첩된 참조 자료형 중에 함수가 포함되어 있을 경우 위 방법을 사용하면 함수가 null로 바뀌게 된단다..
ㅋ어이없어

const arr = [1, 2, [3, function(){ console.log('hello world')}]];
const copiedArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, function(){ console.log('hello world')}]]
console.log(copiedArr); // [1, 2, [3, null]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

꼭 깊은복사가 필요하다면 외부 라이브러리 사용해라..
node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 설치해야함.
아래는 lodash의 cloneDeep을 사용한 깊은 복사의 예시.

const lodash = require('lodash');

const arr = [1, 2, [3, 4]];
const copiedArr = lodash.cloneDeep(arr);

console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

스코프

변수에는 접근할 수 있는 범위가 존재한다. 중괄호(블록) 안쪽에 변수가 선언되었는지, 바깥쪽에 변수가 선언되었는지가 중요하다. 이 범위를 스코프라고 부른다.

스코프 규칙

  1. 안쪽 스코프에서 바깥쪽 스코프로는 접근할 수 있지만 반대는 불가능

      function outer() {
      const outerVar = "I'm in the outer scope"; // outer 함수 내부의 변수
    
      function inner() {
        console.log(outerVar); // inner 함수 내부에서 outerVar에 접근 가능
        // 하지만, inner 함수 내부에서는 outer 함수 내부의 다른 변수에는 접근할 수 없음
      }
    
      inner(); // inner 함수 호출
    }
    
    outer(); // outer 함수 호출
  2. 스코프는 중첩이 가능

  3. 가장 바깥쪽의 스코프는 전역 스코프(Global Scope)
    전역의 반대말은 지역(Local)으로 전역이 아닌 다른 스코프는 전부 지역 스코프(Local Sope)

  4. 지역 스코프에 선언한 변수는 지역 변수, 전역 스코프에서 선언한 변수는 전역 변수이다. 여기서 지역 변수는 전역 변수보다 더 높은 우선순위를 가진다.

스코프종류

블록 스코프

  1. {}기준으로 범위 구분
  2. 블록 스코프 안에서 정의된 변수
    는 블록 범위를 벗어나는 즉시 접근할 수 없음
  3. 같은 함수여도 화살표 함수를 사용하면 블록 스코프로 취급된다

함수 스코프

  1. function 키워드가 등장하는 함수 선언식 및 함수 표현식

선언 키워드 비교


var 키워드로 정의한 변수는 블록 스코프를 무시하고(이부분이 혼란스러울수있음)
함수 스코프만 따름 (화살표 함수의 블록 스코프는 무시하지 않음)

// 함수 스코프를 따르는 var 키워드는 if문 내에서 선언된 x 변수지만 함수외부에서 출력 가능함.
// (전역 변수인 x와 동일한 변수로 취급됨)

function example() {
  if (true) {
    var x = 1;
  }
  console.log(x); // 1
}

example();

let 키워드를 사용한다면 eferenceError: x is not defined 발생

profile
https://lia-portfolio.vercel.app/

0개의 댓글