✍"자바스크립트 중급 강좌 : 140분 완성 - 코딩앙마"를 보고 요약한 글입니다.
var name = 'Mike';
console.log(name); // Mike
var name = 'Jane';
console.log(name); // Jane
// 에러 안남
console.log(name);
var name = 'Mike';
// 선언은 호이스팅 되지만, 할당은 호이스팅이 되지 않는다.
// name이라는 변수만 올려지고, Mike라는 값은 그래도 있음
var name ;
console.log(name); // undefined
name = 'Mike';
let name = 'Mike';
console.log(name); // Mike
let name = 'Jane'; // error!
console.log(name);
// Uncaught SyntaxError: Identifier 'name' has already been declared
// 에러 발생
console.log(name); //ReferenceError
let name = 'Mike';
// 문제 X
let age = 30;
function showAge(){
console.log(age);
}
showAge();
// 문제 발생
let age = 30;
function showAge(){
console.log(age);
let age = 20; // 호이스팅이 일어나는 변수
}
showAge();
여기서 스코프는 showAge함수 내부이다.
let name;
name = 'Mike';
var age;
age = 30;
// 에러 발생 구간 : 할당을 같이 안해줘서
const gender;
gender = 'male';
// 정상 작동
const age = 30;
if(age>19) {
**var txt = '성인'**;
}
console.log(**txt**); // '성인'
// 오류 발생 => 유일하게 벗어날 수 없는 스코프가 함수라고 생각
function add(num1, num2) {
var result = num1 + num2;
}
add(2, 3);
console.log(result);
let user = {
name: 'Mike',
age: 30,
}
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);
function User(name, age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name);
}
}
let user5 = new User('Han', 40);
user5.sayName(); // 'Han'
console.log(this.name)
의 this
는 user5
를 가리킨다.// 생성자 함수 : 삼품 객체를 생성해보자.
function Item(title, price){
// this = {};
this.title = title;
this.price = price;
this.showPrice = function(){
console.log(`가격은 ${price}원 입니다.`);
}
// return this;
}
const item1 = new Item('인형', 3000);
const item2 = new Item('가방', 4000);
const item3 = Item('지갑', 9000);
console.log(item1, item2, item3);
// Item {title: "인형", price: 3000, showPrice: f}
// Item {title: "가방", price: 4000, showPrice: f}
// undefined
item3.showPrice();
// 가격은 9000원 입니다.
let a = 'age';
const user = {
name : 'Mike',
[a] : 30 // age : 30, a에 할당된 값이 들어가게 됨
}
const user = {
[1 + 4] : 5,
["안녕" + "하세요"] : "Hello"
}
// > user
// > {5: 5, 안녕하세요: "Hello"}
const user = {
name : 'Mike',
age : 30
}
const cloneUser = user; // 복제 안됨, 참조값만 복사됨
user변수에는 객체 자체가 들어가 있는 것이 아니라, 객체가 저장되어 있는 메모리 주소인, 객체에 대한 참조값이 저장되어 있기 때문이다.
const newUser = **Object.assign({}, user)**;
// {} + { name: 'Mike', age: 30} =
// {
// name : 'Mike',
// age : 30,
// }
**newUser.name = 'Tom';
console.log(user.name); //'Mike'**
newUser != user
// 이름을 바꾸어도 user는 변함이 없음.
Object.assign({ **gender:'male'** }, user);
// 성별 값만 있는 객체가 user를 병합하는 거고, 총 세개의 프로퍼티를 가지게 됨
// gender : 'male',
// name : 'Mike',
// age : 30,
Object.assign({ **name: 'Tom'** }, user);
// 병합을 하는데, 키가 같다면 덮어쓰게 된다.
// name : 'Mike',
// age : 30,
const user = {
name: 'Mike'
}
const info1 = {
age : 30,
}
const info2 = {
gender : 'male',
}
Object.assign(user, info1, info2)
const user = {
name : 'Mike',
age : 30,
gender : 'male',
}
Object.keys(user);
// ["name", "age", "gender"]
const user = {
name : 'Mike',
age : 30,
gender : 'male',
}
Object.values(user);
// ["Mike", 30, "male"]
const user = {
name : 'Mike',
age : 30,
gender : 'male',
}
Object.entries(user);
/*
[
["name","Mike"],
["age",30],
["gender","male"]
}
*/
const arr =
[
["name","Mike"],
["age",30],
["gender","male"]
];
Object.fromEntries(arr);
/*
{
name : 'Mike',
age : 30,
gender : 'male',
}
*/
지금까지 객체의 property key는 문자형으로 만들었다.
const obj = {
1: '1입니다.',
false : '거짓'
}
object.keys(obj); // ["1", "false"] 문자형으로 반환된다.
// 실제로 접근할 때도 문자형으로 접근
obj['1'] // "1 입니다."
obj['false'] // "거짓"
문자형뿐만 아니라 심볼로도 객체의 propetry key가 가능하다.
const a = Symbol();
const b = Symbol();
console.log(a); // Symbol()
console.log(b); // Symbol()
a === b; //false
a == b; //false
const **id** = Symbol('id');
const user = {
name : 'Mike',
age : 30,
**[id] : 'myid'**
}
/*
> user
> {name: "Mike", age: 30, **Symbol(id): "myid"**}
> user[id]
> **"myid"**
*/
Object.keys(user);
// ["name", "age"]
Object.values(user);
// ["Mike", 30]
Object.entries(user);
// [Array(2), Array(2)]
for (let a in user) { }
const user = {
name : 'Mike',
age : 30
}
const id = Symbol('id');
user[id] = 'myid';
user.name = 'myname';
// 다른 사람이 만들어 놓은 개체에 자신만의 속성을 추가해서 덮어씌면 안됨
심볼은 이름이 같더라도 모두 다른 존재이다. 하지만 전역변수처럼 이름이 같으면 같은 객체를 가리켜야 할 때가 있다. ⇒ 전역 심볼 사용
const id1 = Symbol.for('id');
const id2 = Symbol.for('id');
id1 === id2; // true
Symbol.keyFor(id1); // 생성할 때 적어두었던 이름을 알려준다.
**keyFor**
을 사용하지 못한다.**description**
을 사용하여 이름을 알 수 있다.const id = Symbol('id 입니다.');
id.description; // "id입니다."
**Object.getOwnPropertySymbols**
: 심볼들만 볼 수 있다.**Reflect.ownKeys**
: 심볼형 키를 포함한 객체의 모든 key들을 보여준다.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);
}
user[showName](); // => Mike가 출력됨
// 사용자가 접속하면 보는 메세지
for (let key in user) {
console.log(`His ${key} is ${user[key]}.`);
}
10진수 → 2진수/16진수
let num = 10;
num.toString(); // "10", 숫자를 문자로 변환해줌
num.toString(2); // "1010", 10을 2진수로 나타낸다.
let num2 = 255;
num2.toString(16); // "ff"
let num1 = 5.1;
let num2 = 5.7;
Math.ceil(num1); // 6
Math.ceil(num2); //6
let num1 = 5.1;
let num2 = 5.7;
Math.floor(num1); // 5
Math.floor(num2); // 5
let num1 = 5.1;
let num2 = 5.7;
Math.round(num1) // 5
Math.round(num2) // 6
변수에 100을 곱하고, 반올림을 해준 뒤, 다시 100을 나눠주면 된다.
let userRate = 30.1234;
Math.round(userRate * 100) / 100 // 30.12
문자열을 반환하므로, 반환받은 이후 Number을 이용해 숫자로 반환한다.
let userRate = 30.1234;
userRate.toFixed(2); // "30.12"
Number(userRate.toFixed(2)); // 30.12
let x = Number('x'); // NaN
x == NaN // false
x === NaN // false
NaN == NaN // false
isNaN(x) // true
isNaN(3) // false
let margin = '10px';
parseInt(margin); // 10
Number(margin); // NaN
let redColor = 'f3';
parseInt(redColor); // NaN
parseInt(redColor, 16); // 243, 16진수로 반환
parseInt('11', 2) // 3, 문자열 11을 2진수에서 10진수로 반환
let padding = '18.5%';
parseInt(padding); // 18
parseFloat(padding); // 18.5
Math.floor(Math.random()*100)+1
Math.pow(2, 10); // 1024
let html = '<div class="box_title">제목 영역</div>';
let desc = "It's 3 o'clock."
let name = 'Mike';
let result = `My name is ${name}.` // My name is Mike.
let add = `2 더하기 3은 ${2+3}입니다.` // 2 더하기 3은 5입니다.
let desc = '안녕하세요.';
desc.length // 6
let desc = '안녕하세요.';
desc[2] // '하'
한 글자만 바꾸는 것은 허용되지 않는다.
desc[4] = '용';
console.log(desc);
// 안녕하세요., 아무 변화가 없다.
let desc = "Hi guys. Nice to meet you."
desc.toUpperCase();
// "HI GUYS. NICE TO MEET YOU."
desc.toLowerCase();
// "hi guys. nice to meet you."
문자를 인수로 받아 몇번째 위치하는지 반환해줌
0부터 센다, 찾는 문자가 없으면 -1반환, 중복 인자가 있더라도, 처음 발견하는 첫번째 위치만 반환
s.indexOf("k", 3)
=> s배열에서 index 3번부터 k를 찾아라.
s.indexOf("k")
=> s배열에서 index 0번부터 k를 찾아라.
if문을 쓸 때 주의, > -1 인지 항상 검사, 0이면 if문은 false로 간주하기 때문
let desc = "Hi guys. Nice to meet you.";
desc.indexOf('to'); // 14
desc.indexOf('man'); // -1
if(desc.indexOf('Hi') > -1) {
console.log('Hi가 포함된 문장입니다.');
}
let desc = "abcdefg";
desc.slice(2) // "cdefg"
desc.slice(0, 5) // "abcde"
desc.slice(2, -2) // "cde"
let desc = "abcdefg";
desc.substring(2, 5); // "cde"
desc.substring(5, 2); // "cde"
let desc = "abcdefg";
desc.substr(2,4) // "cdef"
desc.substr(-4,2) // "de"
let desc = " coding ";
desc.trim(); // "coding"
let hello = "hello!";
hello.repeat(3); // "hello!hello!hello!"
1 < 3 // true
"a" < "c" // true
"a".codePointAt(0); // 97 -> 아스키코드 번호를 얻어올 수 있음
String.fromCodePoint(97) // "a" -> 숫자코드를 안다면 문자코드를 얻어올 수 있음
let list = [
"01. 들어가며",
"02. JS의 역사",
"03. 자료형",
"04. 함수",
"05. 배열",
];
// 리스트에서 숫자들을 제외하고 문자들만 출력
let newList = [];
for(let i = 0; i < list.length; i++) {
newList.push(
list[i].slice(4);
);
}
console.log(newList); // ["들어가며", "JS의 역사", "자료형", "함수", "배열"]
// 금칙어 : 콜라
function hasCola(str){
if(str.indexOf('콜라')){
console.log('금칙어가 있습니다.');
} else {
console.log('통과');
}
}
hasCola('와 사이다가 짱이야!'); // 금칙어가 있습니다., if(-1) = true
hasCola('무슨소리, 콜라가 최고'); // 금칙어가 있습니다.
hasCola('콜라'); // 통과, index가 0이라서 if(0) = false
// 따라서 if(str.indexOf(’콜라’) 가 아닌 if(str.indexOf(’콜라’)>-1 을 해줘야 함
// 금칙어 : 콜라
// includes
// 문자가 있으면 true
// 없으면 false 반환
function hasCola(str){
if(str.includes('콜라')){
console.log('금칙어가 있습니다.');
} else {
console.log('통과');
}
}
hasCola('와 사이다가 짱이야!'); // 통과
hasCola('무슨소리, 콜라가 최고'); // 금칙어가 있습니다.
hasCola('콜라'); // 금칙어가 있습니다.
1. replace()로 문자열 치환
replace('old', 'new')
는 문자열에 있는 old를 new로 바꾼 문자열을 리턴한다.let str = "Hello world, Java";
str = str.replace("Java", "JavaScript");
console.log(str);
// 출력 결과
// Hello world, JavaScript
arr.replace(/old str/g, 'new str')
let str = 'Hello world, Java, Java, Java';
str = str.replace(/Java/g, 'JavaScript');
console.log(str);
// 출력 결과
// Hello world, JavaScript, JavaScript, JavaScript
✨ replace 참고 : https://codechacha.com/ko/javascript-replace-in-string/#1-replace%EB%A1%9C-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B9%98%ED%99%98
function Dog(name) {
this.name = name;
}
const dog1 = new Dog("Coco");
Dog.prototype.toString = function dogToString() {
return `${this.name}`;
};
console.log(dog1.toString()); // Coco
let baseTenInt = 10;
let bigNum = BigInt(20);
console.log(baseTenInt.toString(2)); // 1010
console.log(bigNum.toString(2)); // 10100
locale
의 고유 문자열에 의해 분리됨(, 쉼표)var number = 24;
var name="hashin";
var arr = [number, name, "hello"];
var str = arr.toLocaleString();
console.log(str); // 24,hashin,hello
let arr = [1, 2, 3, 4, 5]
arr.splice(1, 2);
console.log(arr); // [1, 4, 5]
let arr = [1, 2, 3, 4, 5]
arr.splice(1, 3, 100, 200);
console.log(arr); // [1, 100, 200, 5]
let arr = ["나는", "철수", "입니다"];
arr.splice(1, 0, "대한민국", "소방관");
// ["나는", "대한민국", "소방관", "철수", "입니다"]
let arr = [1,2,3,4,5];
let result = arr.splice(1, 2);
console.log(arr); // [1, 4, 5]
console.log(result); // [2, 3]
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]
let arr = [1,2];
arr.concat([3,4]); // [1, 2, 3, 4]
arr.concat([3,4], [5,6]); // [1, 2, 3, 4, 5, 6]
function forEach(predicate, thisArg){
for(let i = 0; i < arr.length; i++){
predicate(arr[i], i);
}
}
arr.forEach(function(item, index){ ... })
arr.forEach((item, index) ⇒ { ... });
🌱 예제1
let arr = ["Mike", "Tom", "Jane"];
arr.forEach((name, index) => {
console.log(name); // Mike, Tom, Jane
console.log(`${index + 1}. ${name}`);
// 1. Mike 2. Tom 3. Jane
});
🌱 예제2
a = [10, 11, 12, 13, 14, 15];
a.forEach(function(v, i){
console.log(v, i, this);
}, [1, 2]);
// 출력 결과
10 0 [1, 2]
11 1 [1, 2]
12 2 [1, 2]
13 3 [1, 2]
14 4 [1, 2]
15 5 [1, 2]
let arr = [1, 2, 3, 4, 5, 1, 2, 3];
arr.indexOf(3); // 2
arr.indexOf(3, 3); // 7
arr.lastIndexOf(3); // 7
let arr = [1, 2, 3];
arr.includes(2); // true
arr.includes(8); // false
// 첫번째 예제
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 }, // 0
{ name: "Jane", age: 27 }, // 1
{ name: "Tom", age: 10 }, // 2
];
const result = userList.findIndex((user)=>{
if(user.age < 19){
return true;
}
return false;
});
console.log(result); // 2
// 첫번째 예제
let arr = [1, 2, 3, 4, 5, 6];
const result = arr.filter((item) => {
return item % 2 === 0;
});
console.log(result); // [2, 4, 6]
let newArr = arr.map(function(item, index){ ... });
let newArr = arr.map((item, index) => { ... });
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);
/**
0: {name: "Mike", age: 30, id: 1, isAdult: true}
1: {name: "Jane", age: 27, id: 2, isAdult: true}
2: {name: "Tom", age: 10, id: 3, isAdult: false}
*/
console.log(userList);
/**
0: {name: "Mike", age: 30}
1: {name: "Jane", age: 27}
2: {name: "Tom", age: 10}
*/
let arr = ["안녕", "나는", "철수야"];
let result = arr.join("-");
console.log(result); // 안녕-나는-철수야
split()
: 전체 문자열이 배열의 하나의 요소로 반환split(separator)
: 구분자 기준으로 배열 요소 반환split(separator, limit)
: limit으로 배열의 크기를 지정하여 특정 갯수만 반환let str = 'Hello, My name is Mike.';
const result1 = str.split(",");
const result2 = str.split("");
console.log(result1); // ["Mike", "Jane","Tom","Tony"]
console.log(result2); // ["H", "e", "l", "l", "o", ",", " ", ....,"."]
let user = {
name: "Mike",
age: 30,
};
let userList = ["Mike", "Tom", "Jane"];
console.log(typeof user); // object
console.log(typeof userList); // object
console.log(Array.isArray(user)); // false
console.log(Array.isArray(userList)); // true
console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]
console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]
compareFunction(a, b) < 0
: a가 앞에 정렬compareFucntion(a, b) > 0
: b가 앞에 정렬 function compare(a, b) {
return a-b; // 오름차순 정렬
}
function compare(a, b) {
return b-a; // 내림차순 정렬
}
※예제※
let arr1 = [1, 5, 4, 2, 3];
let arr2 = ["a", "b", "c", "d", "e"]
let arr3 = [13, 27, 5, 8]
arr1.sort();
arr2.sort();
arr3.sort((a, b) => {
return a - b;
/**
a가 크면 양수를 리턴, 같으면 0을 리턴, a가 b보다 작으면 음수를 리턴
a와 b를 비교해서 a가 작으면 a를 앞으로 보냄,
a와 b를 비교해서 b가 작으면 b를 앞으로 보냄
*/
});
console.log(arr1); // [1, 2, 3, 4, 5]
console.log(arr2); // ["a", "b", "c", "d", "e"]
console.log(arr3); // [5,8,13,27]
_.sortBy(arr);
형태로 사용((누적 계산값(이전값), 현재값) ⇒ { return 계산값 }, 초깃값);
reduceRight()
은 reduce반환값 내림차순Callback함수 인자
1. 누산기(accumulator) : 콜백의 반환값 누적, 콜백의 이전 반환값
2. 현재 값(currentValue) : 처리할 현재 요소
3. 현재 인덱스(currentIndex, 옵션) : 처리할 현재 요소의 인덱스, initialValue를 제공한 경우 0, 아니면 1부터 시작
4. 원본 배열(array, 옵션) : reduce()를 호출한 배열
- initialValue(옵션)
callback의 최초 호출에서 첫 번째 인수에 제공하는 값, 초기값을 제공하지 않으면 배열 첫 번째 요소 사용
// 배열의 모든 수 합치기
let arr = [1, 2, 3, 4, 5];
// for, for of, forEach
let result = 0;
arr.forEach(num => {
result += num;
})
console.log(result); //15
해당 작업을 reduce()
함수 한번으로 가능하다.
// 배열의 모든 수 합치기
let arr = [1, 2, 3, 4, 5];
// for, for of, forEach
const result = arr.reduce((prev, cur) => {
return prev + cur;
}, 0)
// prev는 1~4까지의 합, cur은 5
// 따라서 모든 원소들의 합이 됨.
console.log(result);
// map이나 filter대신에 reduce를 사용해서 배열 반환
// 성인만 뽑아서 새로운 배열을 생성
let userList = [
{ name: "Mike", age: 30 },
{ name: "Tom", age: 10 },
{ name: "Jane", age: 27 },
{ name: "Sue", age: 26 },
{ name: "Harry", age: 43 },
{ name: "Steve", age: 60 },
];
let result1 = userList.reduce(()=>{
if(cur.age > 19) {
prev.push(cur.name);
}
return prev;
}, []);
let result2 = userList.reduce((prev, cur) => {
return (prev += cur.age);
}, 0);
let result3 = userList.reduce((prev, cur) => {
if(cur.name.length === 3) {
prev.push(cur.name);
}
return prev;
}, []);
console.log(result1); // ["Mike", "Jane","Sue","Harry","Steve"]
console.log(result2); // 196
console.log(result3); // ["Tom", "Sue"]
Boolean
값 반환callback
이 false
를 반환하는 요소를 찾을 때까지 배열에 있는 각 요소에 대해 한 번씩 callbackFn
함수 실행false
반환false
이면 false
반환true
반환, 호출한 배열을 변형하지 않음[형태]
// 화살표 함수
every((element) => {...})
every((element, index) => {...})
every((element, index, array) => {...})
// 콜백 함수
every(callbackFn)
every(callbackFn, thisArg)
// 인라인 콜백 함수
every(function callbackFn(element) {...})
every(function callbackFn(element, index) {...})
every(function callbackFn(element, index, array) {...})
every(function callbackFn(element, index, array) {...}, thisArg)
- element(필수) : 배열에서 처리되는 현재 요소
- index(옵션): 처리할 현재 요소의 인덱스
- array(옵션): every를 호출한 배열
- thisArg(옵션): callbackFn을 실행할 때 this로 사용하는 값
[예제]
var itemArr = [{name: "가", age: 10}, {name: "나" , age: 15}, {name: "다", age: 20}];
console.log(itemArr.every((item) => item.age <15)); // "나"탈락, false
console.log(itemArr.every((item) => item.age >5)); // true
true
반환true
이면 true
반환false
반환, 호출한 배열을 변형하지 않음[예제]
var itemArr = [{name: "가", age: 10}, {name: "나" , age: 15}, {name: "다", age: 20}];
console.log(itemArr.some((item) => item.age <15)); // true
console.log(itemArr.some((item) => item.age >5)); // true
console.log(itemArr.some((item) => item.age >30)); // false
구조 분해 할당 구문은 배열이나 객체의 속성을 분해해서 그 값을 변수에 담을 수 있게 하는 표현식
let users = ['Mike', 'Tom', 'Jane'];
let [user1, user2, user3] = users;
/*
위 코드의 의미는 다음과 같음
let user1 = users[0];
let user2 = users[1];
let user3 = u sers[2];
*/
console.log(user1); // 'Mike'
console.log(user2); // 'Tom'
console.log(user3); // 'Jane'
// 문자열 구분자
let str = "Mike-Tom-Jane";
let [user1, user2, user3] = str.split('-');
console.log(user1); // 'Mike'
console.log(user2); // 'Tom'
console.log(user3); // 'Jane'
// 만약 해당하는 값이 없다면(기본값)
let [a,b,c] = [1,2]; // c=undefined
let [a=3, b=4, c=5] = [1,2]; //기본값을 세팅
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); // 'Jane'
// 바꿔치기
let a = 1;
let b = 2;
let c = 1;
a = b;
b = c;
// 바꿔치기: 구조 분해 할당
let a = 1;
let b = 2;
[a, b] = [b, a];
let user = {name: 'Mike', age: 30};
let {age, name} = user;
console.log(name); // 'Mike'
console.log(age); // 30
// 새로운 변수 이름으로 할당 가능
let user = {name: 'Mike', age: 30};
let {name: userName, age: userAge} = user;
console.log(userName); // 'Mike'
console.log(userAge); // 30
// 만약 해당하는 값이 없다면(기본값)
let user = {name: 'Mike', age: 30};
let {name, age, gender} = user; // gender = undefined
let {name, age, gender = 'male'} = user; // user객체에 gender가 없다면 male이 기본적으로 할당된다.
let user = {
name: 'Jane',
age: 18,
gender: 'female'
};
console.log(gender); // 'female'
// 객체로 받은 값이 undefined인 경우에만 기본값이 사용된다.
함수에 인수를 얻는 방법은 2가지가 있다.
하나는 arguments를 이용, 나머지 하나는 나머지 매개 변수를 이용하는 방법이다.
function showName(name){
console.log(name);
}
showName('Mike'); // 'Mike'
showName('Mike', 'Tom'); // ?
showName(); // undefined
function showName(name){
console.log(arguments.length);
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']
// 전달 받은 모든 수를 더하는 예제
function add(...numbers) {
let result = 0;
numbers.forEach((num) => (result += num));
// numbers.reduce((prev, cur) => prev + cur);
console.log(result);
}
add(1, 2, 3); // 6
add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 55
// 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 user3 = new User('Jane', 10, 'English');
console.log(user1);
// User {name: "Mike", age: 30, skills : Array(2)}
console.log(user2);
// User {name: "Tom", age: 20, skills : Array(2)}
console.log(user3);
// User {name: "Jane", age: 10, skills : Array(1)}
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let result = [...arr1, ...arr2];
console.log(result); // [1, 2, 3, 4, 5, 6]
// 중간에 삽입 가능
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let result = [0, ...arr1, ...arr2, 7, 8, 9];
let user = {name: 'Mike'}
let mike = {...user, age:30}
console.log(mike) // {name: "Mike", age: 30}
let arr = [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"
// 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]
// 해결 방법1
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr2.reverse().forEach((num) => {
arr1.unshift(num);
});
arr1 = [...arr2, ...arr1];
console.log(arr1); // [4, 5, 6, 1, 2, 3]
// 해결 방법2
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr1 = [...arr2, ...arr1];
console.log(arr1); // [4, 5, 6, 1, 2, 3]
// 객체 예제
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);
});
console.log(user);
// {name: "Mike", age: 30, skills: Array(4)}
// 전개구문 사용
let user = { name: "Mike" };
let info = { age: 30 };
let fe = ["JS", "React"];
let lang = ["Korean", "English"];
user = {
...user,
...info,
skills : [...fe, ...lang],
};
console.log(user);
// {name: "Mike", age: 30, skills: Array(4)}
자바스크립트는 어휘적 환경(Lexical Environment)를 가짐
function makeAdder(x){
return function(y){
// y를 가지고 있고 상위함수인 makeAdder의 x에 접근 가능
return x + y;
}
}
const add3 = makeAdder(3);
console.log(add3(2)); // 5
// add3 함수가 생성된 이후에도 상위함수인 makeAdder의 x에 접근 가능
const add10 = makeAdder(10);
console.log(add10(5)); // 15
console.log(add3(1)); // 4
이러한 것들을 Closure라고 함
- 함수와 렉시컬 환경의 조합
- 함수가 생성될 당시의 외부 변수를 기억
- 생성 이후에도 계속 접근 가능
function makeCounter() {
let num = 0; // 외부 함수의 변수에 접근
// 은닉화
return function () { // 내부 함수
return num++;
};
}
let counter = makeCounter();
console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter()); // 2
// 생성된 이후에 계속 값을 기억하고 있음
setTimeout: 일정 시간이 지난 후 함수를 실행
// 3초 후에 로그를 찍어주는 코드
function fn(){
console.log(3)
}
setTimeout(fn, 3000);
// 함수를 전달하지 않고 직접 코드를 작성해도 무방
setTimeoue(function(){
console.log(3)
},3000);
clearTimeout(tId);
코드가 실행되기 때문에 아무일도 발생하지 않는다.setInterval: 일정 시간 간격으로 함수를 반복 수행
// 3초마다 Mike가 찍히는 코드
function showName(name){
console.log(name);
}
const tId = setInterval(showName, 3000, 'Mike');
clearInterval(tId);
를 실행하면 된다.delay = 0
으로 줘도 실제 바로 실행되지는 않는다.let num = 0;
function showTime() {
console.log(`안녕하세요. 접속하신지 ${num++}초가 지났습니다.`);
}
setInterval(showTime, 1000);
clearInterval
실행let num = 0;
function showTime() {
console.log(`안녕하세요. 접속하신지 ${num++}초가 지났습니다.`);
if (num > 5) {
clearInterval(tId);
}
}
const tId = setInterval(showTime, 1000);
함수 호출 방식과 관계없이 this를 지정할 수 있음
const mike = {
name: "Mike",
};
const tom = {
name: "Tom",
};
function showThisName() {
console.log(this.name); // 여기서 this는 window를 가리킴
}
showThisName(); // 빈문자열 출력
showThisName.call(mike); // Mike 출력
// 생년과 직업을 받아서 객체의 정보를 새로운 데이터로 업데이트 해줌
const mike = {
name: "Mike",
};
const tom = {
name: "Tom",
};
function update(birthYear, occupation){
this.birthYear = birthYear;
this.occupation = occupation;
};
update.call(mike, 1999, 'singer')
console.log(mike);
// {name: "Mike", BirthYear: 1999, occupation: "singer"}
const nums = [3, 10, 1, 6, 4];
const minNum = Math.min.apply(null, nums);
// = Math.min.apply(null, [3, 10, 1, 6, 4])
const maxNum = Math.max.apply(null, nums);
// = Math.max.apply(null, [3, 10, 1, 6, 4])
// = Math.max.call(null, ...nums);
console.log(minNum); // 1
console.log(maxNum); // 10
const mike = {
name: "Mike",
};
function update(birthYear, occupation) {
this.birthYear = birthYear;
this.occupation = occupation;
}
const updateMike = update.bind(mike);
updateMike(1980, "police");
console.log(mike);
// {name: "Mike", birthYear: 1980, occupation: "police"}
const user = {
name: "Mike",
showName: function () {
console.log(`hello, ${this.name}`);
},
};
user.showName(); // hello, Mike
let fn = user.ShowName;
fn(); // hello,
fn.call(user); // hello, Mike
fn.apply(user); // hello, Mike
let boundFn = fn.bind(user); // hello, Mike
boundFn(); // hello, Mike
객체에는 자신이 프로퍼티를 가지고 있는지 확인하는 메서드가 있다. (
hasOwnProperty
)
__proto__
: prototypehasOwnProperty
가 객체 안에 있으면 __proto__에서 탐색을 멈춘다.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..");
},
};
▷ 동일한 부분을 __proto__로 처리할 수 있다.
// car라는 상위개념의 객체를 하나 만듦
const car = {
wheels: 4,
driver() {
console.log("drive..");
},
};
const bmw = {
color: "red",
navigation: 1,
};
const benz = {
color: "black",
};
const audi = {
color: "blue",
};
// car가 차들의 prototype이 되는 것
bmw.__proto__ = car;
benz.__proto__ = car;
audi.__proto__ = car;
bmw.wheels; // 4
▷ bmw객체 내부에서 wheels프로퍼티를 찾는다. 찾으면 거기서 탐색을 멈추고, 없다면 proto에서 찾는다.
▷ 상속은 계속 이어질 수 있다.
// car라는 상위개념의 객체를 하나 만듦
const car = {
wheels: 4,
driver() {
console.log("drive..");
},
};
const bmw = {
color: "red",
navigation: 1,
};
// car가 차들의 prototype이 되는 것
bmw.__proto__ = car;
const x5 = {
color: "white",
name: "x5",
};
x5.__proto__ = bmw;
x5.navigation; // 1
for(p in x5){
console.log(p);
}
// color, name, navigation, wheels, drive
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
*/
▷ __proto__에서 정의한 프로퍼티 모두 출력
▷ hasOwnProperty는 객체가 직접 가지고 있는 프로퍼티만 true를 반환한다.
Object.keys(x5);
// (2) ["color", "name"]
Object.values(x5);
// (2) ["white", "x5"]
▷ 값과 관련된 객체 내장 메서드는 상속된 프로퍼티는 나오지 않는다.
/*
const car = {
wheels: 4,
drive() {
console.log("drive..");
},
};
*/
const Bmw = function (color) {
this.color = color;
};
Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function () {
console.log("drive..");
};
Bmw.prototype.navigation = 1;
Bmw.prototype.stop = function () {
console.log("STOP!");
};
// 생성자 함수가 생성하는 객체의 __proto__를 이렇게 설정한다는 의미이다.
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
/*
x5.__proto__ = car;
z4.__proto__ = car;
*/
x5.stop(); // STOP!
▷ 생성자로 만들어진 모든 객체에 하나하나(__proto__) 작업할 필요가 없다.
▷ 생성자 함수가 새로운 객체를 만들어낼 때 그 객체는 생성자의 인스턴스라고 말한다.
instanceof
연산자가 있다.true
혹은 false
를 반환한다.z4
// Bmw {color: "blue"}
z4 instanceof Bmw
// true
z4.constructor === Bmw;
// true
// 생성자로 만든 인스턴스 객체에는 constructor라는 프로퍼티가 존재한다.
// constructor(생성자)는 Bmw가 나온다.
const Bmw = function (color) {
this.color = color;
};
/*
Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function () {
console.log("drive..");
};
Bmw.prototype.navigation = 1;
Bmw.prototype.stop = function () {
console.log("STOP!");
};
*/
Bmw.prototype = {
// constructor: Bmw, -> 수동을 constructor제시
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(constructor사라짐)
const Bmw = function (color) {
const c = color;
this.getColor = function () {
console.log(c);
};
};
const x5 = new Bmw("red");
▷ getColor
함수는 생성될 당시의 context를 기억하게 하는 함수
// 생성자 함수
const User = function (name, age) {
this.name = name;
this.age = age;
this.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", 19);
constructor
는 객체를 만들어주는 생성자 메서드이다. new
를 통해 호출하면 자동으로 실행된다. 객체를 초기화하기 위한 값이 constructor
내부에 정해진다. 인수를 연결받을 수 있다. showName()
처럼 클래스 내에 정의한 메서드는 User2의 __proto__
내부에 저장된다.// 생성자 함수를 클래스와 동일하게 동작하도록 수정
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);
▷ for~in
은 프로토타입에 포함된 프로퍼티들을 다 보여줬었지만, class메서드는 for~in
문에서 제외된다.
extends
키워드를 사용한다.class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive..");
}
stop() {
console.log("STOP!");
}
}
// Car를 상속해서 Bmw를 만드는 예제
class Bmw extends Car {
park() {
console.log("PARK");
}
}
const z4 = new Bmw("blue");
▷ 클래스 내부에서 선언한 메서드들은 프로토타입 밑으로 들어간다.
class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive..");
}
stop() {
console.log("STOP!");
}
}
class Bmw extends Car {
park() {
console.log("PARK");
}
stop() {
console.log("OFF");
}
}
const z4 = new Bmw("blue");
z4.stop(); // OFF
▷ 동일한 이름으로 메서드를 사용하면 덮어쓰게 된다.
▷ 만약 부모의 메서드를 사용하면서 확장하고 싶다면 super
라는 키워드를 사용하면 된다.
class Bmw extends Car {
park() {
console.log("PARK");
}
stop() {
super.stop(); // Car의 stop을 사용
console.log("OFF");
}
}
constructor
에서 this
를 사용하기 전에 부모 생성자(super constructor)
를 반드시 먼저 호출해야 한다.constructor
에 동일한 인수를 받게끔 해줘야 한다.class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive..");
}
stop() {
console.log("STOP!");
}
}
class Bmw extends Car {
constructor(color) {
super(color); // 부모클래스의 constructor 실행
this.navigation = 1;
}
park() {
console.log("PARK");
}
}
const z4 = new Bmw("blue");
callback
함수라고 한다.▷ new Promise
생성자가 반환하는 promise객체는 state
와 result
를 프로퍼티로 갖는다. state는 초기 pending
상태였다가 resolve
가 발생하면(성공하면), fulfilled
상태가 된다. 이때 result
는 resolve
함수로 전달된 값이다.
▷ 만약 reject()
가 호출되면(실패하면), rejected
상태가 된다. 이때의 result
는 reject함수로 전달된 error
이다.
// 3초 후에 fulfilled
const pr = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('OK')
}, 3000)
});
// 3초 후에 실패
const pr = new Promise((resolve, reject) => {
setTimeout(()=>{
reject(new Error('error..'))
}, 3000)
});
const pr = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('OK')
}, 3000)
});
pr.then(
function(result){}, // resolve되었을 때 실행, result값은 'OK'
).catch(
function(err){} // reject되었을 때 실행
).finally(
function(){
console.log('---끝---')
}
)
);
▷ then이후에 사용할 수 있는 것이 catch
와 finally
이다.
▷ catch
는 reject인 경우에만 실행된다.
▷ finally
는 이행이든, 거부든 처리가 완료되면 항상 실행된다.
// promise를 사용하지 않은 코드 => 콜백 지옥
const f1 = (callback) => {
setTimeout(function () {
console.log("1번 주문 완료");
callback();
}, 1000);
};
const f2 = (callback) => {
setTimeout(function () {
console.log("2번 주문 완료");
callback();
}, 1000);
};
const f3 = (callback) => {
setTimeout(function () {
console.log("3번 주문 완료");
callback();
}, 1000);
};
console.log('시작')
f1(function(){
f2(function(){
f3(function(){
console.log('끝')
})
})
})
// promise로 구현
const f1 = () => {
return new Promise((res, rej) => {
setTimeout(() => {
res("1번 주문 완료");
}, 1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("2번 주문 완료");
}, 3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("3번 주문 완료");
}, 2000);
});
};
// 프로미스 체이닝 (Promises chaining)
console.log('시작');
f1()
.then((res) => f2(res))
.then((res) => f3(res))
.then((res) => console.log(res))
.catch(console.log)
.finally(() => {
console.log("끝");
});
const f1 = () => {
return new Promise((res, rej) => {
setTimeout(() => {
res("1번 주문 완료");
}, 1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("2번 주문 완료");
}, 3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("3번 주문 완료");
}, 2000);
});
};
// promise.all
console.time("x");
Promise.all([f1(), f2(), f3()]).then((res) => {
console.log(res);
console.timeEnd("x");
});
// ["1번 주문 완료", "2번 주문 완료", "3번 주문 완료"]
// x: 3001.15087890625 ms
▷ 한꺼번에 시작하고 모두 이행되면 값을 사용할 수 있으며 시간도 절약할 수 있다.
▷ 하나의 정보라도 누락되면 페이지를 보여주면 안되는 경우 사용된다.
// promise.race
console.time("x");
Promise.race([f1(), f2(), f3()]).then((res) => {
console.log(res);
console.timeEnd("x");
});
// 1번 주문 완료
// x: 1002.0048828125 ms
▷ all은 모든 작업이 완료될 때까지 기다리지만, race는 하나라도 1등으로 완료되면 끝난다.
async await
을 사용하면promise
를chain
형식으로 호출하는 것보다 가독성이 좋아진다.
async function getName() {
return "Mike";
}
// 함수에 async키워드를 붙여주면 항상 promise를 반환한다.
console.log(getName());
// Promise {<fulfilled>: "Mike"}
getName().then((name) => {
console.log(name); // Mike
});
// 만약 값이 promise면 해당 값을 그대로 사용한다.
async function getName() {
return Promise.resolve("Tom");
}
getName().then((name) => {
console.log(name); // Tom
});
// 만약 함수 내부에서 예외가 발생하면 rejected상태의 promise가 반환된다.
async function getName() {
throw new Error("err..");
}
getName().then((name) => {
console.log(name); // Uncaught (in promise) Error: err..
});
getName().catch((err) => {
console.log(err); // Error: err..
});
await
키워드는 async함수 내부에서만 사용이 가능하다.await
키워드 오른쪽에는 promise가 온다.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);
}
console.log("시작"); // 시작
showName(); // Mike
const f1 = () => {
return new Promise((res, rej) => {
setTimeout(() => {
res("1번 주문 완료");
}, 1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("2번 주문 완료");
}, 3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("3번 주문 완료");
}, 2000);
});
};
/*
f1()
.then((res) => f2(res))
.then((res) => f3(res))
.then((res) => console.log(res))
.catch(console.log);
*/
console.log("시작");
async function order() {
const result1 = await f1();
const result2 = await f2(result1);
const result3 = await f3(result2);
console.log(result3);
console.log("종료");
}
order();
▷ 만약 중간에 rejected
가 나면 Error발생하고 코드가 멈춘다. 다음과 같이 해결하면 된다.
▷ async, await
은 try...catch
문으로 감싸주면 된다.
...
console.log("시작");
async function order() {
try {
const result1 = await f1();
const result2 = await f2(result1);
const result3 = await f3(result2);
console.log(result3);
} catch (e) {
console.log("종료");
}
order();
// promise.all 사용
...
console.log("시작");
async function order() {
try {
const result = await Promise.all([f1(), f2(), f3()]);
console.log(result);
} catch (e) {
console.log(e);
}
console.log("종료");
}
order();
ex) Redux Saga
function
키워드 옆에 *
를 붙이고, 함수 내부에 yield
키워드를 사용한다.yield
에서 함수의 실행을 멈출 수 있다.function* fn() {
yield 1;
yield 2;
yield 3;
return "finish";
}
const a = fn();
Generator
함수를 실행하면 Generator
객체가 반환된다. Generator
객체는 next
메서드가 존재한다.function* fn() {
console.log(1);
yield 1;
console.log(2);
yield 2;
console.log(3);
console.log(4);
yield 3;
return "finish";
}
const a = fn();
▷ 가장 가까운 yield
문을 만날 때 까지 실행되고, 데이터 객체를 반환한다.
▷ 반환된 데이터 객체는 value
와 done
프로퍼티를 가지는데, value
는 yield
값이다. 만약 값을 생략하면 undefined
▷ done
은 이름 그대로 함수코드가 끝났는지를 나타내며, 실행이 끝났으면 true
, 아니면 false
이다.
return()
메서드를 실행하면 그 즉시 done
프로퍼티가 true
가 된다. next()
메서드를 실행해도 value
는 얻어올 수 없고, done
은 true
이다.throw()
도 마찬가지로 done
을 true
로 바꾼다.iterable
- Symbol.iterator 메서드가 있다.
- Symbol.iterator는 iterator를 반환해야 한다.
iterator
- next 메서드를 가진다.
- next 메서드는 value와 done 속성을 가진 객체를 반환한다.
- 작업이 끝나면 done은 true가 된다.
function* fn() {
yield 4;
yield 5;
yield 6;
}
const a = fn();
a[Symbol.iterator]() === a; // true
for(let num of a){
console.log(num);
}
// 4 5 6 undefined
▷ Generator
에 Symbol.iterator
메서드를 실행한 값이 자기 자신이다.
▷ Generator
는 iterable
객체이다.
▷ for-of
가 시작이 되면 Symbol.iterator
를 호출하고, 만약에 없으면 Error가 발생한다. 반환된 iterator
에 next()
를 호출하면서 done
이 true
가 될 때까지 반복한다.
iterable
이다.function* fn() {
const num1 = yield "첫번째 숫자를 입력해주세요";
console.log(num1);
const num2 = yield "두번째 숫자를 입력해주세요";
console.log(num2);
return num1 + num2;
}
const a = fn();
▷ Generator
는 외부로부터 값을 입력받을 수 있다.
Generator
는 값을 미리 만들어 두지 않는다. 메모리 관리 측면에서 효율적이다. function* fn() {
let index = 0;
while (true) {
yield index++;
}
}
const a = fn();
▷ 필요한 순간에만 연산해서 값을 준다. 필요한 값만 그때그때 생성한다. (break
가 없는 while(true)
문 사용 가능)
Generator
을 불러온다.function* gen1() {
yield "W";
yield "o";
yield "r";
yield "l";
yield "d";
}
function* gen2() {
yield "Hello,";
yield* gen1();
yield "!";
}
참고 문서
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/every
https://paperblock.tistory.com/67