처음 몇 년 동안 자바스크립트를 사용하면서, 나는 사기꾼처럼 느껴졌었다. 프레임워크를 이용해 웹사이트를 만들 수 있었지만, 뭔가 빠진 느낌이었다. 기초에 대한 확실한 이해가 없었기 때문에 자바스크립트 구직 면접을 두려워했었다.
몇 년 동안, 나에게 자신감을 주는 자바스크립트의 멘탈 모델을 만들었다. 여기, 매우 압축된 버전의 멘탈 모델을 공유하려고 한다. 단어를 하나 제시하고, 각 주제에 맞게 몇 문장을 설명하는 방식으로 구성했다.
글을 읽으면서, 각 토픽에 대해 얼마나 자신감을 느끼는지 마음속으로 점수를 매겨봐라. 당신이 토픽 중 몇 가지를 놓쳤는지 판단하지 않을 것이다! 이 글의 끝에는 몇 가지 도움이 될만한 것을 작성했다.
값(Value): 값의 개념은 약간 추상적이다. "것(thing)"이다. 자바스크립트에서 값은 수학의 숫자, 기하학의 점이다. 프로그램이 실행될 때 프로그램은 값으로 가득차 있다. 1
, 2
, 420
같은 숫자로 된 값 외에, "소는 음매" 같은 문장도 포함되어 있다. 하지만, 모든 것이 값은 아니다. 숫자는 값이지만 if
문은 아니다. 아래에서 몇 가지 다른 값을 살펴보자.
420
같은 숫자나,"소는 음매"
같은 문자열, 객체, 그리고 몇 가지 다른 타입이 존재한다. typeof
를 값의 앞에 두면 값의 타입에 대해 알 수 있다. 예를 들어, console.log(typeof 2)
를 실행한다면 "number"
가 출력될 것이다.2
를 쓸 때마다 항상 같은 2
값을 얻을 것이다. 당신의 프로그램에서 새로운 2
를 "생성"할 수 없고, 2
가 3
이 "되는" 것은 불가능하다. 문자열 또한 동일하다.null
과 undefined
: 두 가지 특별한 값이 있다. 이 두 값은 많은 것을 할 수 없게 만들며, 종종 에러의 원인이 된다. 일반적으로 null
은 의도적으로 값이 없는 것을 의미하며, undefined
는 의도하지 않게 값이 없는 것을 의미한다. 그러나, 언제 어떻게 사용될지는 프로그래머에게 맡겨진다. 두 값은 종종 누락된 값을 처리하는 것보다 연산이 실패하는 것이 괜찮기 때문에 존재한다.같음(Equality): "값" 처럼, "같음"은 자바스크립트의 기본 개념이다. 두 값이 같다고 말한다면(나는 절대 그렇게 말하지 않겠지만), 같은 값(value)이라는 것을 의미한다. 서로 다른 두 값(value)은 존재할 수 없다. 하나일 뿐이다! 예를 들어, "소는 음매"==="소는 음매"
나 2===2
인 것은 2
는 2
이기 때문이다. JavaScript는 이 개념을 나타내기 위해 등호 세개를 사용한다.
==
)를 사용한다. 보기에는 비슷해 보이지만 다른 값을 참조하더라도 느슨하게 동등한 것으로 간주할 수 있다.(2와 "2"처럼) 일찍이 자바스크립트에 편의를 위해 추가되었지만, 이후로 끊임없는 혼란을 불러왔다. 이 개념은 기본 개념은 아니지만, 일반적인 실수의 원인이 된다. 난관에 부딪혔을 때 상황을 해결해 줄 수 있지만, 일반적으로는 사용을 지양한다.2
는 숫자 리터럴이고 "바나나"
는 문자열 리터럴이다.변수: 변수를 통해 이름을 사용해 값에 참조할 수 있다. 예를 들어, let message = "소는 음매"
로 작성하면 코드상에서 반복해서 문장을 적지 않고 message
로 작성할 수 있게 된다. 또한, 이후에 message = "난 바다코끼리다"
처럼 message
가 다른 값을 가리키도록 바꿀 수 있다. 이것이 의미하는 것은 값 자체를 바꾸는 것이 아니다. "와이어"처럼 message
가 어디 있는지 가리키는 위치만 변경된다. 변수는 "소는 음매"를 가리키다, 지금은 "나는 바다코끼리다"를 가리키고 있다.
message
변수를 사용할 수 있다면 별로일 것이다. 변수를 정의하면, 프로그램의 한 부분에서 사용할 수 있게 된다. 스코프가 어떻게 동작하는지 작동 방식에 대한 규칙이 있지만, 일반적으로 변수를 정의한 위치 근처의 {
와 }
중괄호를 확인하면 찾을 수 있다. 이 코드 "블록"이 변수의 스코프다.message = "나는 바다코끼리다"
로 작성할 때, message
변수가 가리키는 값이 "나는 바다코끼리다"
로 바뀐다. 이것을 할당, 쓰기, 혹은 변수 설정이라 한다.let
vs const
vs var
: 보통은 let
을 사용하고 싶을 것이다. 만약 변수에 값 할당을 금지하려면 const
를 사용하면 된다.(몇몇 코드베이스와 동료들은 한 번만 할당 할 경우 const
를 사용하는 약속을 만들기도 한다.) 가능하다면 var
사용은 피하라. var
는 스코프를 더럽힌다.객체: 객체는 자바스크립트에서 특별한 종류의 값이다. 객체의 멋진 점은 다른 값들과 연결을 가질 수 있다는 점이다. 예를 들어 {flavor: "vanilla"}
객체는 "vanilla"
라는 값을 가리키는 flavor
속성이 존재한다. 객체를 "와이어"로 "자신의" 값을 갖는 값으로 생각하라.
flavor
같은 이름을 갖고 "vanilla"
라는 값을 가리키는 것이), 변수와 다르게 요소는 코드(스코프)가 아닌 객체 내에 "존재"하게 된다. 요소는 객체의 일부로 간주하지만, 가리키는 값은 아니다.{}
나 {flavor: "vanilla"}
처럼 문자 그대로 작성해서 객체를 만드는 방법이다. {}
안에는 쉼표로 구분된 여러개의 {proverty: value}
쌍을 가질 수 있다. 이를 통해 객체의 속성이 어디에 "와이어"되는지 할당할 수 있다.2
는 2
와 같다(다시 말하면, 2 === 2
)고 했다. 왜냐하면 2
를 작성하는 어디에서나 항상 같은 값이 "소환"되기 때문이다. 하지만 {}
를 작성한다면, 항상 다른 값이 될 것이다! 따라서 {}
는 다른 {}
와 다르다. 콘솔 창에 {} === {}
를 실행해봐라.(false가 반환될 것이다) 컴퓨터가 코드에서 2
를 만나면 항상 같은 2
를 반환할 것이다. 하지만, 객체 리터럴은 다르다. 컴퓨터가 {}
를 만나면 항상 새로운 객체를 생성한다. 그럼 객체 항등(identity)은 무엇일까? 그것은 여전히 동일하거나, 값의 동일성을 나타내는 또 다른 용어이다. 우리가 "a
와 b
가 항등이다"라고 말하는 것은, "a
와 b
가 같은 값을 가리키고 있다"는 것을 의미한다.(a === b
) 만약 "a
와 b
가 항등이 아니다"는 것은 "a
와 b
가 다른 값을 가리킨다"(a !== b
)는 것을 의미한다..
) 표기법을 사용할 수 있다. 예를 들어 만약 변수 iceCream
의 flavor
요소가 가리키는 값이 "chocolate"
이라면, iceCream.flavor
로 작성할 경우 "chocolate"
가 반환된다.iceCream.flavor
에 접근하고 싶을 수도 있고 iceCream.taste
에 접근하고 싶을 수도 있다. 대괄호([]
) 표기법은 변수를 이용해서 요소에 접근이 가능하게 한다. 예를 들어 let ourProperty = 'flavor'
라고 선언해보자. iceCream[ourProperty]
로 작성하면 "chocolate"를 반환한다. 신기하게도 { [ourProperty]: "vanilla" }
처럼 객체를 생성할 때도 사용할 수 있다.let iceCream = {flavor: "vanilla}
라고 선언했을 때, iceCream.flavor = "chocolate"
로 작성해 값을 변이 할 수 있다.비록 const
로 iceCream
을 선언하더라도 iceCream.flavor
를 통해 변이할 수 있다. 왜냐하면 const
는 iceCream
변수 그 자체만의 할당만을 막을 수 있기 때문에, 객체의 요소(flavor
)가 가리키는 값을 바꿀 수 있다. 이런 오해의 소지 때문에 몇몇 사람들은 const
를 맹목적으로 잘못 사용하기도 한다.["banana", "chocolate", "vanilla"]
로 작성한다면, 본질적으로 0
요소가 "banana"
에, 1
요소가 "chocolate"
를, 2
요소가 "vanilla"
를 가리키는 객체를 생성한다. {0: ..., 1: ..., 2: ...}
처럼 작성하는 것은 매우 귀찮을 것이다. 또한 map
, filter
, reduce
처럼 배열을 다루는 여러가지 메서드또한 제공된다. reduce
가 혼란스럽다고 절망하지 마라 - 다른 사람들도 마찬가지로 혼란스러워하는 개념이다.iceCream.taste
처럼 말이다.(우리가 선언한 요소는 flavor
뿐이다.) 질문에 대한 단순한 답은 undefined
이다. 좀 더 구체적으로 설명해보면, 대부분의 자바스크립트 객체는 "프로토타입"을 갖는다. 당신은 프로토타입을 모든 객체에서 "다음에 어디를 봐야할 지" 결정하는 "숨겨진"요소로 생각할 수 있다. 따라서 iceCream
객체에 taste
라는 요소가 없다면, 자바스크립트는 객체의 프로토타입을 따라 taste
요소를 찾아 거슬러 올라갈 것이다. 그리고 "프로토타입 체인"을 모두 찾았지만 .taste
를 찾지 못한 경우에 undefined
를 반환한다. 당신은 이 메커니즘을 직접적으로 사용할 일은 별로 없겠지만, iceCream
객체에 우리가 정의하지 않은 toString
메서드가 존재하는 것을 설명할 수 있다. - 이 메서드는 프로토타입에서 왔다.함수: 함수는 프로그램에서 일부 코드를 나타내는 목적을 가진 특별한 값이다. 함수는 중복 코드를 여러번 작성하지 않으려는 경우 편리하게 쓸 수 있다. sayHi()
같은 함수를 "호출"하면 컴퓨터에서 해당 코드를 내부적으로 실행한 뒤, 프로그램의 원래 위치로 돌아간다.
sayHi("Amelie")
처럼 매개변수를 이용하면 함수로 몇 가지 정보를 전달할 수 있다. 함수 내에서 매개변수는 변수처럼 동작한다. 이 값들은 "인자" 혹은 "매개변수"로 불린다.(함수를 정의하는 부분인지 호출하는 부분인지에 따라). 그러나 엄격하게 구분할 필요는 없고, 두 용어는 같이 사용될 수 있다.let message = "나는 바다코끼리다"
형태로 지정했었다. let sayHi = function { }
처럼 함수를 변수에 할당하는 것 또한 가능하다. =
이후에 오는 함수를 함수 표현식이라 한다. 함수 표현식은 코드의 한 부분을 나타내는 특별한 값(함수)을 나타내기 때문에, 이후에 원하면 호출할 수 있다.let sayHi = function() {}
처럼 매번 함수 표현식으로 선언하는 것은 귀찮은 일이다. 그래서 function sayHi() {}
같이 함수 표현식보다 짧은 형식을 사용한다. 이것을 함수 선언식이라 한다. 왼쪽에 변수 명을 지정하는 것 대신 function
키워드 뒤에 지정한다. 함수 표현식과 함수 선언식은 대부분 호환된다.let
이나 const
로 먼저 선언이 되어있어야 뒤에서 사용을 할 수 있다. 하지만 이런 특징 때문에 함수가 서로를 호출할 때, 어떤 함수가 다른 함수를 사용할 때, 어떤 함수가 우선적으로 선언되어야 하는지를 추적하는 것은 매우 성가시다. 이런 부분을 편리하게 하기 위해, 함수 선언 구문을 사용할 때만, "호이스팅"되어 순서에 상관없이 사용 가능해진다. 호이스팅은 개념적으로 스코프의 가장 위로 이동된다는 것을 의미한다. 이 경우에는 함수를 호출할 때, 이미 모두 정의되어 있다.this
: this
는 아마 자바스크립트 개념 중 가장 오해받고 있는 함수의 특별한 인수다. 함수를 사용할 때 직접 this를 넘기지 않는다. 대신, 함수 호출 방식에 따라 자바스크립트 자체가 this를 넘겨줄 것이다. 예를 들어, 점 표기법 .
을 사용하여 호출할 경우(iceCream.eat()
처럼) this
는 .
앞에 있는 값일 것이다. (예시에서는 iceCream
) this
는 함수가 어디서 정의되었는지가 아니라, 어디서 호출되었는지에 따라 달라진다. .bind
, .call
, .apply
같은 헬퍼를 이용하면 this
를 좀 더 효과적으로 제어할 수 있을 것이다.let sayHi = () => { }
같이 선언할 수 있다. 화살표 함수들은 간결하고 한 줄로 표현될 수 있다. 화살표 함수는 일반 함수보다 제한적인 기능을 갖는다. 예를 들어, 화살표 함수는 this
의 개념이 없다. 만약 화살표 함수에서 this
를 사용한다면, 가장 가까운 "일반" 함수의 this
를 사용한다. 이것은 매개변수나 함수에 존재하는 변수만 사용하는 것과 비슷하다. 실제로, 사람들이 화살표 함수를 사용하는 것은 코드상에서 둘러싸고 있는 것을 그대로 "보고" 싶을 때 사용한다는 것을 의미한다.f
를 this
값과 인수에 바인딩 하는 것은 사전 정의된 값으로 f
를 호출하는 새로운 함수를 만드는 것을 의미한다 .자바스크립트는 .bind
라는 빌트인 헬퍼가 존재하지만, 함수를 이용하지 않고 직접 바인딩을 할 수도 있다. 바인딩은 중첩된 함수가 외부 함수와 동일한 this
를 "보게"하는 일반적인 방법이었다. 하지만 이런 부분들은 화살표 함수에서 처리되므로, 바인딩은 자주 사용되지 않는다.collectLinks(url)
함수는 먼저 페이지에서 방문 가능한 링크를 수집한 뒤, 모든 페이지를 방문할 때까지 자체적으로 함수를 호출할 것이다. 재귀의 함정은 계속 자기 자신을 호출하기 때문에 끝나지 않는 코드를 작성하기 쉽다는 것이다. 이 경우 자바스크립트는 "스택 오버플로우"로 불리는 에러와 함께 멈출 것이다. 이 에러가 스택 오버플로우라 불리는 이유는 콜스택에 호출해야 하는 함수가 너무 많이 쌓여 말그대로 넘쳤을 때 발생하기 때문이다.setTimeout
은 콜백함수를 갖는데.. 시간이 지나면 이 함수를 뒤이어 실행 시켜 준다. 그러나, 콜백 함수는 특별할 것이 없다. 일반적인 함수이며, "콜백" 함수에 대해서는 어떻게 동작할지에 대한 예상만 이야기한다.자바스크립트는 이런 여러가지 개념들로 구성된다. 나는 정확한 멘탈 모델을 만들때까지 자바스크립트 지식이 매우 불안했고, 다음 세대 개발자들이 이 격차를 빠르게 따라잡기 위해 돕고 싶다.
이 각각의 주제들에 좀 더 깊게 공부하고 싶다면, 이 사이트를 살펴봐라. Just Javascript는 어떻게 자바스크립트가 동작하는지에 대한 나의 정제된 멘탈 모델이며, Maggie Appleton의 놀라운 시각적인 자료들과 함께한다. 이 글과 다르게 페이스가 느리기 때문에 모든 세부적인 내용을 살필 수 있을 것이다.
Just Javascript는 아직 매우 초기 단계이므로 가공하지 않거나, 수정중인 초안 정도의 시리즈들을 이메일로 받을 수 있다. 이 프로젝트가 흥미롭게 보인다면, 이메일로 무료 초안들을 받아봐라. 당신의 피드백에 매우 감사할 것이다. 땡큐!