[JS/Node] 핵심 개념과 주요 문법

LEE JI YOUNG·2021년 9월 8일
0

JS/Node

목록 보기
6/23
post-thumbnail

원시 자료형과 참조 자료형

  • 원시 자료형 (Primitive type data)
    : number, string, undefined, null, boolean, (bigint, symbol)
    • 원시 자료형이라고 하는 이유 : 데이터 저장소(메모리)의 용량이 제한되어 데이터 보관함 한 칸에 하나의 데이터만 넣을 수 있는 그 때의 "원시적인" 방식.
    • 변수 : 값(value) 저장. 데이터의 크기와는 관계 없이 하나의 데이터만!
    • 값 자체에 대한 변경이 불가능(immutable)하지만, 변수에 다른 데이터를 재할당 가능.
      • const는 재할당 불가능.
    • 데이터 값이 복사되어 기존의 데이터에 영향이 가지 않음.
let a = 1; 
let b = a;
b = 2; // a = 1 그대로




  • 참조 자료형 (Reference type data)
    : array, object, function 배열([]), 객체({}), 함수(function(){})
    • 자바스크립트에서 원시 자료형이 아닌 모든 것은 참조 자료형
    • 원시, 참조가 따로 자료 구조를 구현해야 하는 이유 : 변수에 넣을 수 있는 데이터 크기가 제한되기 때문에 "데이터의 크기가 동적으로 변하는" 특별한 데이터 보관함이 필요함.
    • 변수 : 값이 아닌 주소(reference) 저장.
    • heap : 데이터 보관함.
      • 대량의 데이터가 들어오는 경우 고정된 데이터 공간을 사용하는 것이 비효율적이라서 보관함의 사이즈는 동적(dynamic)으로 변함.
    • 변수의 주소를 "참조" > 실제 변수가 있는 장소에 "도착" > 어떤 데이터가 있는지 "읽음"
    • 주소값이 복사되어 복사한 데이터의 원소를 변경하게 되면 주소안의 데이터가 변경 됨.
let e = [10, 20]; 
let f = e;
f[0] = 50;
e;  // e = [50, 20] 으로 변함.
// 문제 3)
console.log('codestates' === 'codestates');     //true
console.log(3.14 === 3.14);     //true
// 참조 자료형의 ===(strict equality)는 주소값이 같은지를 확인, 
// 따라서 두 참조 자료형의 주소값은 다르다고 판단을 내릴 수 있음.
console.log([1,2,3] === [1,2,3]);     //false
console.log({ foo: 'bar' } === { foo: 'bar' });    //false
// 문제 4)
let x = { foo: 3 };
let y = x;
//여기서 변수 y는 x의 주소값을 할당 받음.
y = 2;
//하지만 다시 2라는 값을 할당 받음. 따라서 x.foo는 변함없이 3이다.
// 문제 7)
let score = 80;
function doStuff(value) {
  value = 90;
}
doStuff(score)  // score 값은 80
//매개변수 value에 score의 값 80이 전달되고,```




스코프

  • 스코프 (Scope) : 컴퓨터 공학, 그리고 자바스크립트에서는 "변수의 유효범위" 의미
    • 변수에 접근할 수 있는 범위가 존재. {중괄호(블록)} 안쪽에 변수가 선언되었는가, 바깥쪽에 변수가 선언되었는가가 중요.
      • 바깥 스코프에 선언된 변수는 안쪽 스코프에서 사용 가능.
      • 안쪽 스코프에 선언된 변수는 바깥 스코프에서 사용 불가능.

Scope 문제풀기 3단계

  1. 지역 / 전역 스코프 나누기
  2. 선언된 변수 찾고, 지역 / 전역 변수인지 명시
  3. 선언되지 않은 변수가 가리키는 대상 찾기 (안에서 밖으로!)

    • message라는 변수 자체가 중괄호(블록) 안쪽에 선언되어 있으므로, 바깥쪽에서는 접근할 수 없다. 바깥쪽에서는 접근이 불가능하면 ReferenceError를 낸다.

변수 접근 규칙에 따른 유효 범위

  • 안쪽 스코프에서 바깥쪽 스코프로 접근을 할 수 있으나 반대는 불가능.
  • 스코프는 중첩이 가능함.
  • 가장 바깥의 스코프는 특별히 전역 스코프(Global scope)라고 부름.
    <-(반대말)-> 지역스코프(Local scope)
  • 지역 변수는 전역 변수보다 더 높은 우선순위를 가짐.
    • 쉐도잉(variable shadowing) : 동일하게 선언된 변수 이름으로 인해 바깥쪽 변수가 안쪽 변수에 의해 가려지는(shadow) 현상.

  • 예제3) 정답 : 김코딩 박해커 김코딩
  • 첫번째 출력은 첫째 줄에서 전역 변수로 선언된 name을 가져옴. (showName 함수 안쪽에 선언된 지역 변수 name은 애초에 스코프 규칙에 의해 접근할 수 없다.) "김코딩"을 출력.
    두번째 출력은 함수 안에서 선언한 name이라는 지역 변수에 접근. 변수 이름이 전역 변수와 똑같지만, 지역 변수가 전역 변수보다 우선순위가 높으므로, 쉐도잉(variable shadowing)현상(동일한 변수 이름으로 인해 바깥쪽 변수가 안쪽 변수에 의해 가려지는 현상)으로 지역 변수 name이 출력.
    두번째 출력은 "박해커.
    세번째 출력은 첫번째 출력과 마찬가지로 전역 변수 name을 출력. 지역 변수에 선언된 name 변수는 안쪽 스코프이므로 접근이 불가능. 따라서 "김코딩"을 출력.


  • 예제4) 정답 : 김코딩 박해커 박해커
  • 세번째 줄에서 let 키워드를 사용한 선언이 존재하지 않음. 이는, '박해커'라는 값으로 할당하고 있는 name 변수는 전역에 선언된 name 변수를 그대로 사용하겠다는 의미!!!
    지역 스코프에서 새로 선언되지 않으면 그냥 같은 변수이므로 showName 함수가 실행되기 전, 처음에는 '김코딩'을 출력하고, 그 이후에는 전역변수 name의 값이 바뀌기 때문에 두번째 및 세번째 출력에 '박해커'가 출력된다.

스코프의 종류와 let, const, var

1. 스코프의 종류

  • block scope : 블록 스코프 : {중괄호}로 둘러싼 범위

    - 화살표 함수는 블록 스코프로 취급됨 ! : 화살표 함수로 둘러싼 범위

  • function scope : 함수 스코프 : f( ){함수}로 둘러싼 범위

  • 함수 스코프와 블록 스코프는 논리적인 구분 외에도 코드를 작성할 때 기억해야 할 다른 점이 몇 가지 존재

2. let, const, var

letconstvar
유효범위블록 스코프 및 함수 스코프블록 스코프 및 함수 스코프함수 스코프
값 재할당가능불가능가능
재선언불가능불가능가능
  • let
    • 블록 스코프 안에서 정의된 변수 i let i = 0;는 블록 범위를 벗어나는 즉시 접근할 수 없다.
    • 블록 스코프, 화살표 함수, 함수 스코프 따른다.
    • 재선언 방지함.

  • var
    • 블록 스코프 무시, 화살표 함수, 함수 스코프만 따른다.
    • 재선언해도 에러 없음 - side effect 발생
    • 혼란을 줄 수 있으므로 let 키워드 사용이 권장 됨.

  • var 키워드보다 let 키워드가 더 안전한 이유
    : var 키워드는 재선언을 해도 아무런 에러도 내지 않지만, let키워드는 재선언을 방지하므로써 버그를 막아준다.

  • const
    • 블록 스코프, 화살표 함수, 함수 스코프 따른다.
    • 값의 변경을 최소화하여 보다 안전한 프로그램을 만들 수 있다. 값을 새롭게 할당할 일이 없다면 const 키워드의 사용이 권장됨.
    • 재할당 방지함. 재할당하는 경우, TypeError.

3. 실습: 스코프와 개발자 콘솔



: Local Scope와 Global Scope 확인 가능


: debugger + (shift + enter)


4. 변수 선언에서 주의할 점

  • 전역 변수 최소화하기.
    • 전역변수에 너무 많은 변수를 선언하지 마세요.
      • 전역변수 : 어디서든 접근 가능한 변수
      • 편리한 대신, 다른 함수 혹은 로직에 의해 의도되지 않은 변경(side effect)이 발생할 수 있음.
  • let, const를 주로 사용하기.
    • var는 블록 스코프를 무시하며, 재선언을 해도 에러를 내지 않음. 같은 스코프에서 동일한 이름의 변수를 재선언 하는 것은 버그를 유발함!!
    • 전역 변수를 var로 선언하는 경우 문제가 될 수 있음. var로 선언한 전역 변수가 window기능을 덮어씌워서 내장 기능을 사용할 수 없게 만들 수 있음.
  • 선언 없는 변수 할당 금지
    • 선언 키워드(var, let, const)없이 변수를 할당하지 않기. 선언없이 값을 할당하면 해당 변수는 var로 선언한 전역 변수처럼 취급됨.
    • Strict Mode는 브라우저가 보다 엄격하게 작동하도록 함. 선언 없는 변수 할당의 경우도 Strict Mode는 에러로 판단합니다. Strict Mode를 적용하려면, js 파일 상단에 'use strict' 라고 입력.




클로저

  • 함수가 중첩됨
  • 내부함수가 외부함수를 참조할수 있어야함
  • 클로저 : "외부함수의 변수에 접근할 수 있는 내부함수"

클로저 함수의 특징

1. 함수를 리턴하는 함수.

2. 외부함수와 내부함수 : 리턴하는 함수에 의해 스코프(변수의 접근 범위)가 구분됨.
클로저의 핵심은 스코프를 이용해서 변수의 접근 범위를 닫는(closure; 폐쇄) 데에 있다. 따라서, 함수를 리턴하는 것만큼이나, 변수가 선언된 곳이 중요!

  • 클로저 함수 : 외부함수의 변수에 접근 가능한 내부함수.


클로저의 활용

1. 데이터를 보존하는 함수
: 일반적인 함수는, 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없다. 이와 다르게, 클로저는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리 상에 저장된다. 변수 add5 에는 클로저를 통해 리턴한 함수가 담겨 있다. add5 는 adder함수에서 인자로 넘긴 5라는 값을 (외부 함수의 실행이 끝났음에도!!)x 변수에 계속 담은 채로 남아있다.

2. 정보의 접근 제한

: 클로저를 이용해 내부 함수를 단 하나만 리턴하는 것에 그치지 않고, 객체에 담아 여러 개의 내부 함수를 리턴하도록 만들 수 있다.

: makeCounter 함수는 "increase, decrease, getValue메소드를 포함한 객체"를 리턴. counter1은 객체다.

: '외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다'는 규칙에 의해, 어떤 경우에도 value는 직접 수정이 불가능. 대신, 리턴하는 객체가 제공하는 메소드를 통해 value 값을 간접적으로 조작 가능. =========> "정보의 접근 제한 (캡슐화)" : value의 값을 보존해줌.

  • 정보의 접근 제한 (캡슐화)
    : 만일 스코프로 value 값을 감싸지 않았더라면, value 값은 전역 변수여야만 했다. 하지만 makeCounter라는 함수가 value 값을 보존하고 있기 때문에, 전역 변수로 따로 만들 필요가 없었다. 전역 변수는 다른 함수 혹은 로직 등에 의해 의도되지 않은 변경(side effect)을 초래하여 좋지 않다. side effect를 최소화하면, 의도되지 않은 변경을 줄일 수 있다. 따라서 이에 따른 오류로부터 보다 안전하게 값을 보호 가능.

  • 정보의 접근 제한 (캡슐화) 장점 : 클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있다.

: makeCounter에 의해 리턴된 객체는, makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가진다. 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고(각각 주소가 아닌 객체 자체가 할당되므로...), 각각의 값을 보존할 수 있다. 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 "모듈화"라고 한다.

클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있다!! 즉, 클로저는 모듈화에 유리하다!



!! Spread/Rest 문법 , 구조 분해 알아두면 편리 mdn 숙지하기 !!
Spread 문법 - mdn

Rest 문법 - mdn

구조분해할당 - mdn

유사배열
!! Spread/Rest 문법 , 구조 분해 알아두면 편리 mdn 숙지하기 !!


Spread/Rest 문법 - ...arr, ...obj

  • Spread 문법 : 주로 배열을 풀어서 인자로 전달하거나, 배열을 풀어서 각각의 요소로 넣을 때에 사용.
function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

sum(...numbers) // 질문: 어떤 값을 리턴하나요? 6
let arr = ['code', 'states']
let value = [
  ...arr,
  'pre',
  ...['course', 'student']
]
//['code', 'states', 'pre', 'course', 'student']
  • Rest 문법 : 파라미터를 배열의 형태로 받아서 사용 가능. 파라미터 개수가 가변적일 때 유용.
function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}

sum(1,2,3) // 질문: 어떤 값을 리턴하나요? 6 
sum(1,2,3,4) // 질문: 어떤 값을 리턴하나요? 10
  • Spread 문법 : 배열에서 사용하기 :: immutable
    • 배열 합치기
let parts = ['shoulders', 'knees'];
let lyrics = ['head', ...parts, 'and', 'toes'];
// 질문: lyrics의 값은 무엇인가요? ['head', 'shoulders', 'knees', 'and', 'toes']

let arr1 = [0, 1, 3];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];   // arr1 = [0, 1, 3, 3, 4, 5]
// 참고: spread 문법은 기존 배열을 변경하지 않으므로(immutable), 
// arr1의 값을 바꾸려면 새롭게 할당해야 합니다.
    • 배열 복사
let arr = [1, 2, 3];
let arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4);  // 참고: spread 문법은 기존 배열을 변경하지 않으므로(immutable), 
//arr2를 수정한다고, arr이 바뀌지 않습니다.
// 질문: arr와 arr2의 값은 각각 무엇인가요?
  • Spread 문법 : 객체에서 사용하기 - 합치기, 복사 :: immutable
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };

let clonedObj = { ...obj1 }; // {foo: 'bar', x: 42}
let mergedObj = { ...obj1, ...obj2 };  // {foo: 'baz', x: 42, y: 13}
// 질문: clonedObj와 mergedObj의 값은 각각 무엇인가요?
  • Spread 문법 : 함수에서 나머지 파라미터 받아오기
function myFun(a, b, ...manyMoreArgs) {
  console.log("a", a);
  console.log("b", b);
  console.log("manyMoreArgs", manyMoreArgs);
}

myFun("one", "two", "three", "four", "five", "six");

// 질문: 콘솔은 순서대로 어떻게 찍힐까요
//a one
//b two
//manyMoreArgs (4) ['three', 'four', 'five', 'six']
let x;
let y;
let man;
function myFun(a, b, ...manyMoreArgs) {
  x = a;
  y = b;
  man = manyMoreArgs;
}
myFun("one", "two", "three", "four", "five", "six");

console.log(x);
console.log(y);
console.log(man);
// 질문: 콘솔은 순서대로 어떻게 찍힐까요
//one
//two
// (4) ['three', 'four', 'five', 'six']



구조 분해 (Destructing)

: 구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다.

  • 분해 후 새 변수에 할당
    • 배열
const [a, b, ...rest] = [10, 20, 30, 40, 50];

// 질문: a, b, rest는 각각 어떤 값인가요? 
// 10
// 20
// [30, 40, 50]
    • 객체
const {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
// 질문: a, b, rest는 각각 어떤 값인가요?
// 10
// 20
// {c: 30, d: 40}
  • 객체에서 구조 분해 할당을 사용하는 경우, 선언(const, let, var)과 함께 사용하지 않으면 에러가 발생.
    • 유용한 예제: 함수에서 객체 분해
function whois({displayName: displayName, fullName: {firstName: name}}){
  console.log(displayName + " is " + name);
}

let user = {
  id: 42,
  displayName: "jdoe",
  fullName: {
      firstName: "John",
      lastName: "Doe"
  }
};

whois(user) // 질문: 콘솔에서 어떻게 출력될까요? jdoe is John
let arr = [10, 30, 40, 20]
let value1 = Math.max(arr) // NaN
let value2 = Math.max(...arr) // 40
profile
프론트엔드 개발자

0개의 댓글