[인프런 워밍업 스터디 클럽 1기] FE 2주차 발자국
시작
강의가 좀 밀려있긴 한데... 기간 안엔 다 들을 수 있을 거라 생각하며 차근차근 정리 중이다...
정리
자바스크립트 this 키워드
메서드 내
this⇒ 해당 객체(Object)함수 내
this⇒ window 객체생성자 함수 내
this⇒ 빈 객체(해당 인스턴스)forEach문 내
this⇒ 두 번째 매개 변수 값, 없을 경우 window 객체 또는 해당 객체화살표 함수 내
this⇒ 상위 스코프의 this예제
메서드 내 ⇒ 해당 객체(Object)
const audio = { title: 'a', play() { console.log('play this', this); }, }; audio.stop = function () { console.log('stop this', this); }; audio.play(); //play this { title: 'a', play: f } audio.stop(); //play this { title: 'a', play: f }함수 내 ⇒ window 객체
function playAudio() { console.log(this); } playAudio(); // Window 객체생성자 함수 내 ⇒ 빈 객체(해당 인스턴스)
function Audio(title) { this.title = title; // 이게 없으면 빈 객체 console.log(this); } const audio = new Audio('a'); // Audio { title: 'a' }forEach문 내 ⇒ 두 번째 매개 변수 값, 없을 경우 window 객체 또는 해당 객체
const audio = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach(function (category) { console.log(`title: ${this.title}, category: ${category}`); // 함수 내에 있기 때문에 window 객체를 가리킴 }); }, }; audio.displayCategories(); const audio2 = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach( function (category) { console.log(`title: ${this.title}, category: ${category}`); }, { title: 'audio' } ); // forEach 두 번째 매개변수에 있는 값을 참조시킬 수 있음 }, }; audio2.displayCategories(); const audio3 = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach( function (category) { console.log(`title: ${this.title}, category: ${category}`); }, this // audio3 해당 객체를 참조 ); }, }; audio3.displayCategories();화살표 함수 내 ⇒ 상위 스코프의 this
const audio = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach(function (category) { console.log(`title: ${this.title}, category: ${category}`); // 상위 스코프의 this(Lexical this) => audio { title : 'audio', categories: Array(3) } }); }, }; audio.displayCategories();
call, apply, bind
함수에서 this 사용 시, window 객체가 아닌 값을 참조시키기 위해 사용
함수명.call(thisArg[, arg1, arg2, ...]): 함수를 호출하는 함수함수를 호출할 때, 첫 번째 매개 변수에 함수 내에서 사용할
this값을 지정할 수 있음첫 번째 매개 변수로
null또는undefined가 전달되면 전역 객체가this로 사용됨두 번째 ~ … 매개 변수로 호출할 함수에 전달될 인수 추가 가능
const fullName = function (arg1, arg2) { console.log(`${this.firstName} ${this.lastName}`); console.log(`${arg1} ${arg2}`); }; const person = { firstName: 'Im', lastName: 'Hass', }; fullName.call(person, '매개변수1', '매개변수2'); // Im Hass // 매개변수1 매개변수2함수명.apply(thisArg[, argArray]):call()과 동일call()과 인수를 배열로 넣어주는 차이
const fullName = function (arg1, arg2) { console.log(`${this.firstName} ${this.lastName}`); console.log(`${arg1} ${arg2}`); }; const person = { firstName: 'Im', lastName: 'Hass', }; fullName.apply(person, ['매개변수1', '매개변수2']);**함수명.bind(thisArg)**: 함수가 실행되지 않고 바인딩만 시켜줌함수를 바인딩할 때, 새로운 함수를 생성함 → 원본 함수와 동일한 코드를 가지지만 this가 바인딩된 값이 들어감 ⇒ 새로운 변수에 담거나 즉시 실행 시켜서 사용해야 함
function func(language) { if (language === 'kor') { console.log(`language: ${this.korGreeting}`); } else { console.log(`language: ${this.engGreeting}`); } } const greeting = { korGreeting: '안녕', engGreeting: 'Hello', }; // 원본은 변하지 않기 때문에 undefined 출력 // func.bind(greeting); // func('kor'); // language: undefined // 1. 새 변수에 담아서 사용하기 // const boundFunc = func.bind(greeting); // boundFunc('kor'); // language: 안녕 // 2. 즉시 실행하기 func.bind(greeting)('kor');
Event Loop
자바스크립트는 동기 언어지만, 다른 것의 도움(브라우저의 자바스크립트 엔진 또는 Node.js와 같은 런타임 환경)을 통해 비동기로 처리할 수 있음.
ex) setTimeout(브라우저 api-window object 또는 node api-global object)
setTimeout에 설정된 값이 0(즉시 실행)이어도 콜백 큐에서 호출 스택이 비어야 들어가기 때문에 순서가 다르게 출력됨
자바스크립트 코드를 실행하기 위해 엔진이 필요한데, 자바스크립트 엔진에는 메모리 힙, 콜 스택 두 가지 주요 구성 요소가 있음
Call Stack(호출 스택)
코드 실행에 사용되는 메모리 구조
함수 호출 및 반환을 추적함
코드가 실행될 때 함수 호출은 스택에 추가되고, 함수가 완료되면 스택에서 제거됨
Callback Queue(콜백 큐)
비동기 작업의 콜백 함수들이 대기하는 대기열
비동기 작업이 완료되면 해당 콜백 함수가 콜백 큐에 추가됨
Event Loop(이벤트 루프)
콜 스택과 콜백 큐를 계속 모니터링하여 콜 스택이 비어있을 때 콜백 큐의 첫 번째 콜백을 콜 스택으로 이동시킴
내부 진행 참고 사이트 : https://kamronbekshodmonov.github.io/JELoop-Visualizer/
Closure
외부 함수 보다 생명 주기가 긴 내부 함수 중에서, 종료된 외부 함수의 값을 참조할 수 있는 내부 함수를 의미한다.
클로저 사용 전(오류: b에 접근 불가)
let a = 'a'; function funcB() { let c = 'c'; console.log(a, b, c); } function funcA() { let b = 'b'; console.log(a, b); funcB(); } funcA(); // a b // ReferenceError: b is not defined클로저 사용 후(해결: 내부 함수로 변경)
let a = 'a'; function funcA() { let b = 'b'; console.log(a, b); function funcB() { let c = 'c'; console.log(a, b, c); } funcB(); } funcA(); // a b // a b c
구조 분해 할당
배열이나 객체의 속성을 해체하여 개별 변수에 담을 수 있게 하는 Javascript 표현식
객체 구조 분해 할당
let person = { name: 'hass', age: 30, phone: '123', address: { zipcode: 1234, street: 'rainbow', number: 42, }, }; let { address: { zipcode, street, number } } = person; console.log(zipcode, street, number);별명 지정
let people = [ { name: 'mike', age: 35, family: { mother: 'Jane', father: 'Harry', sister: 'samantha', }, }, { name: 'Tom', age: 25, family: { mother: 'Norah', father: 'Richard', brother: 'Howard', }, }, ]; for (let { name: n, family: { father: f }, } of people) { console.log(`Name : ${n}, Father: ${f}`); } // Name : mike, Father: Harry // Name : Tom, Father: Richard
Map() 메서드
배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환함
array.map(callback(currVal[, index[, array]]])[, thisArg])첫 번째 매개 변수로 callback 함수가 들어가고, 두 번째 매개 변수에 this 바인딩 가능
Filter() 메서드
주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환함
array.filter(callback(element[, index[, array]])[, thisArg])첫 번째 매개 변수로 callback 함수가 들어가고, 두 번째 매개 변수에 this 바인딩 가능
Reduce() 메서드
배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환함
arr.reduce(reducerFunction(acc, curr, idx, src), initalValue)reducerFunction4개의 인자를 가짐누산기(acc)
현재 값(curr)
현재 인덱스(idx)
원본 배열(src)
예제
const result = [0, 1, 2, 3, 4].reduce(function (acc, curr, idx, src) { return acc + curr; }, 10); // console.log(result); // 20acc curr idx src return value 1번째 호출 10 0 0 [0, 1, 2, 3, 4] 10 2번째 호출 10 1 1 [0, 1, 2, 3, 4] 11 3번째 호출 11 2 2 [0, 1, 2, 3, 4] 13 4번째 호출 13 3 3 [0, 1, 2, 3, 4] 16 5번째 호출 16 4 4 [0, 1, 2, 3, 4] 20
undefiend vs null
공통점
둘 다 원시 자료형
undefiend의 타입은undefiend,null타입은null(object로 나옴, strit 모드는null) 유일한 타입
undefined아무 값도 할당받지 않은 상태
개발자의 의도 X, 자바스크립트 엔진이 초기화한 값
null비어있는 값, 존재하지 않는 값
개발자가 의도한 값
null을 할당하면 변수가 이전에 참조하던 값을 명시적으로 참조하지 않겠다고 하는 것이므로, 자바스크립트 엔진이 변수의 메모리 공간에 대해 가비지 콜렉션을 수행함
얕은 비교(Shallow Compare)
숫자, 문자 등 원시 자료형은 값으로 비교
배열, 객체 등 참조 자료형은 참조되는 위치를 비교
Identifiers
userName seasons isFinished enemies user
Call Stack
Address Value 0012ASF “Hass” 3241AF 5 416UHDI true 1235JFT HHSYDW1462 JFFI12HA KHS18205JA
Heap
Address Value HHSYDW1462 ["ene1", "ene2", "ene3"] KHS18205JA {name: "hass", profession: "Drug dealer"}
깊은 비교(Deep Compare)
객체의 경우에도 값으로 비교
Object Depth가 깊은 경우 : lodash 라이브러리의
isEqual()사용Obejct Depth가 깊지 않은 경우:
JSON.stringfy()사용const obj1 = { a: 'a', b: 'b' }; const obj2 = { a: 'a', b: 'b' }; console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true
얕은 복사, 얕은 동결
얕은 복사 : 내부에 중첩된 값은 복사되지 않음(기존과 동일한 객체를 참조함, 하나를 변경하면 똑같이 변경됨)
얕은 동결 :
Object.freeze()메서드, 객체를 동결하여 변경할 수 없도록 함.얕은 복사를 하는 연산자 : 전개 연산자,
Oject.assign(),Array.from(),slice()
깊은 복사
내부 중첩된 값까지 복사함
JSON.parse(JSON.stringify(object))또는 중첩된 부분에 전개 연산자 사용{ …aObj, cObj: {…aObj.cObj }}lodash 라이브러리를 활용
structuredClone(object)메서드 활용
함수 선언문, 함수 표현식
함수 표현식 : 함수를 만들고 변수에 할당, 익명 함수
호이스팅 시, 함수 선언식만 영향을 받음
함수 표현식은 인터프리터가 해당 코드 줄에 도달할 때만 로드됨
함수 표현식은 정의된 로컬 변수의 복사본을 유지할 수 있도록 호이스팅 되지 않음
즉시 호출(실행) 함수 표현식(IIFE(Immediately Invoked Function Expression))
정의 하자마자 즉시 실행되는 함수를 의미함
기본 형태
( function () {} )()첫 번째 소괄호
( ... ): 전역 선언을 막고, IIFE 내부로 다른 변수의 접근을 막음두 번째 소괄호
…(): 즉시 실행 함수를 생성하는 괄호, 이를 통해 Javascript 엔진이 함수를 즉시 해석하고 실행함
IIFE를 변수에 할당하면 IIFE 자체는 저장되지 않고, 함수가 실행된 결과만 저장할 수 있음
IIFE로 Closure가 가능하게 만든 후, 내부 함수에서 외부 함수에 있는 변수에 접근되게 함
사용 목적
변수를 전역으로 선언하는 것을 피하기 위해
IIFE 내부에 다른 변수들이 접근하는 것을 막기 위해
익명 함수를 만들기 위해 (이름이 없기 위한 두 가지 조건 중 한 가지)
이 함수를 할당 받을 변수를 지정해야 함
이 함수를 즉시 호출해야 함
// 이름은 선택 사항이라고 했지만 function 키워드로 만들 때 에러 발생 // function (a, b) { // Error: Identifier expected. // return a- b; // } // 조건1 const minus = function (a, b) { return a - b; }; // 조건2 (function (a, b) { return a - b; })(1, 2);
IIFE 적용 전
const score = () => {
let count = 0;
return {
current: () => { return count; },
increment: () => { count++; },
reset: () => { count = 0; },
};
};
console.log(score); // () => {...} 함수 출력
console.log(score().current()); // 0
score.increment(); // count를 1 증가 시킴
console.log(score().current()); // 0 => score()를 다시 실행 시켜서 count가 0으로 초기화됐기 때문에 값이 증가하지 않음
IIFE 적용 후
const score = (() => {
let count = 0;
return {
current: () => { return count; },
increment: () => { count++; },
reset: () => { count = 0; },
};
})();
console.log(score); // { current: f, ... }
console.log(score.current()); // 0
score.increment(); // count를 1 증가 시킴
console.log(score.current()); // 1
Intersection observer
무한 스크롤, 이미지 lazy loading 등을 구현할 때 사용됨
Lazy Loading 예제
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Intersection Observer</title> <style> img { width: 400px; height: 300px; display: block; margin: 10px auto; } </style> </head> <body> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <script> const observer = new IntersectionObserver( function (entries, observer) { console.log('entries', entries); entries.forEach((entry) => { if (entry.isIntersecting) { // 루트 요소와 타겟 요소가 교차하면 console.log('옵저버'); entry.target.src = entry.target.dataset.src; // dataset에 있는 url을 src에 넣어줌 observer.unobserve(entry.target); // Lazy loading 후에는 observer 해제 } }); }, { threshold: 1 } // 타겟 요소가 루트 요소와 100% 겹치면 콜백 함수 실행 ); const imgEls = document.querySelectorAll('img'); console.log(imgEls); imgEls.forEach((img) => { observer.observe(img); }); </script> </body> </html>
순수 함수(Pure Function)
함수형 프로그래밍 패러다임의 한 부분
특정 함수가 다른 함수에 미치는 예기치 못한 영향을 최소화하고, 함수를 만들고 실행할 때 어떤 결과값을 리턴할지 예측할 수 있다는 장점이 있음
가능한 순수 함수를 사용하는 것이 좋음
클린 코드를 위해서
테스트를 쉽게 하기 위해서
디버그를 쉽게하기 위해서
독립적인 코드를 위해서
두 가지 규칙이 있음
같은 입력이 주어졌을 때, 언제나 같은 결과값을 리턴한다 (same input → same output)
사이드 이펙트를 만들지 않는다 (동일한 input이지만 외부 값에 의해 변경되는 output → impure하게 만듦)
커링(Curry Function)
함수와 함께 사용하는 고급 기술 (다른 언어에도 존재하는 기술)
f(a, b, c) 처럼 단일 호출로 처리하는 함수를 → f(a)(b)(c)와 같이 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합될 수 있게 변환하는 것
즉, 함수를 호출하는 것이 아닌 변환하는 것
예제
const sum = (x, y) => x + y; console.log(sum(10, 20)); // 30 const curriedSum = (x) => (y) => x + y; console.log(curriedSum(10)); // f() y => x + y console.log(curriedSum(10)(20)); // 30const makeFood = (ingredient1) => { return (ingredient2) => { return (ingredient3) => { return `${ingredient1} ${ingredient2} ${ingredient3}`; }; }; }; // const hamburger = makeFood('Bread')('Ham'); // console.log(hamburger); // (ingredient3) => {...} 완성되지 않음 const hamburger = makeFood('Bread')('Ham')('Tomato'); console.log(hamburger); // Bread Ham Tomato => 3개의 인수를 다 넣어줘야 완성됨 // makeFood 축약 const cleanMakeFood = (ingredient1) => (ingredient2) => (ingredient3) => `${ingredient1} ${ingredient2} ${ingredient3}`; const newHamburger = cleanMakeFood('Bread')('Ham')('Tomato'); console.log(newHamburger); // Bread Ham Tomato// 일반 log 함수 function log(date, importance, message) { alert(`[${date.getHours()} : ${date.getMinutes()}]: [${importance} ${message}]`); } log(new Date(), 'DEBUG', 'same bug'); // 커리 함수 function curry(f) { return function (a) { return function (b) { return function (c) { return f(a, b, c); }; }; }; } const curriedLog = curry(log); curriedLog(new Date())('DEBUG')('same bug'); // 동적으로 추가되는 커리 함수 (매개 변수 개수 상관 없이 커리 함수로 변경 가능) function curry2(func) { return function curried(...args) { if (args.length >= func.length) { return func.apply(this.args); } else { return function (...args2) { return curried.apply(this, args.concat(args2)); }; } }; } // const sum = (x, y, z) => x + y + z; const sum = (x, y, z, j) => x + y + z + j; const curriedSum = curry2(sum); // console.log(curriedSum(1)(2)(3)); console.log(curriedSum(1)(2)(3)(4));
strict mode
Javascript의 엄격 모드
제한된 버전을 선택하여 암묵적인 “느슨한 모드(sloppy mode)”를 해제하기 위한 방법
시멘틱스에 몇 가지 변경이 일어남
기존에는 무시되던 에러를 throwing
Javascript 엔진의 최적화 작업을 어렵게 만드는 실수를 바로 잡음. 가끔 엄격 모드의 코드가 비엄격 모드와 동일한 코드 보다 더 빠르게 작동하도록 만들어줌.
적용 방법
파일에
"use strict"지시자 입력함수 안에
"use strict"지시자 입력하여 해당 함수만 적용 가능class를 사용하면 자동으로 strict 모드 적용
자바스크립트 파일의 타입을 module로 설정하면 strict 모드 적용
<script src="script.js" type="module">
기존과 차이점
할당할 수 없는 값에 할당하려 할 때 ex) 선언되지 않은 변수,
undefiend,NaN수정할 수 없는 값을 수정하려 할 때 ex) readOnly 객체
동일한 이름의 파라미터가 존재할 때
함수 내의 this가
undefiend가 됨
댓글을 작성해보세요.