기본 콘텐츠로 건너뛰기

히든 클래스 (Shape)에 대해 간단하게 알아보자.

 히든클래스에 대해 잠깐 알아보자. const obj = { a : 1 } console . log ( obj . a ) // 1  자 우리가 위처럼 작성하였을 때, 자바스크립트 엔진은 저걸 어떻게 해석할까? 일단 obj의 주소를 찾아 갈 것이고, 그 주소에서 a를 찾게 될 것 이다.  자 그러면 a에는 어떤 정보가 있을까? Object . getOwnPropertyDescriptor ( obj , 'a' ) { configurable: true , enumerable: true , value: 1 , writable: true , }  위와 같은 행위를 진행 하게 된다. 실제로는 저 a를 접근할 때, 페이로드가 꽤나 큰 비용이 든다.  하지만 매번 저렇게 진행을 할까? 꽤나 비싼 내용인데 말이다.  자바스크립트 엔진에서 동일한 모양을 동일한 Shape로 인식하게 되며, 위와같은 비용을 덜어낸다.  위에서 언급하였듯이 동일한 모양이다. const obj0 = {} // C0 obj0 . a = 'string' // C1 const obj = { a : 1 } // C1  위의 코드를 보면 obj0는 C0 Shape, obj0.a 를 추가하면 C1,  obj는 처음부터 a를 갖고 있기 때문에, C1이 되게 된다.  동적으로 속성을 추가하는 건 좋지 않게 된다. 추가적인 내용은 다음글로 알아보자.
최근 글

옛날 코드 다시 읽기 5 (new Function)

  옛날 코드를 생각하며.... 예전에 eval을 다루었던 적이 있었다. 뭐 대부분의 상황에서 eval은 백엔드에서 script를 전달하여, 클라이언트에서 사용 할 수 있게 만드는 역할을 하였다. 하지만, eval을 듣기만 해도 몸서리치는 사람들이 주변에 한 두명 정도는 있을 것이다.  가장 큰 문제는  코드 인젝션  : eval 은 전달된 문자열을 코드로 실행하기 때문에, 외부 입력을 eval 에 전달할 경우, 악의적인 사용자가 임의의 코드를 실행할 수 있는 보안 취약점이 생깁니다. 이는 XSS(Cross-Site Scripting)와 같은 공격에 취약하게 만듭니다.  신뢰할 수 없는 소스  : eval 에 의해 실행되는 코드가 신뢰할 수 없는 소스에서 온다면, 시스템이나 데이터를 손상시킬 수 있습니다. 위와 같은 문제를 꼽을 수 있다.  그렇다면, 어떤 대안이 있을까? 서버가 클라이언트에 어떤 행동을 하게 하기 위한 대안 말이다. 문자열로 스크립트를 바로 실행 하지 않고, 함수를 만들어내는 것이다.  당연하게도, new Function 키워드의 경우도 완벽한 보안을 제공하지는 않는다. 하지만, eval의 경우, 현재 스코프의 변수들을 접근 할 수 있는 반면, new Function은 글로벌함수만 접근 할 수 있고, 함수를 만들어내는 코드일 뿐, 실행에 대한 제어는 프런트에서 진행을 하게 된다.  일단 오해를 풀고가야 하는 부분이 있다. eval < new Function 상대적으로 보안에 좋다는 거지, 당연하게도 매우 취약하다. 과거의 우리는 어쩔 수 없이 사용하였을 뿐, 대체할만한 코드는 너무나 많고 많다.  하지만, 현대에 new Function이 갖는 의의는 서버에서 제공하는 문자열을 클라이언드에서 함수로 만들어서 실행 하는 것이 아니다!  템플릿 엔진  우리가 사용하는 .jsx에 대해 생각해보자, 자바스크립트에 그러한 문법이 있는가? 1번 const dom = <div> test </div>  자 위의 코드를 생각해보자,

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... 이런식으로 더하는 코드이다.  자 재귀의 가장 큰 문제는 이것이다. 기본적으로 브라우저에서 사파리를 제외하고는 꼬리재귀 최적화가 되어있지 않기 때문

모든 자바스크립트 개발자가 알아야 하는 33가지 개념 - 원시타입

  자 일단 원시타입이 뭔데? 자 간단하게 설명해보자. 원시타입은 프로그래밍 언어에서 기본적으로 제공되는 데이터 유형이며. 이것은 다른 데이터 유형으로 분류되지 않는 가장 기본적인 유형으로, 보통 숫자, 텍스트 또는 논리값과 같은 간단한 값을 나타내게 된다. 원시타입은 일반적으로 메모리에 직접 저장되며, 객체가 아니라는 점이 중요하다.  자 객체가 아니다?? 그것은 무슨 뜻일까?  "객체가 아니다"는 원시타입이 다른 데이터 유형인 객체와는 다르게 메모리에 직접적으로 저장되는 데이터 유형이라는 것을 의미한다. 객체는 데이터와 그 데이터를 조작하기 위한 메서드(method)들을 포함하는 복합적인 개념이며, 일반적으로 참조(reference)로 저장된다. 이에 반해, 원시타입은 데이터 값 자체가 메모리에 저장된다 자 이제 참조라는 단어가 나왔다. 이건 간단하게 코드로 보자 :) let arr = [ '1' , '2' , '3' , '4' ] let str = '1234' arr [ 1 ] = '3' str [ 1 ] = '3' console . log ( arr . join ( '' )) // 1334 console . log ( str ) // 1234 자, 위의 값에서 arr은 변경이 되었지만, str은 변경 되지 않았다. arr [ 1 ] = '3' // 해당 라인의 의미는 arr 의 1 번 인덱스의 값을 '3' 으로 바꾸어라 라는 의미이다 . // 해당 라인의 의미는 str[1] 의 평가가 끝나서 // "2" = '3' 이 되게 된다 . str [ 1 ] = '3' 레퍼런스의 참조는 평가를 진행하는 경우, 메모리 주소의 참조가 되게 된다. 원시타입은 무엇을 해도, 값일 뿐이라서, 값을 가지고만 핸들링하게 된다.  자바스크립트 세상의 원시타입

모든 자바스크립트 개발자가 알아야 하는 33가지 개념 - 호출 스택

 언젠가 "모든 자바스크립트 개발자가 알아야 하는 33가지 개념" 이라는 내용이 언젠가 유행이었던 적이 있다.  뭐 내용을 보면, 아주 당연하게 알아야 하는 내용이기도 하고, 추가가 되면 되었지 몰라야 할 만한 내용은 딱히 없다.  해당 내용으로 한동안 블로그를 적고자 한다. 1. JavaScript와 같은 고급 코드 라인이 호출 스택 ( 고급 언어 에서 기계 코드 까지 ) 에서 스택 프레임으로 변환되고 실행되는 방식을 이해합니다.  자료구조를 배우면, 링크드리스트를 배우고 나서, 스택을 배웠던 기억이 있다. 간단하게 선입후출 형태의 자료구조이다. 일반적으로 대부분의 언어에서 스택은 최대크기를 갖는다.  자 여기서 이야기 할 것은 자료구조로써의 스택을 공부하고자 하는 것 아니지만, 선입, 후출, 최대크기가 있다는 정도만 생각하자. let g = 0 function first () { let a = 1 let second = () => { let b = 2 third (); console . log ( " 두 번째 " , a ); debugger ; } second (); console . log ( " 첫 번째 " ); debugger ; } function third () { let c = 3 console . log ( " 세 번째 " ); debugger ; } first (); 자 간단하게 보자.  스택에는 위와 같이 쌓이게 되며, fist()가 가장 먼저 stack에 쌓이지만, first()가 가장 마지막까지 남아 있는 것을 볼 수 있다.  하지만 callStack은 함수만 있는 것이 아니다. 간단하게 실행 컨텍스트 까지 포함된다.    실행 컨텍스트라고 하면, 복잡하게 생각될 수 있는데, 크게 세가지로 볼 수 잇다. VariableEnvironment: 

;

  자 제목이 참 뜬근없기도 하다. 저게 뭘까?  empty statement라고 부르는 것이다. for ( let i = 0 ; i < 1000 ; i++) ; 과연 위의 코드는 무엇일까? 옛날 개발자가 보았다면, 특정 시간을 기다리는 코드라고 말할 것이다. 하지만, 현대에서는 절대로 의미 없는 코드라는 것은 알고 있을 것이다. 자 이제 사용할만한 부분을 보자. const condition = 'A' ; if ( condition === 'A' ) { // do Something } else if ( condition === 'B' || condition === 'C' ) ; else { // do Something }  자 일단 위 같은 코드인데, 1. A인 경우 2. A가 아니면서 B,C 인 경우 3. A, B, C 가 아닌경우. 2번의 경우 아무것도 실행 안하고 else문에 도달하는 것을 멈추게 할 수 있다. 실무 입장에서는?  참으로 javascript는 empty를 좋아한다. 희소배열 이라던지, 애로우 평션의 empty body라던지, empty statement라던지,  하지만 모든 empty요소가 오류의 가능성을 내재 하고 있다. 1. 희소 배열의 경우, 일반적인 for문과 map의 순회 횟수가 다른다. 2. 애로운 평션의 엠티 바디의 경우, 원래 원했던 리턴값이 undefined인지, plain object인지 헷갈린다. 3. empty 스테이트먼트의 경우, 진짜로 원해서 그 위치에 ;가 들어간건지 헷갈린다.

세상에 나와선 안되었던 키워드. async/await

 오늘은 한 가지 세상에 있어서 안되었던 키워드를 이야기 하고자 한다. 뭐 물론, 이 키워드를 이용하여, 만들어진 프로그램은 수도 없이 많을 것이다. 특히나, ES6이후에 만들어진 웹사이트 Node 서버들은 무조건 포함이 되는 키워드일 것이다.  이런 생각을 하는 사람들은 그렇게 많지는 않을 것 같다. 아마도 동료들한테 말을 하는 순간 엄청나게 욕을 먹고 까일 것 같다...  async/await은 비동기 코드를 동기코드로 만들어주는 아주 마법 같은 키워드이다. 하지만 그로 인해서 모든 대부분의 개발자들이 동시성이 주는 마법을 잊어버리고 있다. 특히나 싱글스레드인데도 사용하다니, 더 이상 동시성에 대한 고려를 하지 못하도록 원천 차단하고 있다.  그래도 Promise 때 까지만 해도 좋았다. 무슨 말일까? 잠깐만 아래의 코드를 잠깐만 생각해보도록 하자. promise 방식 function initData () { apiCall1(). then ((res) => { this . data1 = res. payload }) apiCall2(). then ((res) => { this . data1 = res. payload }) apiCall3(). then ((res) => { this . data1 = res. payload }) } async/await async function initData () { this . data1 = await apiCall1(). payload this . data2 = await apiCall2(). payload this . data3 = await apiCall3(). payload } 자 이 두개의 코드가 내가 말하고 싶은 것의 축약본이다. apiCall1: 1초, apiCall2: 2초, apiCall3: 3초, 라고 생각해보자. 총: async/await으로 작성된 코드는 6초, promis

함수형 프로그래밍의 역사.

  간단하게, 이 내용을 작성하는 것은, 자꾸만 주변에서 함수형프로그래밍이 망했던... 패러다임이라는 내용이 오갔던, 내용이 있어서 그에 대한 내용을 작성을 합니다.  요즘 개발자들이 오해를 하는 부분들이 있는데, 함수형프로그래밍이 아주 최신에 만들어진 패러다임이라는 생각이다.  간단하게 위의 이미지만 보더라도 말이다. 뭐 간단하게 말하면, 라이터와 성냥같은 차이이다. 라이터가 성냥보다 최근에 만들어진 도구라고 생각 할 수 있지만, 성냥이 더 최근에 개발된 것이다.  컴퓨터 공학도라고 하면, 튜링머신에대해서 배웠을 것이다. 영화( 이미테이션 게임 (2014) )에서 본 사람들도 있을 것이고 말이다. 튜링머신이 왜 만들어졌는지 알까? 영화만으로 본 사람들은 독일군의 암호를 해독하는 그 장치가 튜링머신이라고 오해 할 수 있다. 하지만, 컴퓨터 공학도 라면, 간단하게 종이와 볼펜만 가지고 튜링머신을 만들 것이다.  튜링머신은 20대 였던, 앨런튜링이 작성했던, 논문(  1936년   )에서 만들어진 기계 이름이다. 발명하게된 이유는 A라는 수학자가 세상에 우리가 알고 있는 모든 명제를 학습 시킨 머신이 있다면, 확인되지 않은 명제x를 입력을 하게 되었을 때, 모든 명제에 대해서 참과 거짓을 판별을 할 수 있고, 그렇게 하여 자동으로 명제X들을 다 풀어낼 수 있다. 이 내용을 부정하기 위해 만들어진 종이쪼가리 머신 일 뿐이다. 거짓에대한 증명을 홀팅프러블럼을 이용하여 증명 하였다.  일단 대충 내용이 튜링머신에 대해서 길어지니, 왜 이 내용을 이야기하는 것일까? 우리가 알고 있는 절차형 프로그래밍이 튜링머신과 닮았다고 생각하지 않는가?  현재의 상태를 보관 하고 있는 테이프(상태)에 따라 명령이 실행되는 방식이다. 절차지향형 프로그래밍언어 C라이크 언어들에 대한 내용이다.  자 절차형프로그래밍에 대한 시초는 대충 훑어 보았고, 함수형프로그래밍의 시초를 한 번 살펴보자. 함수형프로그래밍의 방식은 람다대수부터 시작이라고 볼 수 있다.  1930년 대 알론조 처치가 수학기

Iterator Helpers

  오늘날 generator function을 사용하는 사람들은 매우 적다. 현재 상태에 따라, 결과가 달라진다라는 것도 요즘 패러다임에는 맞지 않는 방식이니까 말이다. 하지만, 이번에 Stage 3 Draft / December 12, 2023 스펙에 "Iterator Helpers"가 등장한 것은 꽤나 흥미로운 일이다.  언젠가 iterator에 대해서 다루면서, 가장 간단하게 iterator를 생성하는 방법으로 generator function을 소개하였다. function * test () { yield 1 ; yield 2 ; yield 3 ; } let iter = test () ; const arr = [... iter ] ; console . log ( 'arr' , arr ) ; iter = test () ; for ( const item of iter ) { console . log ( 'item' , item) ; }  iterator는 펼침연산을 수행할 수 있고, for of를 이용하여 순회 할 수 있다. 하지만 무엇인가 부족했다. iterator 자체로는 map을 사용할 수 도 없고, filter를 사용할 수 없었다.  그것이 바로 Iterator helpers인 것이다. function * test () { yield 1 ; yield 2 ; yield 3 ; } [... test (). map (v => v * 2 ). filter (v => v % 3 )]  map과 filter를 사용하여, 새로운 iterator를 받을 수 있게 되었다. 그 뿐만이 아니다. [... test (). flatMap (v => test ()). map (v => v * 2 ). filter (v => v % 3 )]  당연하게도, flatMap이 사용 가능하다!  위에서는 펼짐 연산자를 이용하여, 배열로 형변환을 해주었지만?  test (). toArra

옛날 코드 다시 읽기 4 (비동기 api 호출)

 제너레이터 함수를 다루어 보았을까? 만약 그렇다면, async/await이 없던 시절에 개발을 했던 사람일 수 있다.  자 오늘은 옛날 코드 다시보기가 될 수 있을 수 있다.  callBack  자 하나하나씩 보도록 하자. 일단 xmlHttpRequest나 form방식은 건너띄고, 우리에게 친숙한 callback 방식이다. // callBack 버전 $.ajax({ url : 'https://jsonplaceholder.typicode.com/comments' , dataType : 'json' , success : function (comments) { // 댓글 목록을 처리합니다 . // ... // 댓글 목록을 가지고 POST API 호출 const comment = comments[ 0 ]; // 첫 번째 댓글 선택 const postData = { postId : comment . postId , name : comment . name , email : comment . email , body : comment . body , }; $.ajax({ url : 'https://jsonplaceholder.typicode.com/comments' , method : 'POST' , data : JSON . stringify ( postData ), dataType : 'json' , success : function (data) { console . log ( ' 새로운 댓글이 생성되었습니다 :' , data);