ReadableStream은 fetch함수를 실행하여 얻은 response 객체의 body의 타입으로, fs 모듈에서 제공하는 클래스이며 파일을 읽고 쓰는데에 적합하다. Readable 클래스는 파일을 읽고 쓰는 필요가 없을 경우에 사용하며 메모리 위에서만 존재하고 stream 모듈에서 제공하는 클래스이다.
nodejs 환경에서 다른 서버로 부터 받은 응답을 바로 나의 클라이언트에게 전달 해주는 메서드인 pipe를 활용하기 위해, ReadableStream객체의 getReader 메서드를 통해 데이터(버퍼)를 추출해내서 Readable객체에 push를 해줘야 했다.
자바스크립트를 이용해 실수의 연산을 하다보면 소수점이 이상하게 나타나는 경우가 있다. 컴퓨터는 기본적으로 이진수를 이용하기 때문에 실수를 다루는 방식은 컴퓨팅 언어마다 다르다. Javascript는 모든 숫자를 IEEE 754 표준을 따르는 배정밀도 64 비트 부동 소수점 형식으로 표현하며, 이 형식은 실수를 근사치로 저장한다.
따라서 0.6 / 0.2 와 같은 연산을 실행 해보면 3이 아닌 2.999999999996 와 같은 예측에서 벗어난 값이 나온다. 이를 해결하기 위해 실수를 정수로 바꾼 후에 연산을 하고 다시 원래 실수로 돌려놓는 방법을 사용한다.
var a = 1;
console.log(a)//1
var a = 2;
console.log(a)//2
위와 같이 한번 선언을 한 a 변수 일지라도 var태그를 사용하여 선언 하면 다시 선언을 할 수 있다. 이는 간단한 테스트에는 편리할 수 있으나 코드량이 많아진다면 어디에서 사용했었는지 파악하기가 어렵고 기존의 값이 바뀌어 버그가 발생할 수 있다.
이와 달리 let과 const는 재선언이 불가능하다. 하지만 이 둘에도 차이점이 있는데, 그것은 immutable여부이다.
let은 변수에 재할당이 가능하고, const는 불가능하다.
2. 호이스팅
호이스팅(Hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다.
자바스크립트는 ES6에서 도입된 let, const를 포함하여 모든 선언(var, let, const function function*, class)을 호이스팅한다.
하지만, var 로 선언된 변수와 달리 let로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다.
console.log(foo);//undefined
var foo;
console.log(bar); //Error: Uncaught ReferenceError: bar is not defined
let bar;
이는 let 로 선언된 변수는 스코프의 시작에서 변수의 선언까지 일시적 사각지대(Temporal Dead Zone; TDZ)에 빠지기 때문이다.
참고로 변수는 선언 단계 > 초기화 단계 > 할당 단계에 걸쳐 생성되는데,
var 으로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. 하지만 let으로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.
//스코프의 선두에서 선언 단계와 초기화 단계가 실행된다.
//따라서 변수 선언문 이전에 변수를 참조할 수 있다.
console.log(foo);
var foo;
console.log(foo);
foo = 1; //할당문에서 할당 단계가 실행된다.
console.log(foo); //1
//스코프의 선두에서 선언단계가 실행된다.
//아직 변수가 초기화(메모리 공간 확보와 undefined로 초기화)되지 않았다.
//따라서 변수 선언문 이전에 변수를 참조할 수 없다.
console.log(foo); //ReferenceError: foo is not defined
let foo; //변수 선언문에서 초기화 단계가 실행된다
console.log(foo); //undefined
foo = 1; //할당문에서 할당 단계가 실행된다.
console.log(foo); //1
3. 정리
변수 선언에는 기본적으로 const를 사용하고, 재할당이 필요한 경우에 한정해 let을 사용하는것이 좋다.
그리고 객체를 재할당하는 경우는 생각보다 흔하지 않다. const를 사용하면 의도치 않은 재할당을 방지해 주기 때문에 보다 안전하다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<App />,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
import './App.css';
import ReactDOM from 'react-dom';
let tweet = <div>{Tweet({ writer: '이호준' })}</div>;
let tweet2 = (
<div>
{(function () {
return '리액트';
})()}
</div>
);
let nowLearning = false;
let tweet3 = (
<Tweet writer="김코딩">
{(function () {
if (nowLearning) {
return '리액트';
} else {
return '배틀그라운드';
}
})() + '는 늘 짜릿하네요'}
</Tweet>
);
function Tweet4(){
let langs = ["javascript", 'HTML', 'Python']
// eslint-disable-next-line
return (
<div>
{langs.map(it=><div>{it}</div>)}
</div>
)
}
function Tweet({ writer, children }) {
return (
<div>
{writer}
{children}
</div>
);
}
function clock() {
ReactDOM.render(
<div>{new Date().toLocaleTimeString()}</div>,
document.getElementById('clock'),
);
}
setInterval(clock, 1000);
function App() {
return (
<div className="App">
<header className="App-header" id="App-header">
<h1>Hello World!</h1>
<h2>Start editing to see some magic happen!</h2>
{tweet}
{tweet2}
{tweet3}
<Tweet4 />
<div id="clock">{new Date().toLocaleTimeString()}</div>
</header>
</div>
);
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App.js';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
2. var로 선언한 변수는 선언 전에 사용해도 에러가 나지 않지만 let, const는 에러가 발생합니다.
3. var는 이미 선언되어있는 이름과 같은 이름으로 변수를 또 선언해도 에러가 나지 않지만 let, const는 이미 존재하는 변수와 같은 이름의 변수를 또 선언하면 에러가 납니다.
4. var, let은 변수 선언시 초기 값을 주지 않아도 되지만 const는 반드시 초기값을 할당해야 합니다.
5. var, let은 값을 다시 할당할 수 있지만 const는 한번 할당한 값은 변경할 수 없습니다(단, 객체 안에 프로퍼티가 변경되는 것까지 막지는 못합니다).
함수레벨 스코프란?
// var는 function-scope이기 때문에 for문이 끝난다음에 i를 호출하면 값이 출력이 잘 된다.
// 이건 var가 hoisting이 되었기 때문이다.
for(var j=0; j<10; j++) {
console.log('j', j)
}
console.log('after loop j is ', j) // after loop j is 10
// 아래의 경우에는 에러가 발생한다.
function counter () {
for(var i=0; i<10; i++) {
console.log('i', i)
}
}
counter()
console.log('after loop i is', i) // ReferenceError: i is not defined
세로 길이 2, 가로 길이 n인 2 x n 보드가 있습니다. 2 x 1 크기의 타일을 가지고 이 보드를 채우는 모든 경우의 수를 리턴해야 합니다.
입력
인자 1 : n
number 타입의 1 이상의 자연수
출력
number 타입을 리턴해야 합니다.
주의사항
타일을 가로, 세로 어느 방향으로 놓아도 상관없습니다. (입출력 예시 참고)
입출력 예시
let output = tiling(2);
console.log(output); // --> 2
output = tiling(4);
console.log(output); // --> 5
/*
2 x 4 보드에 타일을 놓는 방법은 5가지
각 타일을 a, b, c, d로 구분
2 | a b c d
1 | a b c d
------------
2 | a a c c
1 | b b d d
------------
2 | a b c c
1 | a b d d
------------
2 | a a c d
1 | b b c d
------------
2 | a b b d
1 | a c c d
------------
*/
Advanced
타일링 문제를 해결하는 효율적인 알고리즘(O(N))이 존재합니다. 반드시 직접 문제를 해결하시면서 입력의 크기에 따라 어떻게 달라지는지 혹은 어떻게 반복되는지 관찰하시기 바랍니다.
// naive solution: O(2^N)
// 2 x 4 보드에 타일을 놓는 방법은 5가지다.
// 각 타일을 a, b, c, d로 구분한다.
// 아직 타일이 놓이지 않는 부분은 -로 표기한다.
// 타일을 놓는 방법은 가장 왼쪽부터 세로로 놓거나 가로로 놓는 것으로 시작한다.
// 1) 세로로 놓는 법
// 2 | a - - -
// 1 | a - - -
// ------------
// 2) 가로로 놓는 법
// 타일을 가로로 놓게 되면, 그 바로 아래에는 가로로 놓을 수 밖에 없다.
// 2 | a a - -
// 1 | b b - -
// ------------
// 이때, 타일이 아직 놓이지 않은 부분은 사실 크기만 다를뿐 같은 종류의 문제라는 것을 알 수 있다.
// 즉, 2 x 4 보드에 타일을 놓는 방법은 아래 두 가지 방법을 더한 결과와 같다.
// 1) 2 x 3 보드에 타일을 놓는 방법
// 2) 2 x 2 보드에 타일을 놓는 방법
// 따라서 2 x n 타일 문제는 아래와 같이 재귀적으로 정의할 수 있다.
// 주의: 재귀적 정의에는 항상 기초(base), 즉 더 이상 재귀적으로 정의할 수 없는(쪼갤 수 없는) 문제를 별도로 정의해야 한다.
// let tiling = function (n) {
// if (n <= 2) return n;
// return tiling(n - 1) + tiling(n - 2);
// };
// dynamic with memoization: O(N)
let tiling = function (n) {
// 인덱스를 직관적으로 관리하기 위해
// 앞 부분을 의미없는 데이터(dummy)로 채운다.
const memo = [0, 1, 2];
// 재귀를 위한 보조 함수(auxiliary function)을 선언)
const aux = (size) => {
// 이미 해결한 문제는 풀지 않는다.
if (memo[size] !== undefined) return memo[size];
if (size <= 2) return memo[n];
memo[size] = aux(size - 1) + aux(size - 2);
return memo[size];
};
return aux(n);
};
// dynamic with tabulation: O(N)
// tabulation은 데이터를 테이블에 정리하면서 bottom-up 방식으로 해결하는 기법을 말합니다.
// let tiling = function (n) {
// const memo = [0, 1, 2];
// if (n <= 2) return memo[n];
// for (let size = 3; size <= n; size++) {
// memo[size] = memo[size - 1] + memo[size - 2];
// }
// return memo[n];
// };
// dynamic with slicing window: O(N)
// slicing window은 필요한 최소한의 데이터만을 활용하는 것을 말합니다.
// 크기 n의 문제에 대한 해결을 위해 필요한 데이터는 오직 2개뿐이라는 사실을 이용합니다.
// let tiling = function (n) {
// let fst = 1,
// snd = 2;
// if (n <= 2) return n;
// for (let size = 3; size <= n; size++) {
// // 앞의 두 수를 더해 다음 수를 구할 수 있다.
// const next = fst + snd;
// // 다음 문제로 넘어가기 위해 필요한 2개의 데이터의 순서를 정리한다.
// fst = snd;
// snd = next;
// }
// return snd;
// };