기본 콘텐츠로 건너뛰기

5월, 2024의 게시물 표시

var는 더이상 사용하지 않을 것인가?

  왜 var는 미움을 받았을까? 우리 동년배 개발자들은 var를 사용하여 개발하세요. 라는 것을 이야기를 많이 들었을 것이다. 그 당시에는 var라는 키워드 자체가 없었던 시절이 있었다.  당시에는 window에 모든 데이터를 제공을 하게 되는 방식이었습니다. 그러다 보니 메모리관리도 안되었으며, 문제가 많이 발생하였었다. var라는 키워드는 window를 더이상 더럽히지 않아도 되었다. 하지만, let, const가 추가적으로 키워드가 생기게 되었다. 하지만, 이는 var 키워드와 스코프가 다른 스코프를 생성하게 되는데, 과연 이게 맞는가?  굳이 함수스코프를 갖는 변수를 선언 할 것이가? 반대로, 블록스코프에서만 사용할 수 있는 let, const를 사용할 것인가? 자 일단 나의 입장에서 const 키워드는 명백한 실수로 보인다. const의 가장 큰 문제는 레퍼런스만 변경이 막혀 있고, immutable을 보장하지는 않는다는 것이다.   자 그러면 var, let만 보도록 하자. var의 경우애는 여러번 중복 선언을 할 수 있고, let의 경우 다른 스코프에서만 중복선언이 가능하다.  자 그러면 왜 쓰지 말라고 하는가? 개발자들은 넓은 범위 보다 항삭 좁은 범위를 사용하길 원한다. 실예로, Bool, Short, Int, Word, Long를 보면, 실제로, Bool, Short, Word, Int의 경우, 32bit PC의 경우, 4byte, 64bitPC의 경우, 8Byte를 사용하게 된다. 그렇다면 이는 메모리를 최적화를 위한 데이터가 아니다. 다 사람을 위한 것이다. 우리는 왜 Long대신, Bool을 선택 했는가?  다른 개발자가 이해하기가 더 쉽고 범위가 좁은 데이터 타입이기 때문이다. 요즘에 var 키워드의 사용법에 대해.....  아래의 코드처럼 사용할 수 있음을 이야기하고 있다. var foo = 1 // ...아주 긴 코드... var foo // 상기 시키기 위한 코드  자 그러면 저 아주 긴 코드를 지나서 foo가 1로 초기화 되

javscript Map을 사용하자.

 javascript에서 키-값 쌍을 같는 자료구조를 사용 할 때에, Object로 많이 진행 할 것이다. 하지만, 사용할 수 있는 기능이 그리 많지는 않다는 것을 이야기하고 싶다. 간단하게 오브젝트내의 키를 몇개나 정의하였는가? 에 해당하는 코드는 아래와 같다. const map = new Map () const obj = {} map . set ( 'a' , 'a' ) obj . a = 'a' map . set ( 'b' , 'b' ) obj . b = 'b' map . set ( 'c' , 'c' ) obj . c = 'c' console . log ( map . size ) console . log ( Object . keys ( obj ). length )  사실 이것은 그렇게 혹 할만한 내용은 아닐 것 같다. 하지만 이건 누군가에게는 좋은 점이라고 볼 수 있을것이다. const map = new Map () const obj = {} map . set ( 1 , 'b' ) obj [ "1" ] = 'b' map . set ( 0 , 'a' ) obj [ "0" ] = 'a' map . set ( 2 , 'c' ) obj [ "2" ] = 'c' console . log ( 'map' , [... map ]. map (([key, value]) => value). join ( ', ' )) // map [b, a, c] console . log ( 'obj' , Object . entries ( obj ). map (([key, value]) => value). join ( ', ' )) // obj [a, b, c]  

Well-formed iterator

  자, 일단 이터레이터라는 용어는 이제 익숙 할 것이다. 별 내용은 아니니, 잠깐의 아래의 예제를 보자. const iterable = { [ Symbol . iterator ](){ let i = 3 ; return { next () { return i== 0 ? { done : true }:{ value : i-- , done : false } } } } } console . log ([... iterable ])  간단한 코드이다. iterable 객체는 [Symbol.iterator]을 제공 하고 있으니 iterable을 만족한다. 리턴 하는 iterator객체는 next함수를 제공하고 있고, value와 done을 제공하고 있으닌 Iterator를 만족한다.  자 그러면 잘만들어지 iterator? 그건 뭘까? 일단 제너레이터 펑션으로 만들면 잘만들어진 이터레이터일 것이다. function * foo () { yield 1 yield 2 yield 3 } console . log ([... foo ()])   자, 아직까지는 솔직히 코드만 보고는 차이를 모르겠다. 하지만 이러면 어떤가? const iterable = { [ Symbol . iterator ](){ let i = 3 ; return { next () { return i== 0 ? { done : true }:{ value : i-- , done : false } } } } } function * foo () { yield 1 yield 2 yield 3 } console . log ([... foo ()[ Symbol . iterator ]()]) // [1, 2, 3] console . log ([... iterable [ Symbol . iterator ]()]) // iterable[Symbol.iterator] is not ...

재귀함수?

  자 일단 재귀함수를 왜 사용할까? 사실 재귀함수로 작성 할 수 있는 모든 알고리즘은 반복문으로 작성할 수 있으며, 모든 반복문 또한 재귀함수로 작성 할 수 있다. 하지만, 여기서 반복문으로 작성한다는 것 자체는 명령형 스타일의 코드이고, 재귀 함수를 작성한 다는 것은 선언형으로 작성한다는 것이다.  명령형과 선언형 중에 선언형이 의미 파악이 더욱 파악하기 쉽다.  자 간단하게 아래의 함수를 보자. function foo (n = 0 ) { if (n === 0 ) return 0 let res = 1 for ( let i = n; i >= 1 ; i --) { res *= i } return res ; }  자 간단하게 위의 코드를 보자. foo라는 함수는 n의 값을 하나씩 줄이면서 기존 결과에 값을 곱하게 된다. function bar (n = 0 ) { if (n === 0 || n === 1 ) return n; return n * bar (n - 1 ) }  자 위의 코드를 보자 bar의 경우에는 종료 시점 0, 1과 현재의 n과 bar(n-1)을 곱한다. 라는 식처럼 보인다.  자 우리는 팩토리얼을 구현한 것이다. 아래의 정의를 보았을 때, foo와 bar중에 어떤 모양이 팩토리얼의 정의에 더 비슷한 모양인가? n ! = n * ( n - 1 ) * ( n - 2 ) * ... * 1  특히 수학의 반복의 표현에서는 대부분의 경우, 재귀로 표현하는 것이 구현 및 의미파악이 더 쉽게된다. function bar (n = 0 ) { if (n === 0 || n === 1 ) return n; return n + bar (n - 1 ) }  자 간단하게 위의 코드로 변경해보았다. 그냥 1 + 2 + 3... 이런식으로 더하는 코드이다.  자 재귀의 가장 큰 문제는 이것이다. 기본적으로 브라우저에서 사파리를 제외하고는 꼬리재귀 최적화가 되어있지 않기 때문