
왜 벌써 월요일이지???
이번 주 강의는 콜백함수에 집중한 주차이다.
다른 코드의 인자로 넘겨주는 함수. 이를 통해 제어권을 넘겨준다.
제어권
콜백 함수도 함수다
강의에서 배운 내용은 콜백 함수도 함수이기 때문에 this가 기본적으로 global 혹은 Window로 지정된다.
// obj
// 2가지 속성
var obj = {
vals: [1, 2, 3],
logValues: function (v, i) {
console.log("test start");
if (this === global) {
console.log("this가 global");
} else {
console.log(this, v, i);
}
console.log("test end");
}
};
// method로서 호출
obj.logValues(1, 2);
// forEach, map, filter
// obj.logValues(1,3) 이런식으로 넣을 경우
// 함수가 아닌 함수가 실행된 값을 넣는 것과 같음
// 아래의 경우는 우리가 logValues 함수를 넣은 것
// 콜백도 함수이기 때문에 global로 뜨는 것
console.log("===콜백함수도 함수이다===");
[4, 5, 6].forEach(obj.logValues);
위 코드에서 두번째 함수로 불렀을때 this가 global이기 때문에 this가 global이라는 문구가 호출한 배열의 길이만큼, 3번 출력된다.
콜백 함수에 다른
this바인딩 하기
여러 방법을 배웠지만 그 중 가장 좋은 방법
var obj1 = {
name: 'obj1',
func: function () {
console.log(this.name);
}
};
//함수 자체를 obj1에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj1로 고정해줘!
setTimeout(obj1.func.bind(obj1), 1000);
var obj2 = { name: 'obj2' };
//함수 자체를 obj2에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj2로 고정해줘!
setTimeout(obj1.func.bind(obj2), 1500);
콜백 지옥과 비동기 제어
들여쓰기의 수준이 심한 경우를 콜백 지옥이라고도 하는데 아래와 같이 생겼다.
setTimeout(
function (name) {
var coffeeList = name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
},
500,
"카페라떼"
);
},
500,
"카페모카"
);
},
500,
"아메리카노"
);
},
500,
"에스프레소"
);
예시는 조금 간단한 편이라 적어도 무슨일 일 일어나는지 예상이 가능하지만 이것이 반복되어 들여쓰기가 거의 읽지 못하는 수준에 도달하면 해독에 시간이 많이 걸린다.
이를 해결 하는 방법은 두 가지
var coffeeList = '';
var addEspresso = function (name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(addAmericano, 500, '아메리카노');
};
var addAmericano = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addMocha, 500, '카페모카');
};
var addMocha = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addLatte, 500, '카페라떼');
};
var addLatte = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
};
setTimeout(addEspresso, 500, '에스프레소');
각 함수의 역할을 한 눈에 볼 수 있으니 간단한 해결책이 될 수 있다. 하지만 매번 새로운 함수의 이름을 붙여야 한다는 것과 함수가 한 번만 쓰이고 말거나 아니면 더 많은 함수를 쓰면서 가독성이 힘들어질 수도 있으므로 근본적인 해결책이 되지는 못한다.
따라서 효율적인 것은 비동기 작업인데 단점이 있다, 순서가 지켜지지 않아 생기는 문제가 발생하는 것. 그렇기 때문에 비동기 작업을 동기적으로 표현해야 한다.
핵심은 "비동기적 작업은 순서를 보장할 수 없으니 순서 보장이 필요한 로직에서 순서를 보장 받기 위해"
2-1. Promise

아래의 두 예시 코드를 노트로 대체한다.
// 1안
var coffeeList = '';
var addEspresso = function (name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(addAmericano, 500, '아메리카노');
};
var addAmericano = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addMocha, 500, '카페모카');
};
var addMocha = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addLatte, 500, '카페라떼');
};
var addLatte = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
};
setTimeout(addEspresso, 500, '에스프레소');
// 2안
var addCoffee = function (name) {
return function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var newName = prevName ? (prevName + ', ' + name) : name;
console.log(newName);
resolve(newName);
}, 500);
});
};
};
addCoffee('에스프레소')()
.then(addCoffee('아메리카노'))
.then(addCoffee('카페모카'))
.then(addCoffee('카페라떼'));
2-2. Generator사용
별표가 붙은 함수로 Iterator 객체를 반환한다.

// Generator
// 1. 제너레이터 함수 안에서 쓸 addCoffee 함수 선언
var addCoffee = function (prevName, name) {
setTimeout(function () {
coffeeMaker.next(prevName ? prevName + ", " + name : name);
}, 500);
};
// *가 붙은 함수가 제너레이터 함수
// 실행시 -> Iterator 객체가 반환됨
// 2. 제너레이터 함수 선언
// yield 키워드로 순서 제어
var coffeeGenerator = function* () {
var espresso = yield addCoffee("", "에스프레소");
console.log(espresso);
var americano = yield addCoffee(espresso, "아메리카노");
console.log(americano);
var mocha = yield addCoffee(americano, "카페모카");
console.log(mocha);
var latte = yield addCoffee(mocha, "카페라떼");
console.log(latte);
};
var coffeeMaker = coffeeGenerator();
coffeeMaker.next();
2-3. Promise와 async/await
1번째 방법은 then()을 활용했는데 async/await 방법도 있다.
// Promise + async/await
// coffeeMaker 함수에서 호출할 함수, 'addCoffee'를 선언
// Promise를 반환
var addCoffee = function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(name);
}, 500);
});
};
//
// 화살표 함수의 경우
// var coffeeMaker = async () => {
var coffeeMaker = async function () {
var coffeeList = "";
var _addCoffee = async function (name) {
coffeeList += (coffeeList ? ", " : "") + (await addCoffee(name));
};
// promise를 반환하는 함수인 경우, await를 만나면 무조건 끝날 때 까지 기다린다.
await _addCoffee("에스프레소");
console.log(coffeeList);
await _addCoffee("아메리카노");
console.log(coffeeList);
await _addCoffee("카페모카");
console.log(coffeeList);
await _addCoffee("카페라떼");
console.log(coffeeList);
};
coffeeMaker();
문제 풀이때 유용한 메소드들
padStart()
// str이 길이에 도달 할 때까지 문자로 채워나감
str.padStart(길이, 문자)
'1234'.padStart(8, '*') // '****1234'
'1234'.padStart(6, '*') // '**1234'
'1234'.padStart(4, '*') // '1234'
'1234'.padStart(3, '*') // '1234'
sort()
// 오름차순 정렬
arr.sort((a,b) => a - b)
// 내림차순 정렬
arr.sort((a,b) => b - a)
시간 복잡도 (Time Complexity)
오늘 새로 배운 개념은 시간 복잡도(time complexity)!! 코드가 작동하는 것도 중요하지만 더 중요한 것이 효율이다. 만일 내가 작성한 코드가 정말 느리다면 어떤 사용자가 그 기능을 사용하겠는가. 따라서 코드의 runtime을 효율적으로 줄일 수 있는 방법을 배우는 것이 알고리즘 공부할때의 핵심이다.
시간 복잡도를 표기하는 방법은 아래 이미지와 같다

이 중 최악의 경우의 수를 나타내는 Big-O 표기법을 사용하여 시간 복잡도를 나타내는 것이 일반적인다.
아래는 대학시절 늘 참고하던 비교표

아래 짤은 해탈한 짤

추가로 공간 복잡도 (Space Complexity)라는 개념도 있지만 시간 복잡도 보다는 중요도가 조금 떨어지는 편이다. 컴퓨터 메모리를 다 사용 하는 코드를 적지 않는 한 크게 신경 써야할 문제는 아니다.
오늘 특별히 새로운 내용은 없고 데이터 타입에 대해 다 같이 복습 하는 시간을 가졌다. 아마 내일 부터는 실습과 새로운 내용이 점점 생길 것 같아서 따로 노트를 적어볼 예정이다.
특강이 다행히도 하나였다, 근데 이제 달리기반 2시간을 곁들인...