lecture1
// 변수, 호이스팅, TDZ
// var는 한번 선언된 변수를 다시 선언 가능
var name = 'Mike';
console.log(name); // Mike
var name = 'Jane'
console.log(name); // Jane
// 같은 상황에서 let은 에러
let name = 'Mike';
console.log(name); // Mike
let name = 'Jane' // error!
console.log(name);
// var는 선언하기 전에 사용 가능
console.log(name); // undefined
var name = 'Mike';
// 왜? var는 이렇게 동작하기 때문
/* var로 선언한 모든 변수는 코드가 실제로 이동하지는 않지만
최상위로 끌어올려진 것처럼 동작 = 호이스팅
console이 undefined를 찍는 이유는 선언은 hoisting 되지만
할당은 호이스팅되지 않기 때문 */
var name;
console.log(name); // undefined
name = 'Mike'; // 할당
// 같은 상황에서 let은 에러
/* let과 const는 호이스팅되지 않는 걸까? NO
호이스팅: 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것처럼 행동
근데 왜 에러를 내는 걸까? -> Temporal Dead Zone (TDZ)때문
TDZ 영역에 있는 변수들은 사용할 수 없음
let과 const는 TDZ의 영향을 받음
할당을 하기 전에는 사용할 수가 없다.
-> 코드를 예측가능하게 하고 잠재적 버그를 줄임
*/
console.log(name); // ReferenceError
var name = 'Mike';
console.log(name) // Temporal Dead Zone
const anme = 'Mike' // 변수 선언 및 할당
console.log(name) // 사용 가능
// 문제가 발생하지 않는 코드
let age = 30;
function showAge() {
console.log(age);
}
showAge();
// 문제 발생 코드
let age = 30;
function showAge() {
console.log(age); // TDZ
let age = 20; // 호이스팅 발생
}
showAge();
/* 변수의 생성과정
1. 선언 단계
2. 초기화 단계
3. 할당 단계
var
1. 선언 및 초기화 단계
2. 할당 단계
초기화: undefined를 할당해주는 단계
let
1. 선언 단계
2. 초기화 단계
3. 할당 단계
호이스팅 되며 선언 단계가 이루어지지만
초기화 단계는 실제 코드에 도달했을 때 되기 때문에
레퍼런스 에러 발생
const
1. 선언 + 초기화 + 할당
let과 var는 선언만 해두고 나중에 할당허용
let var는 값을 바꿀 수 있기 때문에 어찌보면 당연
*/
// const
let name;
name = 'Mike';
var age;
age = 30;
const gender; // error! 선언하며 바로 할당안함
gender = 'male';
/*
var는 함수 스코프
함수 내에서 선언된 변수만 그 지역변수
let, const는 블록 스코프(함수, if, for, while, try-catch 등)
블록 스코프는 모든 코드 블럭 내에서 선언된 변수는
코드 블록 내에서만 유효
외부에서는 접근 불가능
즉 코드 블록 내부에서 선언한 변수는 지역 변수
*/
// if문 안에서 var로 선언한 변수는 if문 밖에서도 사용 가능
// let과 const는 중괄호 안에서만 사용 가능 (블록 스코프)
const age = 30;
if (age > 19) {
var txt = '성인';
}
console.log(txt); // '성인'
// var도 함수 내에서 선언되면 함수 밖에서 사용할 수 없음
// 유일하게 벗어날 수 없는 스코프가 함수
function add(num1, num2) {
var result = num1 + num2;
}
add(2, 3);
console.log(result);
lecture2
// 생성자 함수
// 객체 리터럴
// 개발을 하다보면 이런 비슷한 변수들을 여러개 만들어야 하는 경우가 발생 ex. 회원, 상품 -> 생성자 함수
let user = {
name : 'Mike',
age : 30,
}
// 생성자 함수 첫 글자는 보통 대문자로
// new 연산자를 사용하여 함수 호출
// 생성자 함수는 붕어빵 틀, 와플 틀
// 지금 필요한 재료는 이름과 나이
// 생성되는 객체들은 와플
function User(name, age) {
this.name = name;
this.age = age;
}
let user1 = new User('Mike', 30);
let user2 = new User('Jane', 22);
let user3 = new User('Tom', 17);
// new를 붙여 실행하는 순간 이 방식대로 알고리즘이 동작
// 이렇게 객체를 만들면 일일이 객체를 만드는 것보다 훨씬
// 빠르고 일관성있게 객체를 만들 수 있다.
// 어떤 함수라도 new를 붙여서 실행하면 이 알고리즘이 동작
function User(name, age) {
this = {} // 실제로 없는 코드 : 빈 객체를 만들어 this에 할당
this.name = name; // this에 프로퍼티들 추가
this.age = age;
return this; // 실제로 없는 코드 : this 반환
}
new 함수명();
// user5.sayName() 했을 때 sayName의 this는 user5
function User(name, age) {
this.name = name;
this.age = age;
this.sayName = function() { // this로 할당된 객체에 sayName 메소드 추가
console.log(this.name); // user5.sayName을 호출 했을 때 sayName 함수의 this는 user5
}
}
let user5 = new User('Han', 40);
user5.sayName(); // 'Han'
// 실습
// 생성자 함수: 상품 객체를 생성해보자.
function Item(title, price) {
// this = {};
this.title = title;
this.price = price;
this.showPrice = function() {
console.log(`가격은 ${price}원 입니다.`);
}
// return this;
}
// 만일 new를 안붙인다면 undefined
// new를 안붙이면 그냥 함수를 실행해주는 것
// 리턴해주는 게 없기 때문에 underined 반환하고 item2로 들어감
const item1 = new Item('인형', 3000);
const item2 = Item('가방', 2000);
const item3 = new Item('지갑', 5000);
console.log(item1, item2, item3);
item3.showPrice();
lecture3
// 객체 메소드, 계산된 프로퍼티
// Object methods, Computed property
// 컴퓨티트 프로퍼티
// 대괄호로 묶어주면 a라는 문자열이 아니라 변수a에 할당된 값이 들어감
// 식 자체를 넣는 것도 가능
let a = 'age';
const user = {
name: 'Mike',
[a]: 30,
[1 + 4] : 5,
['안녕' + '하세요'] : 'hello'
}
// 객체에서 사용할 수 있는 몇 가지 메소드
/*
Object.assign()
Object.keys()
Object.values()
Object.entries()
Object.fromEntries()
*/
// Object.assign() : 객체 복제
const user = {
name : 'Mike',
age : 30
}
// 이렇게 하면 복제되는 걸까? NO
const cloneUser = user;
//user에는 객체 자체가 들어있는 게 아니라 객체가 저장되어 있는 메모리 주소인 객체에 대한 참조값이 저장되므로
// clonUser를 만들어서 user를 넣으면 객체가 복사되며 들어가는게 아니라 참조값만 복사
// cloneUser의 이름을 바꾸면 user의 이름도 바뀜
// 하나의 객체를 두 변수가 접근하고 있는 것
cloneUser.name = 'Tom';
// 따라서 객체를 복제해주기 위해선 Object.assign() 사용
// 빈 객체는 초기값, 두번째 매개변수부터 들어온 값이 초기값에 병합됨
// {} + {name:'Mike', age:30} = {name:'Mike', age:30}
const newUser = Object.assign({}, user);
//이름을 바꿔도 user에는 변함 x
newUser.name = 'Tom';
console.log(user.name); // 'Mike'
newUser != user
// {gender:'Male', name:'Mike', age:30} : 총 3개의 프로퍼티를 가지게 됨
const newUser = Object.assign({gender:'Male'}, user);
// 병합을 하는데 키가 갖다면? 덮어쓰게 됨
// {name:'Mike', age:30}
const newUser = Object.assign({name:'Tom'}, user);
// 두개 이상의 객체도 합칠 수 있음
const user = { name:'Mike'}
const info1 = { age:30}
const info2 = { gender:'Male'}
const newUser = Object.assign({}, info1, info2);
const user = {
name : 'Mike',
age : 30,
gender : 'male',
}
// Object.keys() : 키 배열 반환
Object.keys(user);
["name", "age", "gender"]
// Object.values() : 값 배열 반환
Object.values(user);
["Mike", 30, "male"]
// Object.entries() : 키/값 배열 반환
Object.values(user);
[
["name", "Mike"]
["age", "30"]
["gender", "male"]
]
// Object.fromEntries() : 키/값 배열을 객체로
const arr =
[
["name", "Mike"]
["age", "30"]
["gender", "male"]
];
Object.fromEntries(arr);
{
name : 'Mike',
age : 30,
gender : 'male',
}
// 실습
let n = "name";
let a = "age";
const user = {
[n] : 'Mike',
[a] : 30,
[1 + 4] : 5,
};
console.log(user);
function makeObj(key, val) {
return {
[key] : val
}
}
const obj = makeObj('나이', 33);
console.log(obj);
lecture4
// 심볼 (Symbol)
// property key : 문자형
const obj = {
1: '1입니다.',
false : '거짓'
}
// 숫자형이나 불리언으로 만들어도 프로퍼티 키들을 가져와보면 문자형으로 반환
Object.keys(obj); // ["1", "false"]
// 접근할 때도 obj['1']이나 obj['false']로 접근할 수 있음
// Symbol : 유일한 식별자를 만들 때 사용
const a = Symbol(); // new를 붙이지 않음
const b = Symbol();
// a===b, a==b 둘 다 false
// Symbol형은 유일성 보장
// 심볼을 만들 때 설명을 붙여 줄 수도 있음
// 설명을 붙이면 디버깅할 때 편함
// 문자열을 전달해주면 되는데 이 문자열은 심볼 생성에 어떤 영향도 끼치지 않음
const id = Symbol('id');
const id2 = Symbol('id');
// 설명을 똑같이 해도 id===id2, id==id2 false
// 심볼을 객체의 키로 사용해보자
const id = Symbol('id');
const user = {
name : 'Mike',
age : 30,
[id] : 'myid'
}
console.log(user);
// {name:'Mike', age:30, Symbol(id):"myid"}
console.log(user[id]); // "myid"
// 이런 메소드들은 키가 심볼형인 프로퍼티는 건너뜀
// for in을 써도 건너 뜀
Object.keys(user); // ["name", "age"]
Object.values(user); // ["Mike", "30"]
Object.entries(user); // [Array(2), Array(2)]
// 그럼 이 꽁꽁 숨겨진 심볼을 어디에 쓸까?
// 특정 객체의 원본 데이터는 건들이지 않고 속성 추가할 수 있음
// 다른 사람이 만든 객체에 자신만의 속성을 추가해서 덮어쓰면 안됨
// 그렇다고 길고 이상한 네이밍을 하는 것도 좋지 않음
// 만약 그 원본 객체가 어딘가에서 Object.keys나 for in으로 순회하면서 데이터를 사용할 수도 있음
// 내가 추가한 프로퍼티가 어디서 어떻게 튀어나올지 예측 불가능
// 이런식으로 심볼은 이름이 같더라도 모두 다른 존재
// 그런데 가끔 전역변수처럼 이름이 같으면 같은 객체를 가르켜야할 때가 있는데 이럴 때 사용하는게
/* Symbol.for() : 전역 심볼
하나의 심볼만 보장받을 수 있음
없으면 만들고, 있으면 가져오기 때문
Symbol을 함수는 매번 다른 Symbol을 값을 생성하지만
Symbol을.for 메소드는 하나를 생성한 뒤 키를 통해 같은 Symbol을 공유
코드 어디에서든 사용 가능*/
const id1 = Symbol.for('id');
const id2 = Symbol.for('id');
id1 === id2; // true
// 이름을 얻고 싶다면
// keyFor에 변수를 넣어주면 생성할 때 적어준 이름을 알려줌
Symbol.keyFor(id1) // "id"
// 전역 심볼이 아닌 경우는 keyFor사용불가능
// 대신 description 사용
const id = Symbol('id입니다.');
id.description; // "id입니다."
// 사실 심볼을 완전히 숨길 수 있는 방법은 없음
// 숨겨진 심볼 키 보는 법
const id = Symbol('id');
const user = {
name : 'Mike',
age : 30,
[id] : 'myid'
}
// 심볼들만 볼 수 있음
Object.getOwnPropertySymbols(user); // [Symbol(id)]
// 심볼형 키를 포함한 모든 키를 보여줌
Reflect.ownKeys(user); // ["name", "age", Symbol(id)]
// 사실 대부분의 라이브러리, 내장함수는 이런 메소드 사용안하니 유일한 프로퍼티를 추가하고 싶을 때 심볼을 사용하자
// 실습
// 다른 개발자가 만들어 놓은 객체
const user = {
name : 'Mike',
age : 30,
};
// 내가 작업
user.showName = function() {};
// His showName is function() {} 말도 안되는 메세지 출력
const showName = Symbol('show name');
user[showName] = function () {
console.log(this.name);
}
// Symbol을 만들었기 때문에 for in문에서 걸리지 않음
// name, age만 출력
user[showName]();
// 이렇게 하면 Mike도 잘 나옴
// 내가 추가한 메소드도 잘 동작하고 다른 개발자가 만들어 둔 코드에도 영향을 미치지 않는 선에서 메소드 추가
// 원래 user에 showName이라는 메소드가 있었는지 고민할 필요도 없고 다른 사람이 만들어 둔 프로퍼티를 덮어쓸 일도 없음
// 이게 바로 심볼의 장점!
// 사용자가 접속하면 보는 메세지
for (let key in user) {
console.log(`His ${key} is ${user[key]}.`);
}
// His name is Mike
// His age is 30
lecture5
// Number, Math
// 10진수 -> 2진수, 16진수로 바꾸는 법
// toString
let num = 10;
num.toString(); // "10"
num.toString(2); // "1010"
let num2 = 255;
num2.toString(16); // "ff"
// Math
Math.PI // 원주율
Math.ceil(n) // 올림
Math.floor(n) // 버림
Math.round(n) // 반올림
// 소수점 자릿수
let userRate = 30.1234;
// 소수점 둘째자리까지 표현(셋째 자리에서 반올림)
Math.round(userRate * 100) / 100 // 30.12
// toFixed() : 문자열 반환 주의
userRate.toFixed(2) // "30.12"
userRate.toFixed(0) // "30"
userRate.toFixed(6)// "30.123400"
Number(userRate.toFixed(2)) // 30.12
// isNaN() : NaN인지 아닌지 판단
let x = Number('x'); // Nan
// NaN은 자기자신과도 같지 않다고 판단
x == NaN // false
x === NaN // false
NaN == NaN // false
// isNaN()만이 NaN인지 아닌지 판단 가능
isNaN(x) // true
isNaN(3) // false
// parseInt() : 문자열을 숫자로 바꿔줌
let margin = '10px';
// Number와 다른 점은 문자가 혼용되어 있어도 동작
// 읽을 수 있는 부분까지 읽고 숫자로 반환
parseInt(margin); // 10
Number(margin); // NaN
// 그렇기 때문에 숫자로 시작하지 않으면 NaN반환
let redColor = 'f3';
parseInt(redColor); // NaN
// parseInt는 두번째 인수를 받아 진수 지정 가능
let redColor = 'f3';
parseInt(redColor, 16); // 243
parseInt('11', 2) // 3
// parseFloat()은 parseInt와 동일하게 동작하지만 부동소수점 반환
// parseInt는 소수점이하는 무시하고 정수만 반환
let padding = '18.5%';
parseInt(padding); // 18
parseFloat(padding); // 18.5
// Math.random() 0~1사이의 무작위 숫자 생성
// 1~100 사이 임의의 숫자를 뽑고 싶을 때
Math.floor(Math.random() * 100) + 1
// Math.max(), Math.min()
Math.max(1, 4, -1, 5, 10, 9, 5.54); // 10
Math.max(1, 4, -1, 5, 10, 9, 5.54); // -1
// Math.abs() : 절대값
Math.abs(-1) // 1
// Math.pow(n, m) : 제곱
Math.pow(2, 10); // 1024
// Math.sqrt() : 제곱근
Math.sqrt(16) // 4
lecture6
// String 문자열
// html코드 같은 경우 작은 따옴표로 감싸는게 편하고
// 영어는 큰 따옴표로 감싸는게 편함
// 백틱은 ${}를 이용해 변수와 표현식 표현 가능
// 백틱은 여러줄 포함 가능
// 따옴표로 여러줄 쓰려면 \n을 쓰고 한줄에 써야함
// str.length : 문자열 길이
let desc = '안녕하세요.';
desc.length // 6
// 특정 위치에 접근 가능
// 배열과 동일하게 0부터 시작하나 바꿀 수는 없음
desc[2] // '하'
// toUpperCase(), toLowerCase()
let desc = "Hi"
desc.toUpperCase() // "HI"
desc.toLowerCase() // "hi"
// str.indexOf(text)
// 0부터 세고 찾는 문자열이 없으면 -1 반환
// 포함된 문자가 여러개라면 처음 문자만 반환
let desc = "Hi guys. Nice to meet you."
desc.indexOf('to'); // 14
// 주의
// Hi가 맨 처음에 있어 0을 반환하므로 조건문이 거짓이 됨
if (desc.indexOf('Hi')) {
console.log('Hi가 포함된 문장입니다.');
}
// 따라서 -1보다 큰가로 비교하면 됨
if (desc.indexOf('Hi') > -1) {
console.log('Hi가 포함된 문장입니다.');
}
// str.slice(n, m) n은 시작점
// m은 없으면 문자열 끝까지, 양수면 그 숫자까지(포함x), 음수면 끝에서부터 셈
let desc = "abcdefg";
desc.slice(2); // "cdefg"
desc.slice(0, 5); // "abcde"
desc.slice(2, -2); // "cde"
// str.substring(n, m) : n과 m사이의 문자열 반환
// 슬라이스와 유사하지만 m과 n을 바꿔도 동작
// n부터 m까지보다는 n부터 m사이라고 생각
// 음수는 허용하지 않음 0으로 인식
let desc = "abcdefg";
desc.substring(2, 5); // "cde"
desc.substring(5, 2); // "cde"
// str.substr(n, m)
// n부터 시작해서 m개를 가져옴
let desc = "abcdefg";
desc.substr(2, 4); // "cdef"
desc.substr(-4, 2); // "de"
// str.trim(n, m) : 앞 뒤 공백 제거
// n부터 시작해서 m개를 가져옴
// 사용자로부터 입력받을 때 자주 사용
let desc = " coding ";
desc.trim(); // "coding"
// str.repeat(n) : 문자열 n번 반복
let desc = "hello!";
desc.repeat(3); // "hello!hello!hello!"
// 문자열 비교
"a" < "c" // true
"a".codePointAt(0); // 97
String.fromCodePoint(97) // "a"
// 있는지 없는지만 알고 싶을 때 str.includes()
// includes는 문자가 있으면 true
// 없으면 false 반환
str.includes("콜라")
lecture7
// 배열 메소드
/*
push() : 뒤에 삽입
pop() : 뒤에 삭제
unshift() : 앞에 삽입
shift() : 앞에 삭제
*/
// arr.splice(n, m) : 특정 요소 지움
// n번째 요소부터 m개 지워라
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 2);
console.log(arr); // [1, 4, 5]
// arr.splice(n, m, x) : 특정 요소 지우고 추가
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 3, 100, 200); // 2, 3, 4가 지워지고 100과 200이 들어감
console.log(arr); // [1, 100, 200, 5]
// 두번째 인수에 0을 넣으면?
// 아무것도 지우지 않고 중간에 새로운 요소 추가
let arr = ["나는", "철수", "입니다"];
arr.splice(1, 0, "대한민국", "소방관");
// ["나는", "대한민국", "소방관","철수", "입니다"];
// arr.splice() : 삭제된 요소 반환
let arr = [1, 2, 3, 4, 5];
let result = arr.splice(1, 2);
console.log(arr); // [2, 3]
// arr.slice(n, m) : n부터 m까지 반환
let arr = [1, 2, 3, 4, 5];
arr.slice(1, 4); // [2, 3, 4]
// 괄호 안에 아무것도 안넣으면 배열 복사
let arr2 = arr.slice();
console.log(arr2); // [1, 2, 3, 4, 5]
// arr.concat(arr2, arr3 .. ): 합쳐서 새 배열 반환
let arr = [1, 2];
arr.concat([3, 4]); // [1, 2, 3, 4]
arr.concat([3, 4], [5, 6]); // [1, 2, 3, 4, 5, 6]
arr.concat([3, 4], 5, 6); // [1, 2, 3, 4, 5, 6]
// arr.forEach(fn) : 배열 반복
// 함수를 인수로 받음. 함수에는 3가지 매개변수
// 해당 요소, 인덱스, 배열 자체
// 보통 첫 번째와 두 번째만 사용
let users = ['Mike', 'Tom', 'Jane'];
users.forEach((item, index, arr) => {
// item: Mike, Tom, Jane
// index: 0, 1, 2
// arr: users
});
users.forEach((item, index, arr) => {
console.log(`${index + 1}. ${name}`);
});
// arr.indexOf / arr.lastIndexOf
// 발견하면 해당 요소 인덱스, 없으면 -1 반환
let arr = [1, 2, 3, 4, 5, 1, 2, 3];
arr.indexOf(3); // 2
// 두 번째 인수는 시작 위치 4부터 시작해서 3탐색
arr.indexOf(3, 3) // 7
// 끝에서부터 탐색하고 싶을 때
arr.lastIndexOf(3); // 7
// arr.includes() : 포함하는지 확인
// 굳이 인덱스를 확인할 필요는 없고 포함하는지만 확인하고 싶을 때
let arr = [1, 2, 3];
arr.includes(2); // true
arr.includes(8); // false
// arr.find(fn), arr.findIndex(fn)
// indexOf처럼 찾는다는 의미는 동일하지만 보다 복잡한 연산이 가능하도록 함수를 연결할 수 있음. 짝수, 성인 찾기 등
// 첫 번째 true값만 반환하고 끝남
// find는 없으면 undefined를 반환
// findIndex는 없으면 -1을 반환
let arr = [1, 2, 3, 4, 5];
const result = arr.find((item) => {
return item % 2 === 0;
}); // 리턴 값이 트루일 때 멈추고 해당 요소를 알려줌
console.log(result); // 2
let userList = [
{ name: "Mike", age: 30 },
{ name: "Jane", age: 27 },
{ name: "Tom", age: 10 }
];
const result = userList.find((user) => {
if (user.age < 19) {
return true;
}
return false;
});
console.log(result); // { name: "Tom", age: 10 }
const result = userList.findIndex((user) => {
if (user.age < 19) {
return true;
}
return false;
});
console.log(result); // 2
// arr.filter(fn) : 만족하는 모든 요소를 배열로 반환
// find와 달리 조건을 만족하는 모든 요소를 알고 싶을 때
let arr = [1, 2, 3, 4, 5];
const result = arr.filter((item) => {
return item % 2 === 0;
});
console.log(result); // [2, 4, 6]
// arr.reverse() : 배열을 역순으로 재정렬
let arr = [1, 2, 3, 4, 5];
arr.reverse(); // [5, 4, 3, 2, 1]
// arr.map(fn) : 함수를 받아 특정 기능을 시행하고 새로운 배열을 반환
let userList = [
{ name: "Mike", age: 30 },
{ name: "Jane", age: 27 },
{ name: "Tom", age: 10 }
];
let newUserList = userList.map((user, index) => {
return Object.assign({}, user, {
id: index + 1,
isAdult: user.age > 19,
});
});
console.log(newUserList);
// usreList는 변경된 게 없음
console.log(userList);
// join
// 배열을 합쳐서 문자열을 만들려면 join
let arr = ["안녕", "나는", "철수야"];
// 인수로 전달되는 게 구분자
// 아무것도 전달하지 않으면 쉼표로 구분되어 합쳐짐
let result = arr.join(); // 안녕,나는,철수야
let result = arr.join(" "); // 안녕 나는 철수야
console.log(result);
// split
// 문자열을 나눠서 배열로 만들어줌
let users = "Mike,Jane,Tom,Tony"
let result = users.split(",") // ["Mike", "Jane", "Tom", "Tony"]
console.log(result);
let str = "Hello, My name is Mike."
let result = str.split("") // ["H", "e", "l", "l", "o", " ", ... "."]
console.log(str);
// Array.isArray()
// 배열인지 아닌지 확인
// 자바스크립트에서 배열은 객체에 속함
let user = {
name: "Mike",
age: 30,
}
let userList = ["Mike", "Jane", "Tom"];
console.log(typeof user); // object
console.log(typeof userList); // object
console.log(Array.isArray(user)); // false
console.log(Array.isArray(userList)); // true
lecture8
// 배열 메소드2
// arr.sort()
// 배열 재정렬, 배열 자체가 변경되니 주의
let arr = [1, 5, 4, 2, 3];
arr.sort(); // [1, 2, 3, 4, 5]
let arr = ["a", "c", "d", "e", "b"];
arr.sort(); // ["a", "b", "c", "d", "e"]
// 정렬할 때 요소를 문자열로 취급
// 1과 2로 시작하는 애들이 맨 앞에 온 것
let arr = [27, 8, 5, 13];
arr.sort(); // [13, 27, 5, 8]
// 제대로 된 정렬을 하기 위해 값을 비교해줄 수 있는 함수를 전달해야 함
// sort는 함수를 인자로 받음
let arr = [27, 8, 5, 13];
function fn(a, b) {
return a - b;
}
arr.sort(fn);
arr.sort((a, b) => {
return a - b; // 양수, 0, 음수 판단
});
// 작은 애를 앞으로 보냄
// 8과 27비교 8이 작으니 앞으로: 8, 27, 5, 13
// 5와 8비교 5가 작으니 앞으로: 5, 8, 27, 13
// 13과 5비교 13 - 5 -> 양수 변화
// 13과 8비교 변화x
// 13과 27비교 13이 앞으로: 5, 8 ,13, 27
// 복잡하므로 Lodash같은 라이브러리 사용
// _.sortBy(arr)로 정렬(숫자든 문자든 객체든) 가능
//arr.reduce() 인수로 함수를 받음
// (누적 계산값, 현재값) => { return 계산값 };
// 배열의 모든 수 합치기
let arr = [1, 2, 3, 4, 5];
let result = 0;
arr.forEach(num => {
result += num;
})
console.log(result);
// 이 작업을 한번에 해주는 게 reduce
const result = arr.reduce((prev, cur) => {
return prev + cur;
}, 0) // 초기값은 0 (옵션) 안쓰면 첫 번재 요소가 들어감
console.log(result);
// 실용적인 예제
// 성인만 출력
let userList = [
{ name: "Mike", age: 30 },
{ name: "Jane", age: 10 },
{ name: "Tom", age: 27 },
{ name: "Sue", age: 42 },
{ name: "Harry", age: 60 },
];
let result = userList.reduce((prev, cur) => {
if (cur.age > 19) {
prev.push(cur.name);
}
return prev
}, [])
// arr.reduceRight() : reduce와 동일한 기능 배열 우측부터 연산 수행
lecture9
// 구조 분해 할당
// Destructing assignment
// 구조 분해 할당 구문은 배열이나 객체의 속성을 분해해서 그 값을 변수에 담을 수 있게 하는 표현식
// 배열 구조 분해
let [x, y] = [1, 2];
console.log(x); // 1
console.log(y); // 2
let users = ['Mike', 'Tom', 'Jane'];
let [user1, user2, user3] = users;
// let user1 = users[0];
// let user2 = users[1];
// let user3 = users[2];
let str = "Mike-Tom-Jane";
let [user1, user2, user3] = str.split('-');
// ['Mike', 'Tom', 'Jane']
// 배열 구조 분해:기본값
// c에는 undefined가 들어감
let [a, b, c] = [1, 2];
// 만약 값이 undefined면 기본값 사용
let [a=3, b=4, c=5] = [1, 2]; // 1, 2, 5
console.log(a); // 1
console.log(b); // 2
console.log(c); // 5
// 배열 구조 분해: 일부 반환값 무시
// 공백과 쉼표를 이용해 필요하지 않은 요소 무시
let [user1, , user2] = ['Mike', 'Tom', 'Jane', 'Tony'];
console.log(user1); // 'Mike'
console.log(user2); // 'Tony'
// 배열 구조 분해: 바꿔치기
[a, b] = [b, a];
// 객체 구조 분해
let user = { name: "Mike", age: 30 };
let {name, age} = user;
// 아래 코드와 같음
let name = user.name;
let age = user.age;
// 배열 구조 분해와 다른 점: 순서를 신경쓰지 않아도 됨
let {age, name} = user;
// 객체 구조 분해: 새로운 변수 이름으로 할당
let user = { name: "Mike", age: 30 };
let {name: userName, age: userAge} = user;
// 객체 구조 분해: 기본값
let user = { name: "Mike", age: 30 };
// gender은 undefined
let {name, age, gender} = user;
// user 객체에 gender가 없으면 male이 기본적으로 할당
let {name, age, gender = 'male'} = user;
let user = { name: "Mike", age: 30, gender: 'female' };
let {name, age, gender = 'male'} = user;
// user에 gender가 있다면 그 값이 사용
// 객체로부터 받은 값이 undefined일 때만 기본 값 사용
console.log(gender); // 'male'
lecture10
// 나머지 매개변수, 전개 구문
// 인수 전달
// 자바스크립트에서 함수에 넘겨주는 인수 개수에 제한 없음
function showName(name) {
console.log(name);
}
// 이름을 하나 더 전달한다면? 에러 발생x 'Mike'
showName('Mike', 'Tom');
// 에러 발생x name에 undefined가 찍힐 뿐
showName();
// arguments
/* 함수로 넘어 온 모든 인수에 접근
함수 내에서 이용 가능한 지역 변수
length, index
Array 형태의 객체
배열의 내장 메소드 없음(forEach, map) */
function showName(name) {
console.log(arguments.length); // arguments로 접근 가능
console.log(arguments[0]);
console.log(arguments[1]);
}
showName('Mike', 'Tom');
// 2
// 'Mike'
// 'Tom'
// 나머지 매개변수
// 정해지지 않은 개수의 인수를 배열로 나타낼 수 있게 함
// ...을 찍고 뒤에 배열 이름을 정해줌
// 배열에 전달된 인수들이 들어감
// 아무것도 전달하지 않으면 빈 배열이 나타남
function showName(...names) {
console.log(names);
}
showName(); // []
showName('Mike'); // ['Mike']
showName('Mike', 'Tom'); // ['Mike', 'Tom']
// ex 매번 전달하는 숫자의 개수가 다르다고 할 때 모든 숫자의 합을 구하려면?
function add(...nums){
let result = 0;
nums.forEach((num) => (result += num));
console.log(result);
}
add(1, 2, 3);
add(1, 2, 3, 4, 5, 6);
// 리듀스 사용
function add(...nums){
let result = 0;
nums.reduce((prev, cur) => prev + cur);
console.log(result);
}
add(1, 2, 3);
add(1, 2, 3, 4, 5, 6);
// ex user 객체를 만들어 주는 생성자 함수 만들기
// 나머지 매개변수는 항상 마지막에 있어야 함
function User(name, age, ...skills){
this.name = name;
this.age = age;
this.skills = skills;
}
const user1 = new User('Mike', 30, 'html', 'css');
const user2 = new User('Tom', 20, 'JS', 'React');
const user1 = new User('Jane', 10, 'English');
// 전개 구문: 배열
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let result = [...arr1, ...arr2]; //arr1은 1,2,3, arr2는 4,5,6
console.log(result); // [1, 2, 3, 4, 5, 6]
let result = [0, ...arr1, ...arr2, 7, 8, 9];
// [0,1,2,3,4,5,6,7,8,9]
// arr.push(), arr.splice(), arr.concat().. 안해도 됨
// 전개 구문: 객체
// Object.assign을 쓸 필요가 x
let user = {name: 'Mike'}
let mike = {...user, age:30}
console.log(mike) // {name:"Mike", age:30}
// 전개 구문: 복제
let arr1 = [1, 2, 3];
let arr2 = [...arr]; // [1, 2, 3]
let user = {name:'Mike', age:30};
let user2 = {...user};
user2.name = "Tom";
console.log(user.name); // 'Mike'
console.log(user2.name); // 'Tom'
// ex arr1을 [4,5,6,1,2,3]으로 하려면?
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr2.forEach((num) => {
arr1.unshift(num);
})
console.log(arr1); // [6,5,4,1,2,3]
arr2.reverse().forEach((num) => {
arr1.unshift(num);
})
console.log(arr1); // [4,5,6,1,2,3]
// 전개구문 사용
arr1 = [...arr2, ...arr1];
// ex user에 info를 넣고 fe와 lang을 skills을 만들어 넣기
let user = {name:'Mike'};
let info = {age:30};
let fe = ["JS", "React"];
let lang = ["Korean", "English"];
// 전개 구문 미사용시
user = Object.assign({}, user,info,{
skills: []
});
fe.forEach(item => {
user.skills.push(item);
});
lang.forEach(item => {
user.skills.push(item);
});
// 전개 구문 사용해보자
user = {
...user,
...info,
skills : [...fe, ...lang],
};
console.log(user);
lecture11
// 클로저
// 자바스크립트는 어휘적 환경
// here
let one;
one = 1;
function addOne(num) {
console.log(one + num);
}
addOne(5);
/* 코드가 실행되면
스크립트 내에서 선언한 변수들이 Lexical 환경에 올라감
Lexical 환경
one: 초기화 x -> 사용 불가
addOne: function 함수 선언문은 변수와 달리 바로 초기화 -> 사용 가능 (변수에 할당한 함수 표현식은 불가능)
*/
let one; // here
one = 1;
function addOne(num) {
console.log(one + num);
}
addOne(5);
/* Lexical 환경
one: undefined 아직 할당은 안되어 있기 때문에 초기값 undefined를 가짐 -> 사용 가능
addOne: function
*/
let one;
one = 1; // here
function addOne(num) {
console.log(one + num);
}
addOne(5);
/* Lexical 환경
one: 1 숫자 1 할당
addOne: function
*/
let one;
one = 1;
function addOne(num) {
console.log(one + num);
}
addOne(5); // here
/* 전역 Lexical 환경
one: 1 숫자 1 할당
addOne: function
함수가 실행되는 순간 새로운 Lexical 환경이 만들어짐
함수가 넘겨받은 매개변수와 지역변수들이 저장
내부 Lexical 환경
num: 5
내부 Lexical 환경은 외부 Lexical환경에 대한 참조를 가짐
지금은 저 함수의 외부 Lexical환경이 전역 Lexical환경임
코드에서 변수를 찾을 때 내부에서 찾고 없으면 외부, 거기에도 없으면 전역 Lexical 환경까지 범위를 넓혀서 찾음
console.log(one + num)코드의 one과 num은 내부 Lexical 환경에서 우선 찾음
num은 찾았지만 one이 없으니 외부로 넓혀서 있는지 찾음
*/
// 클로저는 함수와 그 함수의 렉시컬 환경의 조합
// 함수가 생성될 당시의 외부 변수를 기억
// 생성 이후에도 그 변수에 계속 접근 가능
// 외부 함수의 실행이 끝나 소멸된 이후에도 내부 함수가 외부 함수의 변수에 접근 가능
// 은닉화
lecture12
// setTimeout 일정 시간이 지난 후 함수를 실행
// setInterval 일정 시간 간격으로 함수 반복
// setTimeout
// 두 개의 매개변수를 받음
// 일정 시간이 지난 뒤 실행하는 함수와 시간
function fn() {
console.log(3)
}
setTimeout(fn, 3000); // = 3초
// 아래와 동일
setTimeout(function() {
console.log(3)
}, 3000);
// 인수가 필요하면 시간 뒤에 적어줌
// clearTimeout()은 예정된 작업을 없앰
// setTimeout은 타임 아이디를 반환하는데 이를 이용해 스케줄링을 취소할 수 있음
function showName(name) {
console.log(name);
}
const tId = setTimeout(showName, 3000, 'Mike'); // 함수, 시간, 인수
clearTimeout(tId); // 3초가 지나기 전에 이 코드가 실행되기 때문에 아무 일도 일어나지 않음
// setInterval 계속 반복 수행
function showName(name) {
console.log(name);
}
const tId = setInterval(showName, 3000, 'Mike');
clearTimeout(tId); // 중단하려면
// 주의
// 딜레이 타임을 0으로 해도 바로 실행되지 않음
setTimeout(function() {
console.log(2)
}, 0);
console.log(1)
// 1이 먼저 찍히고 2가 나중에 찍힘
// 현재 실행 중인 스크립트가 종료된 이후 스케줄링 함수를 실행하기 때문
// 또한 브라우저는 기본적으로 4ms~ 정도의 대기시간이 있음
// ex
let num = 0;
function showTime() {
console.log(`안녕하세요. 접속한지 ${num++}초가 지났습니다.`);
if (num > 5) {
clearInterval(tId);
}
}
const tId = setInterval(showTime, 1000);
lecture13
// call, apply, bind
// 함수 호출 방식과 관계없이 this를 지정할 수 있음
// call 메소드는 모든 함수에서 사용할 수 있으며, this를 특정값으로 지정할 수 있습니다.
const mike = {
name: "Mike",
};
const tom = {
name: "Tom",
};
function showThisName() {
console.log(this.name);
}
// 여기서 this는 window를 가리킴
// window.name이 빈 문자열이기 때문에 아무것도 안뜸
showThisName();
// 함수를 호출하며 call을 사용하고 this로 사용할 객체를 넘기면 해당 함수가 주어진 겍체의 메소드인 것 처럼 사용 가능
// call의 첫 번째 매개변수는 this로 사용할 값이고 매개변수가 더 있으면 그 매개변수를 호출하는 함수로 전달 됨
showThisName.call(mike); // Mike
// 생년과 직업을 받아 이 객체정보를 새로운 데이터로 업데이트
function update(birthYear, occupation) {
this.birthYear = birthYear;
this.occupation = occupation
}
// 첫 번째 매개변수는 this로 사용될 값
// 이후 매개변수부터는 함수가 사용할 매개변수들을 순서대로 적은 것
update.call(mike, 1999, "singer");
console.log(mike); // {name: "Mike", birthYear: 1999, occupation: "singer"}
// apply : 함수 매개변수를 처리하는 방법을 제외하면 call과 완전히 같음
// call은 일반적인 함수와 마찬가지로 매개변수를 직접 받지만, apply는 매개변수를 배열로 받음
function update(birthYear, occupation) {
this.birthYear = birthYear;
this.occupation = occupation
}
update.apply(mike, [1999, "singer"]);
console.log(mike); // {name: "Mike", birthYear: 1999, occupation: "singer"}
// apply는 배열 요소를 함수 매개변수로 사용할 때 유용
const nums = [3, 10, 1, 6, 4];
// apply는 두 번째 매개변수로 배열을 전달하면 그 요소들을 차례대로 인수로 사용
// null은 this로 사용될 값 (min이나 max는 this가 필요하지 않아서 아무 값이나 넣은 것)
const minNum = Math.min.apply(null, nums);
// = Math.min.apply(null, [3, 10, 1, 6, 4])
const maxNum = Math.max.apply(null, nums);
// 동일한 결과
const minNum = Math.min.call(null, ...nums);
// = Math.min.apply(null, 3, 10, 1, 6, 4)
// call과 apply는 동작 방식은 같음
// 매개변수를 받는 방법만 다를 뿐
console.log(minNum);
console.log(maxNum);
// bind : 함수의 this값을 영구히 바꿀 수 있음
const mike = {
name: "Mike",
};
function update(birthYear, occupation) {
this.birthYear = birthYear;
this.occupation = occupation
}
// bind는 새로 바인딩한 함수를 하나 만듦
// 이 함수는 항상 mike를 this로 가짐
const updateMike = update.bind(mike);
updateMike(1980, 'police');
console.log(mike);
const user = {
name: "Mike",
showName: function () {
console.log(`hello, ${this.name}`);
},
};
user.showName();
let fn = user.showName; // fn에 할당할 때 this를 잃어버림
// 메소드는 점 앞에 있는게 this
fn(); // hello,
// 호출할 때 fn만 호출하니까 this가 없는 것
fn.call(user); // hello, Mike
fn.apply(user); // hello, Mike
let boundFn = fn.bind(user);
boundFn(); // hello, Mike
lecture14
// 상속, 프로토타입
const user = {
name: "Mike"
}
user.name // "Mike"
user.hasOwnProperty('name') // true
user.hasOwnProperty('age') // false
// user 객체에 name이라는 프로퍼티가 있으니까 참이 나오고 age는 없으니까 거짓
// hasOwnProperty는 만든 적이 없는데 어디있는 걸까?
// __proto__에 있는데 이것을 프로토타입이라고 함
const user = {
name: "Mike",
hasOwnProperty : function() {
console.log('haha')
}
}
user.hasOwnProperty(); // haha
// 일단 객체에 프로퍼티가 있으면 거기서 탐색을 멈춤
// 없을 때만 프로토타입에서 프로퍼티를 찾음
const bmw = {
color: "red",
wheels: 4,
navigation: 1,
drive() {
console.log("drive..");
},
};
const benz = {
color: "black",
wheels: 4,
drive() {
console.log("drive..");
},
};
const audi = {
color: "blue",
wheels: 4,
drive() {
console.log("drive..");
},
};
// wheels랑 drive() 동일 -> 차들이 늘어나면 새로운 변수로 만들어지는 건데 공통된 부분을 어떻게 처리? -> 프로토타입으로 처리
const car = {
wheels: 4,
drive() {
console.log("drive..");
},
}
const bmw = {
color: "red",
navigation: 1,
};
const benz = {
color: "black",
};
const audi = {
color: "blue",
};
bmw.__proto__ = car;
benz.__proto__ = car;
audi.__proto__ = car;
console.log(bmw); // {color: "red",navigation: 1}
console.log(bmw.color); // "red"
console.log(bmw.wheels); // 4
// bmw객체 내부에서 wheels 프로퍼티를 찾고 찾으면 탐색을 멈춤
// 없다면 프로토타입에서 확인
// 상속은 계속 이어질 수 있음
const x5 = {
color: "white",
name: "x5"
};
x5.__proto__ = bmw;
console.log(x5.name); // "x5"
console.log(x5.color); // "white"
console.log(x5.navigation);
// x5에서 navigation을 찾고 없으니까 프로토타입인 bmw에서 탐색을 하고 있으니까 멈춤
console.log(x5.drive());
// x5에 드라이브 메소드가 없고 bmw에도 없고 프로토타입인 car까지 올라가서 드라이브 메소드를 사용
// 이런것을 프로토타입 체인이라고 함
// prototype chain
for (p in x5) {
console.log(p);
}
/*
color
name
navigation
wheels
drive
*/
// 키, 값과 관련된 객체 내장 메소드는 상속된 프로퍼티는 나오지 않음
Object.keys(x5); // ["color", "name"]
Object.values(x5); // ["white", "x5"]
for (p in x5) {
if (x5.hasOwnProperty(p)) {
console.log('o', p);
} else {
console.log('x', p);
}
}
/*
o color
o name
x navigation
x wheels
x drive
*/
// hasOwnProperty는 객체가 직접 가지고 있는 프로퍼티만 true를 반환해줌
const car = {
wheels: 4,
drive() {
console.log("drive..");
},
}
const Bmw = function (color) {
this.color = color;
}
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
// 생성자 함수를 쓰는 이유가 간편해서인데매번 하나를 만들 때마다 이렇게 하면 귀찮음
x5.__proto__ = car;
z4.__proto__ = car;
// 아래처럼 하자!
const Bmw = function (color) {
this.color = color;
}
// 생성자함수가 생성하는 객체의 __proto__ 이렇게 설정한다는 의미
// 이렇게 한 번만 작업을 해주면 생성자로 만들어진 모든 객체에 일일이 작업할 필요가 없어짐
Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function () {
console.log("drive..")
}
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
// 생성자 함수가 새로운 객체를 만들어 낼 때 그 객체는 생성자의 instance라고 불려짐
// 자바스크립트는 이를 편리하게 확인할 수 있는 instanceof연산자가 있음
// 객체와 생성자를 비교할 수 있고, 해당 객체가 그 생성자로부터 생성된 것인지를 판단해서 참 혹은 거짓을 반환
z4 instanceof Bmw; // true
// 생성자로 만들어진 instance객체에는 constructor라는 프로퍼티가 존재
z4.constructor === Bmw;// true
// 코드를 좀 더 깔끔히
const Bmw = function (color) {
this.color = color;
}
// 하지만 이렇게 하면 constructor가 사라짐
Bmw.prototype = {
wheels : 4,
drive() {
console.log("drive..");
},
navigation : 1,
stop() {
console.log("STOP!");
}
}
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
z4.constructor === Bmw;// false
// 이런 현상을 방지하기 위해 하나씩 프로퍼티를 추가하는 게 좋음
// 혹은
Bmw.prototype = {
constructor: Bmw, // 이렇게 명시해줘도 괜찮음
wheels : 4,
drive() {
console.log("drive..");
},
navigation : 1,
stop() {
console.log("STOP!");
}
}
z4.constructor === Bmw;// true
const Bmw = function (color) {
this.color = color;
}
const x5 = new Bmw("red");
x5.color; // "red"
x5.color = "black";
x5.color; // "black"
// 아무나 색상을 바꿀 수 있음 -> 클로저 사용
const Bmw = function (color) {
const c = color;
this.getColor = function () {
console.log(c);
}
}
const x5 = new Bmw("red");
// 코드를 이렇게 바꾸면 초기에 세팅된 컬러 값을 얻을 수만 있고 바꿀 수는 없음
lecture15
// 클래스 : ES6에 추가된 스펙
// 지금까지 비슷한 형태의 객체를 생성하기 위해 생성자 함수 사용
const User = function (name, age) {
this.name = name;
this.age = age;
};
User.prototype.showName = function () {
console.log(this.name);
}
const mike = new User("Mike", 30);
// 클래스로도 만들 수 있음
class User2 {
constructor(name, age) {
this.name = name;
this.age = age;
}
showName() {
console.log(this.name);
}
}
const tom = new User2("Tom", 10);
// new를 통해 호출 했을 때 내부에서 정의된 내용으로 객체를 생성하는 것은 동일
/* class키워드 사용
constructor는 객체를 만들어주는 생성자 메소드
new를 통해 호출하면 자동으로 constructor실행
constructor는 인수를 넘겨받을 수 있음
클래스 내 정의 메소드는 user2의 프로토타입에 정의
*/
/* 생성자와 클래스 차이 : 클래스의 탄생 이유?
생성자의 경우
new를 빼고 실행시 const mike = User("Mike", 30);
User함수가 반환 하는 값 (지금은 return 문이 없어 아무것도 반환하지 않기 때문에 undefined가 됨)
반환 값이 undefined고 그 값이 mike로 들어감
개발자가 실수한 코드지만 문제없이 동작함
에러라고 알아차릴 수 없음
클래스의 경우
const tom = User2("Tom", 10);
타입 에러 발생
클래스는 new없이 실행할 수 없음
두 경우의 프로토타입을 살펴보면
클래스의 경우에는 컨스트럭터가 클래스라고 명시가 되어있음
이 경우에 new 없이 호출하면 에러가 발생하도록 설계되어 있음
for in문을 사용할 경우
생성자의 경우
showName도 나옴
클래스의 경우
showName은 나오지 않음
for in문은 프로토타입에 포함된 프로퍼티들을 다 보여주고 객체가 가지고 있는 프로퍼티만 판별하기 위해 hasOwnProperty를 사용해야 했음
클래스의 메소드는 for in 문에서 제외 됨
*/
// 생성자에서의 상속은 프로토타입을 이용해 구현
// 클래스에서의 상속은 extends키워드를 사용
class Car {
constructor(color) {
this.color = color;
this.wheel = 4;
}
drive() {
console.log("drive..");
}
stop() {
console.log("STOP!");
}
}
class Bmw extends Car {
park() {
console.log("PARK");
}
}
const z4 = new Bmw("blue");
// 메소드 오버라이딩
// 동일한 이름으로 메소드를 정의하면 덮어 씀
class Bmw extends Car {
park() {
console.log("PARK");
}
// 만약 부모의 메소드를 사용하면서 확장하고 싶을 때는 super라는 키워드 사용
stop() {
super.stop(); // Car의 stop을 사용함
console.log("OFF!");
}
}
// 생성자 오버라이딩
class Bmw extends Car {
constructor() {
this.navigation = 1;
}
park() {
console.log("PARK");
}
}
const z4 = new Bmw("blue"); // 에러! constructor에서 this를 사용하기 전에 super constructor, 즉 부모 생성자를 반드시 먼저 호출해야함
// 클래스의 constructor는 {} 빈 객체를 만들어주고 this로 이 객체를 가리킴
// 반면 extends를 써서 만든 자식 클래스는 빈 객체가 만들어지고 this에 할당하는 이 작업을 건너 뜀
class Bmw extends Car {
constructor() {
super(); // 항상 super키워드로 부모 클래스의 컨스트럭터를 실행해줘야 함
this.navigation = 1;
}
park() {
console.log("PARK");
}
}
const z4 = new Bmw("blue"); // color가 undefined가 됨
// 제대로 동작하기 위해서는 자식 클래스의 constructor에 동일한 인수를 받는 작업을 해줘야 함
class Bmw extends Car {
constructor(color) {
super(color);
this.navigation = 1;
}
park() {
console.log("PARK");
}
}
class Bmw extends Car {
park() {
console.log("PARK");
}
}
// 자식 클래스에 컨스트럭터가 없을 때는 아래처럼 동작함
class Bmw extends Car {
constructor(...args) {
super(...args);
} // 이 부분이 있는 것 처럼 행동
park() {
console.log("PARK");
}
}
// 자식 생성자는 무조건 부모 생성자를 호출해야함
// super을 이용해 호출해주고 this.프로퍼티도 할당해줘야 함
lecture16
// 프로미스 Promise
const pr = new Promise((resolve, reject) => {
// resolve 성공한 경우 실행되는 함수
// reject는 실패한 경우 실행되는 함수
// 위와 같이 어떤 일이 완료된 이후에 실행되는 함수 : callback 함수
});
// new Promise가 반환하는 프로미스 객체는 state와 result를 프로퍼티로 가짐
/*
state는 초기에 pending(대기), result는 undefined
-> resolve(value)호출 시(성공 시) fulfilled(이행됨)가 됨, result는 value가 됨 (resolve 함수로 전달된 값)
-> reject(error)가 호출 되면(실패) rejected(거부됨)이 됨,
result는 error가 됨 (reject 함수로 전달된 에러)
*/
const pr = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 3000)
});
// state는 peding이었다가 3초 뒤에 fulfilled로 바뀌고 result는 'OK'가 됨
const pr = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('error...'))
}, 3000)
});
// state는 peding이었다가 3초 뒤에 rejected로 바뀌고 result는 error가 됨
// 지금까지 판매자의 코드
// 주문을 받으면 3초 동안 작업 후 성공, 실패를 알려줌
const pr = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 3000)
});
// 이제 소비자의 코드
// then을 이용해서 resolve와 reject를 처리 가능
pr.then(
function(result){}, // promise가 이행 되었을 때 실행 result에는 OK가 들어옴
function(err){} // promise가 거부 되었을 때 실행 result에는 에러가 들어옴
)
pr.then(
function(result){
console.log(result + ' 가지러 가자.');
},
function(err){
onsole.log('다시 주문해주세요..');
}
)
// then 이외에 사용할 수 있는 것: catch, finally
// catch는 에러가 발생한 경우 즉 reject인 경우에만 실행 됨
// catch: then과 동일하게 동작하지만 가독성이 좋고 첫 번째 함수를 실행했다가 나는 에러도 잡아줄 수 있음
pr.then(
function(result){}
).catch(
function(err){}
)
// finally는 이행이든 거부든 처리가 완료되면 항상 실행
pr.then(
function(result){}
).catch(
function(err){}
).finally(
function() {
console.log('---주문 끝---')
}
)
// Promise.all
console.time('x')
Promise.all([f1(), f2(), f3()]).then((res) => {
console.log(res);
console.timeEnd('x');
});
// 세 작업이 모두 완료되어야 then 부분이 실행
// Promise.all은 한꺼번에 시작하고 모두 이행되면 값을 사용할 수 있음 -> 시간 절약
// 만일 reject되는 경우가 있다면 어떤 데이터도 얻지 못함
// 하나의 정보라도 누락되면 페이지를 보여주면 안되는 경우 사용할 수 있음
// Promise.race
Promise.race([f1(), f2(), f3()]).then((res) => {
console.log(res);
});
// all과의 차이점 : all은 모든 작업이 완료될 때까지 기다리지만 race는 하나라도 1등으로 완료되면 끝냄
lecture17
// async, await
// 프로미스의 then 메소드를 체인 형식으로 호출하는 것보다 가독성이 좋아짐
// 함수 앞에 async라는 키워드를 붙이면 항상 프로미스 반환
async function getName() {
return "Mike";
}
console.log(getName()); //Promise {<fulfilled>: "Mike"}
getName().then((name) => {
console.log(name);
});
// await 키워드는 async함수 내에서만 사용할 수 있음
//await 키워드 오른쪽에 프로미스
function getName(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(name);
}, 1000);
});
}
async function showName() {
const result = await getName('Mike'); //getName에서 resolve된 값을 기다렸다가 넣어줌
console.log(result); // 1초 뒤에 Mike 찍힘
}
console.log("시작");
showName();
// 비동기 함수 병렬로 실행
lecture18
// Generator 함수의 실행을 중간에 멈췄다가 재개할 수 있는 기능
// Generator는 function 옆에 *을 써서 만듦
// 내부에 yield 키워드를 사용
function* fn() {
yield 1;
yield 2;
yield 3;
return "finish";
}
// 제너레이터 함수를 실행하면 제너레이터 객체 반환
// 제너레이터 객체는 next 메소드가 있음
const a = fn();
console.log(a); // 코드 실행시 제너레이터 객체만 반환되고 함수 본문 코드는 실행되지 않음
console.log(a.next()); // 가장 가까운 yield문을 만날 때까지 실행되고 데이터 객체를 반환
// 반환된 데이터 객체는 {value: 1, done: false}
// value는 yield 오른쪽에 있는 값 생략시 undefined
// done은 함수 코드가 끝났는지 나타내며 실행이 끝났으면 true
console.log(a.next()); // {value: 2, done: false}
console.log(a.next()); // {value: 3, done: false}
console.log(a.next()); // {value: "finish", done: true}
console.log(a.next()); // {value: undefined, done: true}
// 제너레이터는 next()메소드 외에 return()과 throw()메소드를 가짐
// 실행 중 리턴 메소드를 호출하면 그 즉시 done이 true가 됨
console.log(a.return('END')); // {value: "END", done: true}
console.log(a.next()); // {value: undefined, done: true}
// throw()도 마찬가지로 done을 true로 바꿈
function* fn() {
try {
yield 1;
yield 2;
yield 3;
return "finish";
} catch (e) {
console.log(e);
}
}
console.log(a.next()); // {value: 1, done: false}
console.log(a.next()); // {value: 2, done: false}
console.log(a.throw(new Error('err'))); // catch문에 있는 내용이 실행되고 {value: undefined, done: true}
console.log(a.next()); // {value: undefined, done: true}
/* Generator
iterable (반복이 가능)
- Symbol.iterator 메소드가 있다
- Symbol.iterator는 iterator를 반환해야 한다
iterator (메소드 호출 결과)
- next 메소드를 가진다
- next 메소드는 value와 done 속성을 가진 객체를 반환한다
- 작업이 끝나면 done은 true가 된다
Generator는 iterator이면서 iterable이다
iterable은 for of를 통해 순회 가능
문자열도 배열도 iterable
Generator는 외부로부터 값을 입력받을 수도 있다
Generator는 값을 미리 만들어 두지 않음
*/
const arr = [1, 2, 3, 4, 5];
// arr가 가지고 있는 Symbol.iterator메소드를 실행한 결과를 넣기
const it = arr[Symbol.iterator]();
it.next(); // {value: 1, done: false}..
it.next(); // {value: undefined, done: true}
function* gen1() {
yield "w";
yield "o";
yield "r";
yield "l";
yield "d";
}
function* gen2() {
yield "Hello, ";
yield* gen1();
yield "!";
}
console.log(...gen2()); //Hello, w o r l d !
📌 아래 강의의 내용을 정리한 글입니다.
'프로그래밍 > JavaScript' 카테고리의 다른 글
[JS] 자바스크립트 실행 컨텍스트(Execution Context)란? (0) | 2022.10.18 |
---|---|
[JS] 고차 함수 (0) | 2022.05.02 |
[JS] 태그 선택, 이벤트 달기 (0) | 2022.04.29 |
[JS] 이벤트 버블링 / 이벤트 위임 (0) | 2022.04.11 |
[JS] 호이스팅, TDZ, 동기·비동기, 콜 스택 동작 (0) | 2021.09.23 |