[Javascript] JS 기술 파헤치기🥄 (1)
나혼자 공부하고 복습 정리하는거라 가독성 0임..
💯 1. 클로저
클로저 : 함수 내부에 있는 지역변수의 상태값을 기억하는 거! 함수는 한 번 호출되면 실행 컨텍스트라는 특별한 환경을 만듭니다. 그 안에 지역 변수도 있고요. 그런데 클로저는 이 실행 컨텍스트가 끝난 후에도, 그 안에 있는 변수들을 기억할 수 있게 함! 외부에선 직접 접근이 불가함
⇒ 안전한 상태관리! 코드의 안정화 (전역변수는 누구나 접근,바꾸게 되는데 얘는 캡슐화 되어서 불가함)
function makeCounter() {
let count = 0; // 'count'는 makeCounter() 함수 내부의 지역변수
return function() {
count++; // 반환된 함수가 count에 접근해서 값을 변경
return count; // 변경된 count 값을 반환
};
}
const counter = makeCounter(); // makeCounter() 함수 실행
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
💯 2. 이벤트위임
그냥 일일이 하나의 요소에 이벤트 주는 방식은 → 반복문 써야함
WHY? NodeList 자체는 배열이 아니기 때문에 **addEventListener**를 바로 사용할 수 없다는 점!
const listItems = document.querySelectorAll('ul li');
listItems.forEach(item => {
item.addEventListener('click', function() {
alert('클릭된 항목: ' + item.textContent);
});
});
하지만? 부모요소에 이벤트 달고, 이벤트 위임을 시키면 불필요한 메모리 사용도 줄이고, 동적 이벤트 위임도 가능함!
부모 자체에 이벤트 핸들러 달고, 인식 있으면 그 자식이 뭐였는 지에 따라서 분기처리!
(부모 컨테이너에 달고, 어디에서 이벤트 타깃이 되었는지 찾아 들어가면 됨)
const todoList = document.getElementById('todo-list');
todoList.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
alert('클릭된 항목: ' + event.target.textContent);
}
});
장점
1. 동적 요소 처리: 이벤트 위임을 사용하면 동적으로 추가된 자식 요소들도 처리 ㄱㄴ.
2. 성능과 메모리 효율: 부모 요소에 하나의 이벤트 리스너만 추가하므로 성능상 더 유리하고, 메모리 사용을 줄
💯 3. 프로토타입 상속 (Feat. 프로토타입 체인)
// 부모 객체
const parent = {
name: '부모',
greet: function() {
console.log('안녕하세요, ' + this.name);
}
};
// 자식 객체
const child = Object.create(parent); // child는 parent의 프로토타입을 상속받음
child.name = '자식'; // 자식 객체에 name 속성 추가
// 부모 객체의 greet 메서드를 호출
child.greet(); // "안녕하세요, 자식"
=> 프로토타입 체인?
- child는 parent의 프로토타입을 참조.
- parent는 Object.prototype의 프로토타입을 참조.
- Object.prototype의 프로토타입은 null이어서 여기서 끝.
객체가 특정 속성(예: name)을 가지고 있지 않으면, 자바스크립트는 그 객체의 프로토타입(부모 객체)을 찾아서 그 안에 해당 속성이 있는지 확인해요. 만약 부모 객체에도 없으면, 그 부모의 프로토타입을 찾아서 계속 검색해요. 이 과정이 프로토타입 체인
없으면 → null로 반환함
💯 4. 호이스팅
선언문이 코드의 선두로 끌어올려진 것처럼 동작하는 자바스크립트의 고유한 특징
- 함수 선언문 var이로 인해 함수를 선언하기 전에 호출하더라도 에러가 발생x
- 함수 전체가 호이스팅되어 코드의 맨위로 끌어올려진것처럼 동작
- 변수 선언문 let, const따라서 변수를 선언하기 전에 접근하면 최종적으로 undefined가 반환됨 !
- 변수의 선언만 호이스팅되며 변수의 초기화는 호이스팅되지 않음
Q. 왜 let과 const는 undefined가 아니라 ReferenceError일까?
let과 const는 변수를 선언하기 전까지 해당 변수를 사용할 수 없는 상태임.
undefined가 반환되지 않고 변수 자체가 "존재하지 않음" 상태이므로 ReferenceError가 발생!
반면, var는 선언만 호이스팅되기 때문에 초기화 전에 변수에 접근하면 undefined를 반환
💯 5. 이벤트 버블링
이벤트가 발생 시 해당 요소에서 이벤트가 처리 된 후 상위 요소로 이벤트가 전파되는 현상
⇒ 그래서 이벤트 위임을 쓸 수 있게 되는거임!!
이벤트 버블링을 통해 이벤트가 전파되는 동안 부모 요소에서도 그 이벤트를 감지할 수 있게됨
따라서 이벤트를 처리하는 핸들러 함수를 부모 요소에 등록하면, 자식 요소에 발생한 이벤트를 모두 처리할 수 있으며, 이를 통해 여러 개의 하위 요소에 발생하는 이벤트를 하나의 이벤트 핸들러로 바인딩하는 이벤트 위임 처리 ㄱㄴ해짐
💯 6. 이벤트 캡처링
이벤트 버블링과 반대로, 상단에서 하단 방향으로 이벤트가 전파됨
잘 안 쓰긴 하지만,, 이렇게 구현함 (핸들러의 3번째 인자에서!)
divs.forEach((div) => {
div.addEventListener("click", clickEvent, { capture: true });
});
💯 7. same-origin policy
Same-Origin Policy 동일 출처 정책과 CORS 에러
프로토콜, 호스트, 포트가 동일한 서버로만 ajax요청을 받도록 함! → 보안 측면에서의 정책 (공격 받을 경로를 줄이는 겨)
BUT, 이 제한을 좀 더 열어주고 특정 포트나 도메인 열어서 허용하는 방법이 CORS임!
💯 8. 동기 비동기 함수
동기, 비동기란? (+Promise, async/await 개념)
- 동기 함수 : 순서대로, 앞의 실행이 끝날 때까지 기다림[요청 - 응답] 요 이후에 그다음 태스크 진행됨
- 물론, cpu사용은 동일하더라도, 조금은 비효율적임
- 비동기 함수 : 기다리지 않고 해당 태스크를 처리할 수 있음ex) 프로미스, .then(), async/await
- 대신, 비동기 작동 이후에 반드시 프로미스를 반환!
💯 9. 이벤트 루프
이벤트 루프 == 비동기 함수들을 적절한 타이밍에 작동시키는 관리자!
이벤트 루프는 콜스택에 현재 실행 중인 컨텍스트가 있는지 또한 태스크 큐에 대기중인 함수등이 있는지 반복해서 확인하는 역할
- 여기서 만약 콜스택이 비어있고, 태스크 큐에 대기중인 함수가 존재하면 이벤트 루프는 순차적으로 태스크 큐에 대기중인 함수를 콜스택으로 이동
- 이후 콜스택으로 이동한 함수는 실행되고, 최종적으로 태스크 큐에서 일시 보관된 함수는 비동기 처리방식으로 동작
언어 자체의 설계를 바꾸는 것 보단, 브라우저의 멀티 스레드를 이용하는 자바스크립트의 비동기 프로그래밍을 지원하는 것. 그리고 이 비동기 프로그래밍의 핵심이 이벤트 루프인 겨!
💯 10. 스택과 큐
- 콜 스택: 동기처리, 일반적으로 함수의 실행 순서를 추적하는 구조로 함수 호출 시 스택에 추가하고 실행이 완료되면 콜스택에서 제거
- 테스크 큐: 비동기 함수의 콜백함수 혹은 이벤트 핸들러등을 일시적으로 보관하는 영역, 비동기 함수가 호출되면 해당 함수의 콜백함수가 태스크 큐에 추가
- 이후 이벤트 루프가 콜스택이 비어있는지 확인하고, 콜스택이 비어있을 경우에만 태스크 큐에서 콜백함수 가져와 콜스택에 추가하고 함수를 실행**(=틱)**