코드스테이츠_3주차_화

윤뿔소·2022년 9월 6일
0

CodeStates

목록 보기
12/47

그동안 JS의 기본적인 데이터와 문법에 대해서 공부해봤다.
이제 그 문법이 어떻게 구성되고 데이터를 처리하는 로직이 어떻게 진행되는지 더 자세하게 확인하고 공부하자!

원시 자료형과 참조 자료형

  • 원시 자료형(primitive data type)과 참조 자료형(reference data type)의 구분이 왜 필요한지에 대해서 영상을 보고 이해
  • 원시 자료형과 참조 자료형의 차이를 이해하고, 각자 맞는 상황에서 사용
  • 원시 자료형이 할당될 때는 변수에 값(value) 자체가 담기고, 참조 자료형이 할당될 때는 보관함의 주소(reference)가 담긴다는 개념을 코드로 설명
  • 참조 자료형은 기존에 고정된 크기의 보관함이 아니라, 동적으로 크기가 변하는 특별한 보관함을 사용한다는 것을 이해

원시 자료형

  • 고정된 저장 공간을 차지하는 데이터
  • 타입 부분에서 배웠듯 Stack이라는 메모리 주소의 사물함에 저장하고 꺼내 쓰는 데이터
  • 하나의 데이터 메모리 주소(사물함)에 하나의 값만 있는 형태, 옛날에 용량이 제한될 때 썼던 방식이라 그래서 '원시'라는 표현을 씀
  • number, string, boolean, undefined, (null) 등

참조 자료형

원시 자료형에서 대량의 데이터가 들어왔을 때, 고정된 데이터 공간을 사용하는 것이 비효율적이라고 느꼈음. 예를 들어 하나의 값만 넣는 것에 한계를 느꼈고, 하나의 사물함의 용량에도 한계를 느껴 "데이터의 크기가 동적으로 변하는" 특별한 데이터 보관함이 필요해졌음

  • 변수에 값이 아닌 주소를 저장, 그 주소로 가면 특별한 '사물함'(데이터가 담긴 저장소)이 생김
  • 이 '사물함'의 명칭은 'Heap'이고, 용량을 마음대로 조절할 수 있음, 즉! 동적임!
  • 실생활에선 Reperence, 즉 '자료를 찾는다.'의 의미지만 공학에선 정확히 변수가 가리키고(refer) 있는 데이터를 참조한다는 의미임

결론: 특징과 차이

  • 원시는 값, 참조는 주소를 메모리 주소에 할당함, 즉! 변수에 어떤 값을 할당할 때 차이가 생김
let x = 2;
let y = x; // 값이 같지만 다른 메모리 주소가 할당
y = 3; // 그러므로 y가 변해도 영향 x
let x = [10, 20, 30, 40];
let y = x; // 다른 메모리 주소지만 그 안에 있던 Heap이 같음
y[0] = 5; // 그 Heap이 변했으니 x도 변함(얕은 복사)

let player = { score: 3 }; // 3. score이 2로 변함
function doStuff(obj) { // 2. 함수 호출
  obj.score = 2; // 2-1. 같은 Heap 주소끼리 공유돼 변함, 사물함이면 원본 안변함
}
doStuff(player); // 1. 함수 실행, player를 인수로 넣어 호출

  • 참조 데이터가 매번 할당될 시 JS는 똑똑해 우리가 보기에 같은 값이지만 다른 Heap이 생성됨, 그니까 미리 주소값과 메모리 값을 잡아둔다고 생각하자,,
console.log([1,2,3] === [1,2,3]);
console.log({ foo: 'bar' } === { foo: 'bar' });
// false false, 다 다른 heap 저장 공간의 주소를 확보
  • 그러므로 메모리 주소인 '사물함'과 참조형일 때의 동적 데이터 주소인 'Heap'을 따로 봐야함
let myArray = [2, 3, 4, 5];
let ourArray = myArray; // 사물함은 다르지만 Heap은 같음
ourArray[2] = 25; // myArray의 heap은 [2, 3, 25, 5]
ourArray = undefined; // ourArray는 undefined, my는 안변함

스코프와 선언

Scope는 range랑 비슷한 의미, 즉 범위를 나타냄, 공학에선 '변수의 유효 범위'를 뜻함! 이제 알아보자

  • 스코프의 의미와 적용 범위를 이해
  • 스코프의 주요 규칙을 이해
  • 전역 스코프와 지역 스코프의 차이를 이해
  • block scope와 function scope의 차이를 이해
  • 변수 선언 키워드(let, const, var)와 스코프와의 관계를 설명
  • 전역 객체가 무엇인지 설명

특징과 종류

  • Global Scope, Local Scope(블록, 함수)가 대표적임
  • 전역 스코프(Global Scope)는 가장 바깥의 스코프를 뜻함, 지역 스코프는 그 반대
  • 중첩 가능함! 안쪽에서 바깥쪽으로 접근 가능하지만 반대는 불가능
  • 지역 변수는 전역 변수보다 더 높은 우선순위를 가짐
  • 예시: 아래 코드는 스코프 차이를 보여줌, showname1은 함수 안쪽에서 선언돼 Local에서만 변수를 쓸 수 있고, showname2는 선언하지않고 그대로 '재할당'해준 경우라 Local이 Global의 변수를 갖다쓴 것 뿐임
let name = "윤뿔소"
function showname1() {
  let name = '코뿔소'; // 선언 o
  console.log(name);
}
function showname1() {
  name = '코뿔소'; // 선언 x
  console.log(name);
}
console.log(name); // '윤뿔소'
showname(); // '코뿔소'  
console.log(name); // 1: '윤뿔소', 2: '코뿔소'

블록 스코프

  • 중괄호 안에서 선언된 변수의 범위를 뜻함
  • ⭐️화살표 함수가 있는데 화살표 함수는 블록 스코프임!!

함수 스코프

함수 안에서 선언된 변수의 범위를 뜻함

변수 정의 키워드: let, const, var

  • 스코프: var로 선언된 변수는 블록 스코프를 무시하고 함수 스코프만 따름, 물론 예외도 있음(화살표 함수는 무시안함), 그래서 직관적이지 못하고 코드가 혼란스러울 수도 있음, 초보들한텐 직관적인 let, const 권장
  • 재선언: var는 재선언이 가능함, let, const는 재선언 불가능, 훨씬 안정적
  • 재할당: const는 재할당 불가, 그러므로 의도하지 않은 값의 변경을 막을 수 있음!

결론

  • 변수가 사용할 수 있는 스코프를 제대로 정해 스파게티 코드가 되지 않게 주의! 특히 전역 변수(어디서든 접근 가능)에 너무 많은 변수를 선언하지말기, Side Effect 발생
  • var는 쓰지않도록 주의하자
    1.직관을 무시하는 스코프 무시
    2.재선언이 가능해 버그 유발
    3.자칫 window(브라우저의 전역 변수)에 var의 전역 변수가 덮어써 내장 기능을 못하게 됨
  • 'Strict Mode'라는 걸 이용해 오류 추적 가능

클로저

함수가 여러개라면? 여러개가 있는데 그에 따른 변수들을 가져올 수 싶다면?

  • 클로저 함수의 정의와 특징에 대해서 이해
  • 클로저가 갖는 스코프 범위를 이해
  • 클로저를 이용해 유용하게 쓰이는 몇 가지 패턴을 이해

정의

  • 함수와 함수가 중첩돼 선언된 어휘적(lexical) 환경의 조합
  • 어휘적 환경(Lexical environment): 반환된 내부함수가 자신이 선언됐을 때의 환경, JS 엔진은 함수가 호출된 위치가 아닌 함수가 정의된 위치에 따라 상위 스코프를 결정함!
  • 클로저 함수 : 외부 함수의 변수에 접근(참조)할 수 있는 내부 함수
  • 즉! 클로저 함수는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리상에 저장되어 있음

활용

  • 일반적인 함수는 실행되고 죽으면 함수 내부의 변수를 사용할 수 없음
  • 클로저를 통해 실행이 끝나더라도 외부 함수 내 변수가 메모리 저장돼서(렉시컬 인바이러먼트, 렉시컬 스코프가 메모리에 저장) 외부 함수의 실행이 끝났음에도 리턴된 값을 변수로 담고 있을 수 있음!! 아래 코드는 adder(5)라는 값을 add5라는 변수에 담았음
  • 이걸 이용해 클로저로 활용을 해보자!

HTML 생성기

divMaker 함수는 div라는 문자열을 tag 라는 변수에 담아두어 div태그를 만듬 여기서 클로저를 통해 content를 변수로 불러냄

const tagMaker = tag => content => `<${tag}>${content}<${tag}>`
const divMaker = tagMaker('div');
divMaker("Hello") // '<div>Hello<div>'
divMaker("Rhino") // '<div>Rhino<div>'

클로저 모듈 패턴

  • 클로저를 이용하면 재사용성을 극대화하고, 함수 하나를 완전히 독립적인 부품 형태로 분리해 모듈화를 이룰 수 있음!
  • ⭐️객체 데이터로 클로저 함수를 담을 수 있음!!@
  • 아래 코드는 모듈 패턴의 예시로서 makeCountervalue를 선언, 0으로 할당시킨뒤 함수들을 객체 데이터로 넣고 불러와지면 카운트하는 형식의 코드임
const makeCounter = () => {
  let value = 0;
  return {
    increase: () => {
      value = value + 1
    },
    decrease: () => {
      value = value - 1
    },
    getValue: () => value
  }
}

const counter1 = makeCounter();

counter1.increase();
counter1.increase();
counter1.decrease();
counter1.decrease();
counter1.increase();
counter1.getValue(); // 맞춰봐라!
  • 왜 이렇게 쓰냐면 value의 값을 로컬 스코프에서 선언해 정보의 은닉을 할 수 있고 깔끔하게 쓸 수 있기 때문에 모듈화를 할 수 있는 것임!

결론

  • 아래 사진 같이 outer()의 변수 x는 원래 죽는거지만 inner의 내부 함수로 밖에서도 꺼내쓸 수 있음즉 변수를 살려내는 방법임
  • 아래 코드의 포인트는 ⭐️변수가 정의된 위치마다 결과가 달라짐!! f2부터 변수 b는 함수 안에 있기에 초기화됨, 즉! f1 === f2는 독립된 렉시컬 스코프가 다르기에 false가 나옴!
var a = 0;
function foo() {
    var b = 0;
    return function() {
        console.log(++a, ++b);
    };
}

var f1 = foo();
var f2 = foo();

f1(); // --> 1, 1
f1(); // --> 2, 2
f2(); // --> 3, 1 - f2를 새로 실행, 렉시컬 환경이 달라져 변수가 쌓이는 것이 달라짐
f2(); // --> 4, 2

+ 즉시실행함수, 삼항연산자(isShow ? 'block' : 'none'), 스타일객체(box.style.display)

profile
코뿔소처럼 저돌적으로

0개의 댓글