데브코스 프론트엔드 5기/JavaScript 주요 문법

230920 [Day2] JavaScript 주요 문법 (1)

코딩하는 키티 2023. 9. 20. 22:44

JavaScript와 프론트엔드 개발

JavaScript로 무엇을 할 수 있을까?

  • 과거 : 단순하게 버튼을 눌렀을 때 DOM 조작 / 이벤트가 발생했을 때 alert
  • 현재 : 프론트엔드 개발 / 서버, 모바일 앱 개발
  • 서버 개발 : node.js, NestJS, Express ...
  • 앱 개발 : React Native, NativeScript, Cordova ...
  • 프론트엔드 개발 : jQuery, Backbone.js, React, Vue, Angular ... +특정 도메인에 해당하는 지식

브라우저의 동작 원리

1. 통신 : 브라우저와 서버와의 통신을 의미

  • 브라우저가 서버에 요청을 보내면 서버는 요청에 따라 특정 값을 응답합니다.
  • 요청은 한 번에 하나를 할 수도 있고, 한 번에 여러 개를 할 수도 있습니다.

 

2. 렌더링 : DOM이라는 객체를 화면에 그리는 것을 의미

  • DOM이란?
    • 통신을 통해 받은 HTML을 브라우저가 읽어서 생성됩니다.
    • 트리구조로 이루어져 있습니다.

 

3. 스크립트 실행 : 자바스크립트를 실행시키는 것을 의미

  • 브라우저가 script tag를 통해서 js를 읽으면 바로 실행됩니다.
  • 이를 통해 UI에 event를 등록하거나, 화면을 다크모드로 바꾸는 등 동적인 화면을 구성할 수 있습니다.

프론트엔드 개발자의 역할

  • 브라우저에서 동작하는 UI를 개발하는 것
  • 디자인 -> 프론트엔드 <- 백엔드
  • 다른 직군과 협업을 위한 소통 능력이 중요 (다방면으로 공부필요)
  • 기획, 마케팅
  • 핵심 역량 : 커뮤니케이션 , UI, 네트워크, 보안, 브라우저, 디자인
  • 해서는 안되는 것 : 컴퓨터 과학 무시, CSS 안하기, 코더가 되는 것

 

항상 도전하고 개발하자!!!

 

변수, 상수, 자료형 그리고 메모리

변수

  • var, let
  • let variable = 126 (키워드, 변수명/ 메모리상 주소, 값)

상수

  • 변하지 않는 값
  • const variable = 126 (키워드, 변수명, 값)

자료형

  • Number
// Number 타입
let integer = 126 // 정수
let float = 1.26 // 소수
let nan = parseInt('abc') // NaN
let inf = 1 / 0 // 무한
  • String
// String 타입
let string1 = "'String'"
let string2 = '"String"'
let string3 = `-${string2}-`
let string4 = 'I\'m String';
  • Boolean
// Boolean 타입
let bool1 = true
let bool2 = false
  • Object
// Object 타입
let object = {
  name: "Lee Jin-Ah",
  age: 24,
};
object['age'] // 24
// key 값은 무조건 문자열!!
  • Array
// Array 타입
let array = [0, 'a', true]
array[0] // 0
array[1] // 'a'
array[2] // true
  • Function
// Function 타입
let helloWorld = function() {
  return 'hello world'
}
  • Undefined & Null
let a;
console.log(a) // undefined
let b = null;
console.log(b) // null
// 차이점
// undefined : 변수 또는 상수가 선언되었지만 아무런 값도 대입되지 않은것
// null : 해당 변수가 비어있음을 명시한 것

메모리

  • 메모리는 할당 ➡️ 사용 ➡️ 해제 3가지 과정을 거친다.
    1. 변수를 생성하면
    2. 우리가 사용할 수 있도록 메모리가 할당
    3. 할당된 메모리에 값을 넣어 사용할 수 있다.
    4. 최종적으로 사용을 마치면 해제해서 메모리를 제거할 수 있다.
  • 메모리는 한정적이다.
    • 변수나 상수를 만들 때마다 메모리 공간은 줄어든다.
      • 메모리가 꽉 차게 되면 프로그램이 터지기 때문에 별도의 조치가 필요!
        • 이를 방지하기 위해 자바스크립트 엔진은 _Garbage Collector_를 통해 메모리를 정리한다.
  • Garbage Collector
    • Garbage Collector는 Garbage Collection이라는 자동 메모리 관리 알고리즘을 통해 만들어진 객체로, 사용하지 않는 메모리를 해제하는 역할을 한다.
    • 덕분에 우리는 메모리를 크게 신경 쓰지 않고 코딩할 수 있다!

 

메모리 심화

변수를 선언할 때 js 내부에서 발생하는 일

//예시1
let variable = 126;
let variable2 = variable;
variable = variable + 1;
  1. let variable = 126; 코드가 실행되면 js는 변수의 고유 식별자 variable을 생성한다.
  2. 메모리에 주소가 할당된다.
  3. 생성된 주소에 값( = 126)이 들어간다.
  4. let variable2 = variable; 코드가 실행되면 식별자 variable2가 생성되고 variable2는 variable과 동일한 주소와 값을 가지게 된다.
  5. variable = variable + 1; 코드가 실행되면 메모리가 새로 할당되어 변수 variable은 새로운 주소와 값을 가지게 된다.

js에서 원시 타입은 변경이 불가하기 때문에 원시 타입의 값이 변경될 경우에는 항상 메모리가 새로 할당된다.

 

js 엔진은 가상 머신(virtual machine)으로 구성되어 있다.

  • 가상 머신에는 메모리 모델이 구현되어 있다.
    • Heap 영역과 Call Stack 영역
      • Heap 영역 : 참조 타입이 들어간다.
      • Call Stack 영역 : 원시 타입이 들어간다.
//예시2
let a = 10;
const b = 20;
const arr = [];
arr.push(5);
arr.push(3);
arr.push(1);
  1. 차례대로 stack 영역에 쌓이게 된다.
  2. 배열( = [])은 Object 타입이기 때문에 참조 타입으로 분류되어 Heap 영역에 해당 배열의 영역이 생성되게 된다.
  3. 배열 변수 arr는 Call stack에 생성되고 배열 변수는 Heap 역역에서 생성된 배열의 메모리 주소를 참조한다.
  • arr는 const로 정의된 상수인데 push()를 통해 요소가 추가될 수 있는 이유
    • Heap 영역의 메모리는 동적으로 크기가 변할 수 있다는 특징을 가지고 있다.
    • 배열에 값을 추가하면 Heap 메모리 영역에 그대로 할당
    •  콜스택에 할당된 메모리를 변경하는 것이 아닌 Heap영역에 메모리를 변경하기 때문
  • 사용을 마친 메모리
    • Garbage Collector에 의해 정리된다. 
      • Mark and Sweap Algorithm에 의해 작동하는데 이는 닿을 수 없는 주소 더 이상 필요 없는 주소로 정의하고 지우는 알고리즘이다.
      • 예시 1에서도 처음 메모리 값(=126)은 이제 아무도 참조하지 않으니 Garbage Collector에 의해 정리된다. 

 

표현식과 연산자

표현식

어떤 결과 값으로 평가되는 식

숫자, 문자열, 논리값 같은 원시 값을 포함하여 변수, 상수, 함수 호출 등으로 조합할 수 있다.

 

연산자의 종류

1. 할당 연산자(ex. +=, -=, *=, /=, %=, <<=, >>=)
-> 오른쪽 표현식을 왼쪽 피연산자 값에 할당하는 연산자
2. 비교연산자(ex. ==, !=, ===, !==, >, >=, <, <=)
-> 좌측 피연산자와 우측 피연산자를 비교하는 연산자
3. 산술연산자(ex. +, -, *, /)
-> 덧셈, 뺄셈, 곱셈, 나눗셈을 하는 연산자(Number을 반환)
4. 비트연산자(ex. &, |, ^, <<, >>)
-> 비트를 직접 조작하는 연산자
5. 논리연산자(ex. &&, || !)
-> Boolean을 통해 참과 거짓을 검증하는 연산자
6. 삼항연산자(ex. x > y ? 값1 : 값2)
-> 조건에 따라 값을 선택하는 연산자
7. 관계연산자(ex. "속성명" in x)
-> 객체에 속성이 있는지 확인하기 위한 연산자
8. typeof
-> 피연산자의 타입을 반환하는 연산자

 

흐름 제어

Control Flow

흐름을 제어하는 방법 중 하나, 조건이나 반복을 통해 상태를 제어하는 것을 의미

조건문

조건이 맞을 때만 실행되는 문장 문법

  • if
    • 괄호 안 조건식이 참인 경우 실행
    • else if, else 같이 사용
    • false, undefined, null, 0, NaN, ''(empty string) 값들도 거짓이 될 수 있으니 주의
  • switch
    • 괄호 안 값에 따라 분기되는 문법
    • case, default 함께 쓰인다.
    • case 마지막에 break를 쓰지 않으면 다음 case도 같이 실행되므로 주의

 

반복문

반복적인 작업을 지시하는 문법

  • for
    • 초기문, 조건문, 증감문으로 이루어져있다.
    • 조건문의 결과가 거짓이 되면 반복이 종료
  • while
    • 괄호안 조건이 거짓이 될 때까지 반복
  • do-while
    • while문과 다르게 먼저 진입 후 로직을 실행한 다음 조건 검사

 

배열과 객체

배열

  • 연관된 데이터를 연속적인 형태로 저장하는 복합 타입
  • 배열에 포함된 원소는 순서대로 번호(index)가 붙는다.

 

배열 생성

// 빈 Array를 생성할 수 있다.
const arr1 = new Array () ;

const arr2 = [ ];

// 미리 초기화된 Array를 생성할 수 있다.
let arr2 = [1, 2, 3, 4, 5];

// 많은 값을 같은 값으로 초기화할 경우 을 쓸 수 있다.
let arr3 = Array(5).fill(0);

// 특정 로직을 사용하여 초기화할 경우 from을 사용할 수 있다.
let arr4 = Array. from({ length: 100 ), (_. i) => i);
[]
[]
[1, 2, 3, 4, 5]
[0, 0, 0, 0, 0]
[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, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

 

배열 요소

  • arr.length로 배열 크기 조작 가능 -> 권장되지 않는다
  • join : 배열의 원소들 사이에 원하는 문자를 끼워서 문자열로 반환시켜준다.
  • reverse : 배열의 원소의 순서를 뒤집는다 (원래 배열 영향 감)
  • concat : 두개의 배열을 합체
  • push,pop : 시간복잡도 O(1), 각각 배열의 맨 뒤의 요소를 추가 삭제한다
  • unshift,shift : 시간복잡도 O(n), 각각 배열의 맨 앞의 요소를 추가 삭제한다
  • slice : arr.slice(start,end)면 arr[start] 부터 arr[end-1]까지의 요소들을 담을 배열을 반환한다. (원본배열 영향 x)
  • splice : arr.splice(start, 개수) 가 잘려나감. (원본배열 변화 o)

 

배열 순회

  • for
  • for of
const arr = [1,2,3,4,5,6];

for ( var i =0 ; i < 6;  i += 1) {
	console.log(arr(i);
}

// 1,2,3,4,5,6

for (const item of arr) {
	console.log(item);
}

// 1,2,3,4,5,6

 

객체

  • 여러 값을 키-값 형태로 결합시킨 복합 타입
const obj1 = new Object( );  // { }

const obj2 = { };  // { }

const obj = { name: "jin" , age: "24"};
// 객체 추가
obj["email"] = "kitty@naver.com"
[obj.phone](http://obj.phone) = “01000000000”

// 객체 내 정보 삭제 
delete obj.phone;

// in operator 를 이용하여 key 가 있는지 확인하기.
console.log("email" in obj);   // true
console.log("phone" in obj);  // false

// 객체 내 key 와 value 집합 구하기
console.log(Object.keys(obj) // [”name”, “age”, “email”] → 배열의 형태로 key 나옴 
console.log(Object.values(obj) // [”jin”, “24”, “kitty@naver.com”] → 배열의 형태로 value 나옴
  • for in 문으로 객체 순회
const obj = { name: "jin" , age: "24"};  

for ( const key in obj) {

console.log(key, obj[key]);

}

// name jin 
// age 24 
// email kitty@naver.com

 

스코프와 클로저

스코프

  • 식별자의 유효범위이며 변수가 어느 범위까지 참조되는 지 알 수 있는 것
  • global scope 전역 스코프 : 누구든지 참조 가능한 + 데이터 영역에 위치
  • local scope 지역 스코프 : 해당 지역에만 참조 가능한 + 스택 영역에 위치 + 메모리 절약(블록 실행 종료시 소멸)
  • 블록 안에서 선언된 식별자는 밖에서 사용 못한다(반대는 가능)
  • 블록 안쪽에 변수나 상수가 새로 선언되면 바깥것을 덮어쓴다.
  • 함수 or 블록 선언할때 생긴다.

 

  • var 사용하면 안되는 이유 - 호이스팅이 되어버려서 상단으로 올라간다(함수 레벨 스코프) ⇒ 예상치 못한 오류
    • 블록 내부에 새롭게 선언하더라도, 블록 외부 변수 값도 변하게 된다.
    • 이런 차이가 발생하는 이유는, var 는 함수수준의 스코프이고 const 와 let 은 블록 수준 스코프이기 때문이다.
    • 따라서 var는 개발자가 오류를 찾기가 힘들어진다.
console.log(a) //undefined
var a = 5;
{
    //호이스팅 되어 변수 선언이 상단으로 올라가 버린다. 
    //var은 함수 수준의 scope이다. 
    var a = 10;
    console.log(a); // 10
}
console.log(a) // 10

클로저

  • 함수가 선언된 환경의 스코프를 기억하여 함수가 스코프 밖에서 실행될 때에도 기억한 스코프에 접근할 수 있게 만드는 문법
  • = 함수 블럭안에 선언된 변수들이 함수가 실행되어도 밖에서도 접근한 문법
  • 함수가 생성될 당시의 외부변수를 기억 → 생성 이후에도 계속 접근 가능하다.
<script>
function outerFunc(word) {
  let say = "Hello ";
  let innerFunc = function (word) {
    console.log(say + word);
  };
  return innerFunc;
}

let inner = outerFunc();
inner("World!");
//Hello World!
</script>

 

  • 위와 같이 외부함수보다 내부함수가 더 오래 살아남는 경우를 가르킨다.
  • 은닉화 : 클로저를 이용하여 변수와 함수를 숨기는 것
  • 클로저를 잘 이해하면 알기 힘든 버그를 발견하기 쉽다.
  • 만약 비동기함수가 생각처럼 동작하지 않을 때, 클로져와 관련된 이슈일 수 있다는 생각을 해보고 즉시 실행함수를 사용하는 방법으로 코드를 바꾸거나 or let을 사용해보자