github에만 업로드하고 블로그에 업로드를 안해서 부랴부랴 업로드 하러왔습니다 ㅎㅎ😆
Javascript의 타입은 원시 타입과 객체 타입으로 구분되며 변수는 아래와 같이 선언할 수 있다.
var x = 10;
let y = 10;
const y = 10;
아래의 코드에서 x
는 변수이며 10
은 값에 해당한다.
let x = 10;
Javascript에서 값이라고 정의한 것들은 변수에 넣을 수 있다.
값에 해당하는 데이터는 문자열, 숫자, 객체등이 존재한다.
function foo() {}
let y = foo;
Javascript에서는 함수 또한 객체이며 위와 같이 변수에 할당 가능하다.
Javascript에서 모든 함수는 undefined
나 return
으로 명시된 값을 반환한다.
Javascript에서의 함수는 값이며 모든 값은 변수에 대입될 수 있다.
Javasript에서 실행 결과가 값이 되는 것은 식이며 그 외의 모든 것은 문이다.
Javascript에서 함수를 정의하는 방법은 함수 정의문과 함수 정의식이 존재한다.
function foo() {}
const bar = function bar() {};
bar();
호출한 bar()
의 이름은 함수 정의식에서 오른쪽에 대입된 함수의 이름이 아니다.
오른쪽에 정의된 함수의 이름은 bar
변수에서는 알 수 없는 익명 함수가 된다.
(function () {})();
익명 함수를 값으로 만들어 사용하기 위해서는 익명 함수를 ()
로 감싸 즉시 호출하면 된다.
function foo(x) {
x();
return function () {};
}
const y = foo(function () {});
함수는 값으로 취급되기 때문에 위와 같이 매개변수 x
의 형태로 함수를 전달할 수 있다.
또한 foo
함수의 반환값으로 함수가 반환될 수 있으며 반환된 함수는 변수에 저장할 수 있다.
위와 같이 함수를 매개변수로 넘겨주는 것을 콜백 함수라고 하며 호출을 위임하는 역할을 한다.
소프트웨어 공학에서 함수를 인자로 받고 함수를 반환하는 함수를 1급 함수 (High Order Function)이라고 한다
ES6이전에서 함수식을 만들때 아래와 같이 만들 수 있다.
const foo = function (x) {};
위의 foo
함수에서 foo
함수를 재귀호출하기 위해서는 오른쪽에 대입된 함수의 이름이 필요하다.
const foo = function foo() {
foo();
};
ES6 이후에서 함수식을 만들때 =>
를 이용한 화살표 함수로 만들 수 있다.
const bar = (x) => {};
화살표 함수는 한줄 함수, 람다 함수라고도 불리며 아래와 같이 하나의 식을 반환하는 경우 자주 사용된다.
const bar = (x) => x * 2;
화살표 함수는 인자가 하나일 경우 ()
를 생략할 수 있으며 한줄로 구성될 경우 return
생략이 가능하다.
const foo = x => 10;
const boar = (x, y) => x * y;
new
연산자를 호출하면 this
를 생성하며 인스턴스 객체를 생성된 this
를 반환한다.
function foo() {
this.age = 10;
}
const y = new foo();
console.log(y); // { age: 10, constructor: object }
생성된 인스턴스 객체 y
를 출력하면 constructor
가 추가되어 반환되는 것을 볼 수 있다.
생성된 객체의 타입을 확인하기 위해서는 instanceof
를 y instanceof foo
와 같이 사용할 수 있다.
함수를 new
연산자를 이용해 인스턴스를 생성할 수 있지만 ES6이후 부터는 아래와 같이 클래스 문법을 제공한다.
class bar {
constructor() {
this.age = 10;
}
}
console.log(new bar()); // { age: 10, constructor: object }
보기에는 비슷하지만 코드 구조적인 부분에서 class
를 이용하면 조금 더 명시적이다.
function foo() {
this.age = 10;
}
foo(); // undefined
new foo(); // { age: 10 }
또한 작성된 함수가 인스턴스를 위한 함수이지만 해당 기능으로의 사용을 강제할 수 없다.
아래 코드에서 this.name
은 person
객체의 name
을 가리킨다.
const person = {
name: "김민수",
getName() {
return this.name;
},
};
console.log(person.getName()); // 김민수
this
가 결정되는 방식들은 실행 컨텍스트로 호출시에 결정된다.
const man = person.getName;
console.log(man());
위의 코드에서 man
은 getName
함수를 저장해 man()
과 같이 호출할 수 있다.
하지만 man
이 호출되는 시점의 실행 컨텍스트는 전역 객체이므로 name
이 존재하지 않아 오류가 발생한다.
const man = person.getName.bind(person);
console.log(man()); // 김민수
person.getName.call(person); // 김민수
this
를 고정하기 위해서는 bind
, call
, apply
함수를 사용할 수 있다.
아래의 코드에서 foo
함수는 bar
함수를 반환한다.
function foo(x) {
return function bar() {
return x;
};
}
const f = foo(10);
console.log(f()); // 10
bar
함수를 담은 f
는 f()
와 같이 호출할 수 있으며 10
을 반환한다.
위의 코드의 bar
함수에서 접근하는 x
변수는 bar
함수 스코프의 외부 범위다.
foo
함수가 호출이 완료된 시점에서 foo
의 스코프는 사라지지만 반환된 bar
에서는 x
를 기억한다.
위와 같이 함수 실행결과로 반환된 함수가 외부의 스코프를 기억하고 있는 상태를 클로저라고 한다.
function makePerson() {
let age = 10;
return {
getAge() {
return age;
},
setAge(x) {
age = x > 1 && x < 130 ? x : age;
},
};
}
p = makePerson();
console.log(p.getAge()); // 10
p.setAge(500);
console.log(p.getAge()); // 10
p.setAge(20);
console.log(p.getAge()); // 20
클로저를 사용하면 p.age
와 같이 접근할 수 없으며 위와 같이 값을 캡슐화해 보호할 수 있다.
아래와 같이 setTimeout
이나 API
를 호출하는 경우 코드가 비동기적으로 실행된다.
setTimeout(function () {
console.log("foo");
setTimeout(function () {
console.log("bar");
}, 2000);
}, 1000);
위의 코드는 비동기적인 로직을 2 depth를 갖는다. 하지만 더 깊이가 깊어질 경우 콜백 헬이 발생한다.
비동기 로직 작성시 콜백 헬과 같은 코드를 방지하기 위해 나온 것이 Promise
다.
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("응답1");
}, 1000);
reject();
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("응답2");
}, 1000);
reject();
});
p1.then(p2())
.then(function (r) {
console.log(r);
})
.catch(function () {});
Promise
의 then
의 콜백 함수는 resolve
로 호출되며 catch
로 넘겨준 콜백 함수는 reject
로 호출된다.
Promise
를 조금 더 가독성이 좋게 만들기 위해서 async - await
을 이용할 수 있다.
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function main() {
console.log("1");
try {
const result = await delay(3000);
} catch (e) {
console.error(e);
}
console.log("2");
}
main();
위의 코드를 실행해보면 콘솔에 1
이 출력된 후 3초 뒤에 2
가 출력된다.
await
은 async
키워드가 붙은 함수에서 사용 가능하며 Promise
의 resolve
된 값을 저장할 수 있다.
Promise
의 reject
는 try-catch
구문의 catch
를 이용해 사용할 수 있다.
어플리케이션에서 여러 개의 컴포넌트에서 하나의 특정 상태가 필요한 경우가 생긴다.
Flux
패턴은 하나의 store
에서 상태에 관한 정보를 저장해 사용하는 패턴이다.
가상돔 사용 이전의 어플리케이션은 하나의 상태가 변경될 시 모든 화면이 다시 그려지기 때문에 불가능 했다.
React에서는 가상돔을 사용해 기존의 DOM과 비교해 다른 부분만 DOM에 반영하기 때문에 가능해졌다.
redux
의 store
에 저장된 데이터는 컴포넌트가 직접적으로 데이터를 바꿀 수 없다.
상태를 직접적으로 바꿀 수 없도록 하기위해 클로저를 이용해 state
를 은닉화한다.
createStore
를 이용해 store
를 생성할 수 있고 getState
를 이용해 상태를 볼 수 있다.
store
에 저장된 값은 반드시 reducer
함수가 호출되어 변경되어야 한다.
reducer
는 반드시 action
을 인자로 받는 dispatch
를 이용해 호출되어야 한다.
reducer
는 호출될 때 기존 저장된 상태와 실행할 액션 객체를 매개변수로 전달 받는다.
액션 객체의 타입에 type
에따라 기존 state
의 값을 변경시켜 store
에 저장된다.
subscribe
함수는 store
를 구독하는 listener
배열에 받은 fn
을 저장해 dispatch
시 마다 실행한다.
redux.js
export function createStore(reducer) {
let state;
const listener = [];
const publish = () => {
listener.forEach(({ subscriber, context }) => {
subscriber.call(context);
});
};
const dispatch = (action) => {
state = reducer(state, action);
publish();
};
const subscribe = (subscriber, context = null) => {
listener.push({ subscriber, context });
};
const getState = () => ({ ...state });
return {
getState,
dispatch,
subscribe,
};
}
export function actionCreator(type, payload = {}) {
return {
type,
payload: { ...payload },
};
}
index.js
아래와 같이 실제 redux
와 동일하게 reducer
를 작성할 수 있다.
import { createStore, actionCreator } from "./redux";
const INIT = "init";
const INCREMENT = "increment";
const RESET = "reset";
function reducer(state = {}, { type, payload }) {
switch (type) {
case INIT: {
return {
...state,
count: payload.count,
};
}
case INCREMENT:
return { ...state, count: state.count ? state.count + 1 : 1 };
case RESET:
return {
...state,
count: 0,
};
default:
return { ...state };
}
}
액션을 조금 더 쉽게 dispatch
하기 위해 아래와 같은 헬퍼 함수들을 이용할 수 있다.
init
: store
의 값을 count로 초기화 increment
: store
의 count를 1증가 또는 1로 설정 reset
: store
의 count를 0으로 초기화 function init(count) {
store.dispatch(actionCreator(INIT, { count }));
}
function increment() {
store.dispatch(actionCreator(INCREMENT));
}
function reset() {
store.dispatch(actionCreator(RESET));
}
아래와 같이 실제 store
를 생성하고 subscribe
함수를 이용해 로깅 또한 진행할 수 있다.
createStore
함수로 스토어를 생성 getState
함수를 사용해 값을 출력하는 함수 작성 subscribe
메서드를 이용해 2번에서 작성한 함수로 구독 const store = createStore(reducer);
function update() {
console.log(store.getState());
}
store.subscribe(update);
아래와 같은 방법으로 action
을 dispatch
해 store
에 저장된 상태를 변경할 수 있다.
type
과 payload
을 포함한 액션을 dispatch
actionCreator
헬퍼 함수를 사용한 후 dispatch
payload
를 전달받아 dispatch
까지 해주는 헬퍼 함수 이용store.dispatch({ type: INCREMENT }); // { count: 1 }
store.dispatch(actionCreator(INCREMENT)); // { count: 2 }
increment(); // { count: 3 }
store.dispatch({ type: INIT, payload: { count: 5 } }); // { count: 5 }
store.dispatch(actionCreator(INIT, { count: 0 })); // { count: 0 }
init(10); // { count: 10 }
store.dispatch({ type: RESET }); // { count: 0 }
store.dispatch(actionCreator(RESET)); // { count: 0 }
reset(0); // { count: 0 }
actionCreator
, increment
, reset
함수는 액션을 조금 더 간단히 dispatch
하도록 도와주는 헬퍼함수다.
위와 같이 우리가 사용하던 redux
와 비슷하게 작동하는 것을 볼 수 있다.
2회차를 하면서 평소 doc에 있던 redux를 어떤식으로 만들었는지 알 수 있게 되는 강의였습니다.