[인프런 워밍업 스터디 클럽 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)
reducerFunction
4개의 인자를 가짐누산기(acc)
현재 값(curr)
현재 인덱스(idx)
원본 배열(src)
예제
const result = [0, 1, 2, 3, 4].reduce(function (acc, curr, idx, src) { return acc + curr; }, 10); // console.log(result); // 20
acc 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)); // 30
const 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
가 됨
댓글을 작성해보세요.