레이블이 ECMAScript 6인 게시물을 표시합니다. 모든 게시물 표시
레이블이 ECMAScript 6인 게시물을 표시합니다. 모든 게시물 표시

2018-02-09

[CoffeeScript] 왜 커피스크립트를 사용하지 않는가

 아랫글은 2016년에 썼던 글인데 왜인지 모르게 아직 publish 안 하고 있었다.
 그 사이에 ES2015(ES6)의 변경을 추가 한 커피스크립트2가 나왔다. 하지만 이미 ES2015를 넘어 ES2017도 나왔고, 브라우저들도 ES2016는 네이티브로 지원하고 있기 때문에 앞으로도 커피스크립트를 쓸 일은 없을 것 같아 발견한 김에 publish 한다.


 커피스크립트는 자바스크립트 코드를 간결하게 만드는 것을 목표로 만들어진 언어다. 2009년 첫 버젼을 릴리즈 하였고, 2010년 12월 1.0이 릴리즈 되었다.
 내가 커피스크립트를 처음 썼던 것은 1.0이 릴리즈 된 지 조금 뒤인 2011년 경이었던 것 같다. 지금도 자바스크립트 코드가 다른 언어에 비해 간결하지는 않지만, 당시 자바스크립트 코드는 지금보다도 verbose 하였기 때문에 꽤 애용하였었다. 그러다가 웹 말고 다른 일을 하다 보니 자바스크립트를 사용하지 않게 되었고 자연스럽게 커피스크립트도 안 쓰게 되었다. 그러다가 2014년경 잠시 웹 개발을 하게 되었는데 이때 습관적으로 다시 커피스크립트를 사용하였었다. 하지만 그것도 잠시였고, 그 뒤로는 사용하지 않게 되었다. 더 이상 커피스크립트를 쓰지 않게 된 이유는 크게 2가지였다.

 일단 커피스크립트의 문법은 너무 애매했다. 커피스크립트가 가장 중요하게 생각하는 요소 중 하나는 자바스크립트로 일대일로 매칭되는 것이다. 하지만 처음 커피 스크립트를 보면 자바스크립트 같은 느낌이 전혀 들지 않는다. 이는 커피스크립트 코드에서는 괄호를 거의 사용하지 않기 때문이다. 사실 일부 기능을 제외하고 대부분의 커피스크립트 코드는 적절한 위치에 괄호를 추가하는 것으로 자바스크립트 코드로 변환할 수 있다. 이는 커피스크립트의 설계자가 자바스크립트의 괄호가 자바스크립트 코드를 복잡하게 만든다고 생각했기 때문이다.
 하지만 실제로 사용해보면 이는 딱히 편하지 않다. 물론 괄호가 없기 때문에 타이핑은 많이 줄어든다. 하지만 코드를 작성해본 사람은 알겠지만 적은 타이핑은 딱히 간결한 코드를 만드는데 중요한 요소가 아니다. 게다가 이 정도 타이핑은 사실 좋은 에디터를 쓰면 해결될 문제다. 실제로 커피스크립트를 사용하면 타이핑이 줄어서 드는 이득보다 코드를 읽기 힘들어져서 생기는 문제가 더 크다. 물론 익숙해지면 어느 정도 해결되는 문제지만, 엣지 케이스에 대해서는 꾸준히 밟는 지뢰가 생긴다. 게다가 문제가 발생했을 때 컴파일 에러가 발생하는 것이 아니라 예상치 못했던 방식으로 컴파일돼서 실행되는 것이 더 큰 문제다.

 또한 당시에는 ECMAScript 6(a.k.a. ES6)를 ECMAScript 5로 변환시켜주는 babel이라는 기술이 나오고 있었다. ES6를 사용하면 커피스크립트만큼은 아니지만, 어느 정도 간결한 코드를 사용할 수 있었다. 그리고 ES6에는 이미 커피스크립트의 arrow 문법(-> 대신 =>를 사용하기는 한다)과 class가 추가됐다. 게다가 변수 선언이 암시적으로 이루어지는 커피스크립트의 특성상 ES6에 추가 된 let이나 const같은 장점을 전혀 사용할 수 없다.

 뭐 이런저런 이유로 더 이상 커피스크립트를 사용하지는 않을 것 같다. 하지만 커피스크립트의 노력이 헛되다고 생각하지는 않는다. arrow를 함수로 사용하기 시작한 것도, 자바스크립트에 class를 도입한 것도 내가 알기로는 커피스크립트가 처음이었고, 자바스크립트를 조금 더 쓰기 좋은 언어로 만드는데 커피스크립트가 기여한 바는 결코 작지 않다. 다만, 이미 커피스크립트가 해결하고자 하는 문제는 거의 다 자바스크립트에 반영됐고, 자바스크립트는 다음 문제로 넘어가고 있다. 따라서 앞으로 커피스크립트를 쓸 일은 없을 것 같다.

2016-01-10

[ECMAScript 6] class 선언하기

 JavaScript는 훌륭한 객체 지향 언어다. 하지만 프로토타입 기반 객체지향이라는 독특한 개념과 특유의 verbose한 문법 때문에 다른 언어에서 넘어온 사람들은 쉽게 적응하지 못하였고, 객체 지향스럽지 않은 코드를 작성하였다. 그럼에도 프로토타입은 다른 객체 지향 언어가 제공하는 class에 비해서 더 유연한 확장성 지원하기 때문에 많은 JavaScript 개발자들은 class가 필요 없다는 입장을 고수해왔다.
 하지만 프로토타입이 코드를 verbose 하게 만들고, 가독성을 떨어뜨린다는 주장은 꾸준히 제기되었고, 결국 ECMAScript 6에 드디어 class 키워드가 추가되어 보다 쉽게 객체 지향적 코드를 작성할 수 있게 되었다.  여기서 중요한 것은 class 키워드가 ES6에 추가되었다고 해서 클래스 기반 객체지향이라는 개념이 추가된 것은 아니라는 것이다. ES6도 여전히 프로토타입 기반의 객체 지향 언어이다. class는 프로토타입 기반 객체를 만드는 syntactic sugar일 뿐이다. 즉, 위와 같은 코드는 ES5를 기준으로 보면 아래와 같이 해석된다.1) class 스타일의 간결성과 prototype의 유연성을 동시에 갖기 위한 선택이었다.
1) 완전히 일치하는 것은 아니다. ES5에는 생성자로 쓸 수 없는 함수가 존재하지 않기 때문에 method를 완벽히 재현할 수 없다.

2016-01-09

[ECMAScript 6] property 선언하기

 property란 사용하는 코드는 member data에 접근하듯이 사용하지만 실제로는 함수가 호출되도록 하는 프로그래밍 언어의 기능을 말한다. C#, Python, Ruby 등 객체를 중요시하는 언어에는 대부분 존재하는 기능이며 당연히 기존의 JavaScript에도 있었다. 하지만 Object.defineProperties 함수를 이용해야 해서 코드가 복잡해진다는 문제가 있었다.  ES6에서는 property를 쉽게 선언할 수 있는 문법을 도입하였다. 메소드의 이름 앞에 get이나 set을 붙이는 것만으로 property를 선언할 수 있다.

2016-01-08

[ECMAScript 6] method 선언하기

 ECMAScript 5에는 메소드에 해당하는 개념이 없었다. 그저 함수가 first-class citizen이기 때문에 객체의 멤버변수로 함수를 할당하는 방식으로 메소드를 만들었다.

 ECMAScript 6에는 method를 만들기 위한 문법이 추가되어 메소드를 선언할 수 있게 되었다.

 이는 크게 보면 ES5에서 사용하던 함수를 멤버변수에 할당하는 방식과 다를 것 없다. 하지만 사소한 부분에서 약간 다르다.  메소드는 이름을 가지지만 new를 통해서 객체를 만들어낼 수 없다.

 이는 메소드만이 가지는 특징이다. 일반적인 함수는 모두 new를 통해 객체를 만들어낼 수 있다. 반면에 람다 함수는 new를 통해서 객체를 만들 수 없지만, 이름을 가지지 않는다.

2015-09-18

[ECMAScript6] 성공적인 Promise는 중첩되지 않는다.

 ES6 Promise에는 독특한 특징이 있는데, 지난번 글에서는 설명할 타이밍을 잡지 못해서 그냥 넘어갔었다. 이번에 그 특징에 관해 설명하도록 하겠다.

 전에 모나드에 관해서 설명하면서 모나드의 가장 기본적인 operator 중 하나인 bind operatorM[T] 타입의 모나드가 T 타입의 인자를 받아서 M[U] 타입의 값을 리턴하는 함수를 인자로 받아서 M[U] 타입의 모나드로 타입을 진행시킨다1)고 하였다. 하지만 ES6 Promisethen 함수에 관해서 설명하면서 then 함수가 받는 콜백이 값을 리턴하면 resolved 된 Promise가 리턴되고, 값을 throw 하면 rejected 된 Promise가 리턴된다고 하였다. 즉, then 함수만으로는 모나드를 리턴하는 함수를 통해서 타입을 전진시키는 bind operator를 구현할 수 없으므로 완전한 모나드를 구현하지 못한다. 그렇다면 ES6의 Promise는 어떻게 Promise를 전진시킬까?

 간단하다. 그냥 then 함수가 인자로 받는 콜백은 Promise를 리턴해도 된다.

 사실 Promise가 모나드라는 것을 생각하면, 이쪽이 올바른 사용 법이다. 하지만 ES6뿐 아니라 다른 모나드 구현체에서도 bind operator뿐 아니라, 모나드가 아닌 값을 리턴하는 함수. 즉, (M[T], T => U) => M[U]에 해당하는 함수도 구현한다. 이는 사실 내부적으로 unit operator와 bind operator를 호출하기 때문에 굳이 필요한 함수는 아니다. 그러함에도 이 함수가 존재하는 이유는 실제로 이 구현을 사용하는 경우가 일반적인 bind operator를 사용하는 경우보다 많아서 사용자의 편의를 위해서 제공되는 것일 뿐이다.
 그래도 보통은 둘을 같은 이름의 함수로 구현하지는 않고, 다른 이름의 함수로 구현한다. 그렇지만 ES6에서는 then 함수가 두 가지 일을 한다. 동적 타입 언어의 특징을 최대한 활용한 것이다.

 하지만 then 함수처럼 콜백 함수가 리턴하는 것이 Promise인지 아닌지에 따라서 동작이 달라지면 실수하기 쉽다. 이것을 ES6는 애초에 Promise가 중첩되지 못하도록 함으로써 동작에 일관성을 취했다. 즉, PromisePromise를 값으로 지니지 못한다.

 Promise가 중첩되지 못하게 하는 역할은 Promise.resolve 함수가 한다. resolve 함수의 스펙을 보면 resolve 함수는 받은 인자가 Promise이고 같은 생성자를 가진다면, 받은 인자 그 자체를 리턴한다고 되어 있다.2) then 함수는 내부적으로 실행된 결괏값을 이용하여 resolve 함수를 호출한 뒤 그 결괏값인 Promise를 돌려준다.


 resolve 함수는 then 함수뿐이 아니라 Promise.all 함수에서도 사용된다. 지난번에 all 함수를 설명할 때는 아직 resolve 함수에 대해서 제대로 설명하지 않아서, Promise의 iterable을 받아 하나의 Promise로 zip 한다고 하였다. 하지만 all 함수도 내부적으로 아이템들을 전부 resolve 함수에 넘기기 때문에, iterable에 Promise가 아닌 다른 값을 넣어서 호출할 수 있다.


1) 말로 풀어쓰려니 복잡한데, 기호로 표현하면 아래와 같다.
(M[T], T => M[U]) => M[U]
2) 스펙 문서 3번 스텝

2015-09-11

[ECMAScript 6] Promise - 비동기 코드 작성하기

 모든 언어가 마찬가지겠지만, 기존의 JavaScript에서는 비동기적 코드를 작성하고 관리하는 것은 크게 어려운 일이었다. node.js에서는 콜백을 이용하는 방식을 사용했지만, 이는 콜백 헬이라는 새로운 문제를 만들어냈다. 이를 해결하기 위해 step이나 async 같은 다양한 라이브러리가 나왔지만 이런 라이브러리로도 콜백 방식이 가지는 복잡도는 해결하지 못했고, 여전히 비동기 코드를 작성하는 것은 어려운 문제였다. 그래서 ECMAScript 6에서는 비동기 코드를 쉽게 작성할 수 있도록 Promise를 표준 라이브러리에 도입하였다. Promise는 그 이름에서도 알 수 있듯이 비동기적인 코드를 작성할 수 있도록 도와주는 promise monad의 일종이다.

 Promise는 기본적으로 생성자를 통해서 만들어진다.

 이렇게 생성된 Promise는 pending state가 된다. pending state는 아무 값도 가지지 않은 상태다. pending인 Promise는 후에 resolved state(혹은 fulfill state)가 되거나 rejected state가 될 수 있지만, 이 상태로는 아무것도 할 수 없다.

 Promise의 상태를 바꾸기 위해서는 콜백 함수를 이용해야 한다. Promise의 생성자는 한 개의 콜백 함수를 받는다. 이 콜백은 executor라고 불리는데, Promise 객체를 생성하는 중에 호출된다. executor가 호출될 때는 2개의 함수가 인자로 넘어간다. 첫 번째는 resolver라고 불리고, 두 번째는 rejecter라고 불린다. pending state인 Promiseresolver가 호출되면 이 Promise는 resolved state가 되고, resolver의 인자를 값으로 지닌다. 반대로 pending state인 Promiserejecter이 호출되었다면 이 Promise는 rejected state가 되고, rejecter의 인자를 Promise가 reject 된 이유로 가지게 된다. 중요한 점은 pending state인 Promise만 resolve 하거나 reject 할 수 있고, 이미 resolved 되었거나 rejected 된 Promise는 다시 resolve 하거나 reject할 수 없다.

 혹은 함수를 실행시키지 않고, 이미 결정된 값을 가지고 콜백 없이 Promise를 만드는 방법도 있다. resolvereject 함수를 이용하는 것이다. resolve 함수를 이용하면 resolved 된 Promise가 만들어지고, reject 함수를 이용하면 reject 된 Promise가 만들어진다.

 이렇게 생성된 Promise는 2개의 멤버 함수를 가진다. 우선 기본적인 멤버 함수는 then()이라는 함수다. then()은 promise monad의 map과 map_error operation을 합쳐놓은 함수이다. then()은 2개의 콜백을 받는다.


 첫 번째 받은 콜백은 Promise가 resolved일 때 호출되는 함수이고, 두 번째 콜백은 Promise가 rejected일 때 호출되는 함수이다.
 정확히는 상태가 변하면 바로 호출되는 것은 아니다. 만약 then() 함수가 호출될 때 Promise가 pending 되어 있었다면 상태가 변할 때 job queue에 추가되고, 상태가 resolved나 rejected였다면 그 즉시 job queue에 추가되었다가, job queue에서 다음 일을 꺼내질 때 실행된다.
 이때 콜백 함수는 값을 하나 인자로 받는데, 첫 번째 콜백의 경우 Promise가 resolve 되었을 때의 값을, 인자로 받고, 두 번째 콜백은 Promise가 reject 된 이유를 받는다.


 then method는 Promise를 리턴하기 때문에 체이닝 할 수도 있다.
 이때 반환되는 Promise가 가지는 값은 실행되는 콜백의 결과에 따라 달라진다. 만약 콜백 함수가 정상적으로 실행되고 값을 반환했다면, 리턴되는 Promise는 함수의 결괏값을 resolve 한 값이 리턴되는 Promise의 값이 된다. 반대로 만약 콜백이 에러를 throw 했으면 throw 한 값을 reason으로 하는 rejected state인 Promise가 리턴된다.


 하지만 then() 함수만 있으면 rejected 된 Promise에 대해서만 값을 진행시키고 싶을 때 약간 불편하다. 그래서 catch 함수가 존재한다. Promise.prototype.catch(onRejected) method는 Promise.prototype.then(undefined, onRejected)와 완전히 같은 동작을 한다. 따라서 아래의 코드의 promise1promise2는 같은 의미의 Promise가 된다.


 ECMAScript 6의 Promise는 여러 개의 Promise를 join 할 수 있는 Promise.all이라는 함수도 제공한다. all() 함수는 인자로 iterable 한 ArraySet 같은 것을 받는다. 그리고 받은 iterable의 모든 값이 resolved 되면, 그 값들이 resolve 된 값의 배열을 리턴한다. 이를 통해서 여러 개의 독립적으로 돌아가는 비동기적인 코드의 실행 결과를 하나로 모을 수 있다.


 혹은 Promise.race() 함수를 이용해서 여러 Promise 중에서 먼저 완료된 Promise를 얻어올 수도 있다.

 사실 기존에도 JavaScript를 위해 Promise를 제공하는 라이브러리는 많이 있었다. node.js가 나오기 전에 유행했던 jQuerydeferred object도 사실상 Promise monad의 일종이었고, node.js나 브라우저 양쪽에서 돌아가도록 pure JavaScript로 작성된 Qbluebird 같은 라이브러리도 존재했다. 하지만 이는 다들 독립적인 동기로 작성된 것으로 서로 간에 API가 호환되지 않아 사용법이 전부 달랐다. 하지만 이제 표준 라이브러리에 Promise가 들어왔으므로 그냥 표준 라이브러리에서 제공하는 Promise를 사용하면 된다.

2015-08-26

[ECMAScript 6] Symbol - 7번째 primitive type

  지금까지 자바스크립트에는 number, boolean, string, null, undefined, object의 6가지 타입밖에 없었다. 그래서 C의 enum 같은 타입이 필요하거나, 일종의 태깅 같은 것을 위해 고유한 값이 필요했을 경우 보통 number나 string 타입을 이용했다.

 하지만 ECMAScript 6에서는 이제 number나 string을 이용할 필요가 없다. ECMAScript 6에서는 새로운 타입인 Symbol 타입이 추가되었기 때문이다.

 Symbol 타입의 값은 Symbol 함수를 통해서만 생성할 수 있고, new를 통해서 만들 수 없다.

 Symbol 함수는 인자로 description을 받을 수도 있고, 아무 인자도 받지 않을 수도 있다. 이 인자는 실제로 생성되는 Symbol에 영향을 주지 않는다. 로깅 등을 위해서 toString 함수를 이용해 string으로 변환할 때, 반영되지만 이는 디버깅을 위해서고, 일반적으로 이 description을 이용할 일은 없다. 같은 description을 이용해 생성한 Symbol도 실제로는 다른 값을 가진다.


 이는 Symbol 타입이 unique함을 보장하기 때문이다. Symbol 타입은 immutability와 unique 함이 보장된다. number나 string도 immutability는 보장된다. 하지만 unique 함은 보장되지 않는다. 이것이 Symbol 타입과 number/string 타입과의 차이점이다.

 같은 Symbol을 가지고 오기 위해서는 생성한 Symbol을 전역 변수로 등록시키고 있어야 한다. 이것을 해주는 게 for 함수이다.

 Symbol.for() 함수는 key를 인자로 받는다. 이전에 같은 key로 생성한 Symbol이 있으면 그 Symbol을 돌려주고, 처음 받은 key면 새로운 Symbol을 생성하여, 저장한 뒤 돌려준다.

 주의해야 할 것은 Symbol.for(key) 함수는 이미 등록된 Symbol이 있을 경우만 그것을 가지고 올 수 있다는 것이다. 따라서 Symbol(description)을 이용해서 생성한 Symbol은 Symbol.for(key)를 이용해서 가지고 올 수 없다.

 이런 특징을 이용해서 아래와 같이 object의 private property를 만드는 것도 가능하다.


2015-08-25

[ECMAScript 6] block 안에서 함수 만들기

 JavaScript 함수 선언의 가장 큰 특징은 함수의 선언 위치에 상관없이 언제나 코드의 가장 위에서 함수를 선언한 것처럼 코드가 실행된다는 것이다. 따라서 아래와 같은 코드는

 사실은 아래와 같은 코드라고 보아도 된다.

 이를 function hoisting이라고 한다. 이 덕분에 함수 선언문보다 앞에서 함수를 사용할 수 있다. 하지만 함수 선언은 언제나 스코프의 가장 윗부분으로 hoisting 된다. 따라서 함수 안에서 선언된 함수는 함수 내에서 언제나 같은 함수를 의미했고, 특정 block 안에서는 다른 함수를 의미하도록 사용할 수 없었다.

 하지만 ECMAScript 6에서는 block 단위의 함수 선언을 허용한다.

 즉, 위와 같이 if block 안에서만 다른 값을 의미하도록 하는 것이 가능하다.

 하지만 아쉽게도 이는 아직 대부분 브라우저나 node.js에서는 구현되지 않았다. 따라서 블록 단위 함수 선언을 사용하려면 babel.js를 사용해야 한다.

2015-08-22

[ECMAScript 6] 함수 이름 가져오기

 자바스크립트는 두 가지 방식으로 함수를 선언할 수 있다.
 평범하게 방법은 함수를 선언하여 사용할 수도 있고, 익명함수를 만들어 사용할 수도 있다.

 ECMAScript 5까지는 어떻게 만들어지든 둘 사이에는 차이가 없었다. 만들 수 있는 위치나, 함수의 선언 및 할당이 실제로 이뤄지는 위치가 다르기는 하지만, 어쨌든 만들어지고 난 다음에 둘은 아무런 차이가 없었다.

 위와 같은 코드는 사실 아래와 같은 코드에 syntax sugar일 뿐이다.


 하지만 ECMAScript 6 이후로 둘은 name property라는 다른 점을 가진다. 첫 번째 방식으로 만든 함수는 named function이라고 불리며 name이라는 프로퍼티를 가진다. 반면에 두 번째 방식으로 만들어진 함수는 anonymous function이라고 불리며 길이가 0인 문자열("")을 name property로 가진다.

2015-08-11

[ECMAScript 6] fat arrow function

 fat arrow function(=>)는 ECMAScript 6에 추가된 익명 함수를 생성하는 새로운 방법이다. 기존의 함수를 만드는 것보다 짧게 함수를 만들 수 있다.

이는 다른 함수에 콜백으로 함수를 넘겨야 하는 경우 요긴하게 사용된다.

 하지만 단순히 길이가 짧다는 것만이 arrow function의 장점이 아니다. 오히려 중요한 특색은 arrow function은 thislexical scope에서 찾는다는 것이다.

 JavaScript 함수의 가장 큰 특징 중 하나는 this가 dynamic binding 된다는 것이다. 보통은 큰 문제가 되지 않지만, 메소드를 콜백으로 넘겨주어야 하는 경우나 메소드를 변수로 받을 경우 원하는 대로 돌아가지 않았다.


 ECMAScript 5에서는 Function에 bind 메소드가 추가되어 원하는 오브젝트를 this로 바인드 한 함수를 만들 수 있지만, 매번 이런 작업을 하는 것은 귀찮은 일이다. 이제 ECMAScript 6에서는 이런 경우에 =>을 이용하여 this가 lexical binding 된 함수를 만들면 된다.

2015-07-19

[ECMAScript 6] const - 상수 선언하기

 constlet과 같이 ECMAScript 6에 도입된 block 단위 상수 선언문이다.
 중복으로 선언할 수 없고, 선언 전에 사용할 수 없다는 것은 let과 같다. 거기에 const는 추가적인 제약이 더 붙는다.

 우선 const로 선언된 이름에는 값을 재할당할 수 없다. 이는 문법적으로 에러로 처리한다. 따라서 const에 값을 할당하는 구문을 실행할 때 발생하는 것이 아니라 const에 값을 할당하는 구문이 있는 함수가 선언될 때 에러가 발생한다.

 또한, const를 이용해서 상수를 선언할 때는 언제나 값을 초기화해주어야 한다. const로 선언된 상수에 값을 할당하지 못한다는 것을 생각하면, 당연한 일이다. 이 또한 문법 에러로, 초기화하지 않는 const를 선언할 때가 아니라, 선언하는 구문이 있는 함수를 선언할 때 에러가 발생한다.

 하지만 const도 상수 선언을 위한 완벽한 해결책은 아니다. const로 선언한 상수에는 값을 재할당할 수 없지만, 상수임에도 불구하고 값을 변경시킬 수 있기 때문이다. const로 선언한 상수는 어디까지나 값의 재할당을 막을 뿐, 그 값을 보호해주지 않는다.

 const가 완벽한 해결책인 것은 아니지만, 한계를 알고 적절하게 사용하면 좀 더 안정적이고 가독성 있는 코드를 작성할 수 있다.

2015-07-18

[ECMAScript 6] let - block 단위 스코프

 ECMAScript 6에서는 기존의 function scope였던 var 이외에 let이라는 block scope 변수 선언을 지원한다. let을 통한 변수 선언은, var를 통한 변수 선언과 다르게 hoisting 하지 않는다. 즉, 변수가 선언된 이후부터 변수가 유효하고 그전에는 해당 변수를 사용할 수 없다.


 hoisting을 없앤 것뿐 아니라 그 외의 실수하기 쉬운 부분을 에러로 처리하여 좀 더 안전한 코드를 작성할 수 있도록 하였다.

 예를 들면, ECMAScript 5에서는 hoisting 된 변수를 실수로 선언 전에 사용할 경우 그 변수는 undefined가 된다. 하지만 let을 사용하면, 변수를 hositing 하지 않을 뿐 아니라, 변수를 선언한 블록 안에서 선언 전에 해당하는 이름을 사용하는 것을 에러로 처리한다.

 또한, 이전에는 같은 스코프, 다시 말해서 같은 함수 안에서 변수의 선언문이 여러 개 있는 것이 정상적인 구문으로 처리되었다. 하지만 let을 사용한다면 같은 스코프에서 중복으로 선언하는 것이 에러로 처리된다.

 아쉬운 점은 위와 같은 에러가 함수의 선언에서 발생하는 것이 아니라, 실제로 그 구문을 실행할 때 발생한다는 것이다. 따라서 여전히 높은 커버리지의 테스트를 작성해야 안전한 코드라고 보장할 수 있다. 하지만 예전처럼 예상하지 못한 undefined가 나와서 문제가 발생한 부분이 아닌 다른 곳부터 추적해가야 할 일은 없어졌다.

 또 다른 문제는 babel에서 다르게 동작한다는 것이다. 파이어폭스나 크롬 등 모던 브라우저나 io.js는 ECMAScript 6를 지원하지만, 오래된 버전의 IE나 node.js 등에서는 아직 let을 지원하지 않는다. 그런 환경에서는 babel을 사용해야 하는데, babel에서는 중복된 선언이나, 선언 전에 사용하는 것을 에러로 처리하지 않는다.
 하지만 조만간 node.js에서도 let을 지원할 것이고, 오래된 버전의 IE도 없어지고 있으니 이런 문제는 곧 해결될 것이다.