ES6의 새로운 기능은 무엇입니까? CoffeeScript 변환의 관점

게시 됨: 2022-03-11

저는 2년 넘게 CoffeeScript 팬이었습니다. 나는 더 생산적으로 CoffeeScript를 작성하고, 어리석은 실수를 덜 하며, 소스 맵을 지원하므로 CoffeeScript 코드를 디버깅하는 것이 완전히 고통스럽지 않습니다.

ES6의 새로운 기능은 무엇입니까? CoffeeScript 변환의 관점

최근에 Babel을 사용하여 ES6을 플레이해 왔으며 전반적으로 팬입니다. 이 기사에서 나는 CoffeeScript 변환의 관점에서 ES6에 대한 나의 발견을 편집하고 내가 좋아하는 것과 여전히 놓치고 있는 것을 볼 것입니다.

구문상 중요한 들여쓰기: 이점 또는 해로움?

나는 항상 CoffeeScript의 구문상 중요한 들여쓰기와 약간의 애증의 관계를 가지고 있습니다. 모든 것이 잘되면 "Look ma, no hands!"라는 메시지가 나타납니다. 그러한 초소형 구문을 사용하는 것을 자랑스럽게 생각합니다. 그러나 일이 잘못되고 잘못된 들여쓰기로 인해 실제 버그가 발생하면(저를 믿으세요. 그런 일이 발생합니다), 이점이 가치 있는 것처럼 느껴지지 않습니다.

 if iWriteCoffeeScript if iAmNotCareful badThings = 'happen'

일반적으로 이러한 버그는 몇 개의 중첩 문이 있는 코드 블록이 있고 실수로 한 문을 잘못 들여쓸 때 발생합니다. 예, 일반적으로 피로한 눈으로 인해 발생하지만 내 생각에는 CoffeeScript에서 발생할 가능성이 훨씬 더 높습니다.

내가 ES6을 쓰기 시작했을 때 내 직감이 어떤 반응을 보일지 확신할 수 없었습니다. 중괄호를 다시 사용하기 시작하는 것이 실제로 정말 기분이 좋았습니다. 최신 편집기를 사용하는 것도 도움이 됩니다. 일반적으로 중괄호를 열기만 하면 편집기가 친절하게 닫을 수 있기 때문입니다. CoffeeScript의 부두교 마법이 끝난 후 고요하고 맑은 우주로 돌아간 기분이었습니다.

 if (iWriteES6) { if (iWriteNestedStatements) { let badThings = 'are less likely to happen' } }

물론 나는 세미콜론을 버려야 한다고 주장한다. 필요없으면 버리라고 합니다. 나는 그것들이 추하다고 생각하고 그것은 추가 타이핑입니다.

수업 지원

ES6의 클래스 지원은 환상적이며 CoffeeScript에서 이동하는 경우 집에 있는 것처럼 느낄 것입니다. 간단한 예를 들어 둘 사이의 구문을 비교해 보겠습니다.

ES6 클래스

 class Animal { constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs } toString() { return `I am an animal with ${this.numberOfLegs} legs` } } class Monkey extends Animal { constructor(bananas) { super(2) this.bananas = bananas } toString() { let superString = super.toString() .replace(/an animal/, 'a monkey') return `${superString} and ${this.bananas} bananas` } }

CoffeeScript 클래스

 class Animal constructor: (@numberOfLegs) -> toString: -> "I am an animal with #{@numberOfLegs} legs" class Monkey extends Animal constructor: (@numberOfBananas) -> super(2) toString: -> superString = super.toString() .replace(/an animal/, 'a monkey') "#{superString} and #{@numberOfLegs} bananas"

첫인상

가장 먼저 알 수 있는 것은 ES6이 CoffeeScript보다 훨씬 더 장황하다는 것입니다. CoffeeScript에서 매우 편리한 단축키 중 하나는 생성자에서 인스턴스 변수의 자동 할당을 지원하는 것입니다.

 constructor: (@numberOfLegs) ->

이것은 ES6의 다음과 동일합니다.

 constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs }

물론 이것이 세상의 끝이 아니며, 이보다 명확한 구문이 더 읽기 쉽습니다. 누락된 또 다른 작은 점은 this 에 대한 @ 바로 가기가 없다는 것입니다. 이것은 항상 Ruby 배경에서 좋은 느낌을 주지만 다시 한 번 엄청난 거래 차단기는 아닙니다. 일반적으로 우리는 여기에서 매우 편안하다고 느끼며 실제로 메서드를 정의하는 데 ES6 구문을 선호합니다.

얼마나 슈퍼!

ES6에서 super 가 처리되는 방식에 작은 문제가 있습니다. CoffeeScript는 Ruby 방식으로 super 를 처리하지만("같은 이름의 슈퍼클래스 메서드로 메시지를 보내주세요"), ES6에서 "네이키드" 슈퍼를 사용할 수 있는 유일한 시간은 생성자입니다. ES6은 super 가 수퍼클래스 인스턴스에 대한 참조인 보다 일반적인 Java와 유사한 접근 방식을 따릅니다.

내가 돌아갑니다

CoffeeScript에서 암시적 반환을 사용하여 다음과 같은 아름다운 코드를 작성할 수 있습니다.

 foo = -> if Math.random() > 0.5 if Math.random() > 0.5 if Math.random() > 0.5 "foo"

여기서 foo() 를 호출할 때 "foo"를 다시 받을 가능성이 매우 적습니다. ES6은 이것 중 어느 것도 가지고 있지 않으며 우리가 return 키워드를 사용하여 반환하도록 강요합니다:

 const foo = () => { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { return "foo" } } } }

위의 예에서 볼 수 있듯이 이것은 일반적으로 좋은 일이지만 여전히 추가하는 것을 잊고 모든 곳에서 예기치 않은 정의되지 않은 반환을 받고 있습니다! Arrow Functions와 관련하여 다음과 같이 하나의 라이너에 대해 암시적 반환을 얻는 예외가 하나 있습니다.

 array.map( x => x * 10 )

이것은 일종의 편리하지만 중괄호를 추가하면 return 이 필요하기 때문에 약간 혼란스러울 수 있습니다.

 array.map( x => { return x * 10 })

그러나 여전히 의미가 있습니다. 그 이유는 중괄호를 추가한 경우 여러 줄을 사용하고 싶기 때문이며 여러 줄이 있는 경우 반환하는 내용을 명확하게 이해하는 것이 좋습니다.

ES6 클래스 구문 보너스

우리는 CoffeeScript가 클래스를 정의할 때 약간의 문법적 트릭을 가지고 있다는 것을 보았지만 ES6에도 몇 가지 고유한 트릭이 있습니다.

게터와 세터

ES6은 다음 예제와 같이 getter 및 setter를 통한 캡슐화를 강력하게 지원합니다.

 class BananaStore { constructor() { this._bananas = [] } populate() { // fetch our bananas from the backend here } get bananas() { return this._bananas.filter( banana => banana.isRipe ) } set bananas(bananas) { if (bananas.length > 100) { throw `Wow ${bananas.length} is a lot of bananas!` } this._bananas = bananas } }

getter 덕분에 bananaStore.bananas 에 액세스하면 잘 익은 바나나만 반환됩니다. 이것은 우리가 bananaStore.getBananas() 와 같은 getter 메소드를 통해 구현해야 하는 CoffeeScript에서와 같이 정말 훌륭합니다. JavaScript에서 속성에 직접 액세스하는 데 익숙할 때 이것은 전혀 자연스럽게 느껴지지 않습니다. 또한 각 속성에 대해 액세스 방법을 생각해야 하기 때문에 개발을 혼란스럽게 만듭니다. .bananas 또는 getBananas() 입니까?

setter는 위의 장난감 예제에서 볼 수 있듯이 데이터 세트를 정리하거나 잘못된 데이터가 설정된 경우 예외를 throw할 수 있으므로 똑같이 유용합니다. 이것은 Ruby 배경을 가진 누구에게나 매우 친숙할 것입니다.

나는 개인적으로 이것이 모든 것에 대해 getter와 setter를 사용하여 미쳐야 한다는 것을 의미한다고 생각하지 않습니다. getter 및 setter를 통해 인스턴스 변수를 캡슐화하여 무언가를 얻을 수 있다면(위의 예와 같이) 계속 진행하십시오. 그러나 isEditable 과 같은 부울 플래그가 있다고 상상해보십시오. isEditable 속성을 직접 노출하여 아무 것도 잃지 않는다면 가장 간단한 방법이므로 이것이 최선의 방법이라고 주장합니다.

정적 메서드

ES6은 정적 메서드를 지원하므로 이 못된 트릭을 수행할 수 있습니다.

 class Foo { static new() { return new this } }

이제 new Foo() 를 작성하는 것이 싫다면 이제 Foo.new() 를 작성할 수 있습니다. 순수주의자들은 new 가 예약어이기 때문에 투덜거릴 것입니다. 그러나 매우 빠른 테스트 후에 Node.js와 최신 브라우저에서는 잘 작동하는 것 같습니다. 그러나 프로덕션에서 이것을 사용하고 싶지는 않을 것입니다!

불행히도 ES6에는 정적 속성에 대한 지원이 없기 때문에 클래스 상수를 정의하고 정적 메서드에서 액세스하려면 다음과 같이 해야 합니다.

 class Foo { static imgPath() { return `${this.ROOT_PATH}/img` } } Foo.ROOT_PATH = '/foo'

선언적 인스턴스 및 클래스 속성을 구현하는 ES7 제안이 있으므로 다음과 같이 할 수 있습니다.

 class Foo { static ROOT_PATH = '/foo' static imgPath() { return `${this.ROOT_PATH}/img` } }

이것은 이미 CoffeeScript에서 매우 우아하게 수행할 수 있습니다.

 class Foo @ROOT_PATH: '/foo' @imgPath: -> @ROOT_PATH

문자열 보간

마침내 우리가 자바스크립트에서 문자열 보간을 할 수 있는 때가 왔습니다!

 `I am so happy that in the year ${new Date().getFullYear()} we can interpolate strings`

CoffeeScript/Ruby에서 가져온 이 구문은 약간 유치한 느낌이 듭니다. 아마도 백틱이 시스템 명령을 실행하는 데 사용되는 내 Ruby 배경 때문에 처음에는 상당히 잘못된 느낌이 듭니다. 또한 달러 기호를 사용하는 것이 80년대처럼 느껴지지만 아마도 저만 그런 것 같습니다.

이전 버전과의 호환성 문제로 인해 CoffeeScript 스타일 문자열 보간을 구현하는 것이 불가능했다고 생각합니다. "What a #{expletive} shame" .

화살표 함수

화살표 기능은 CoffeeScripter에서 당연한 것으로 간주됩니다. ES6은 CoffeeScript의 뚱뚱한 화살표 구문을 거의 구현했습니다. 그래서 우리는 멋진 짧은 구문을 얻었고 어휘 범위가 지정된 this 를 얻었으므로 ES5를 사용할 때 다음과 같이 후프를 건너뛸 필요가 없습니다.

 var that = this doSomethingAsync().then( function(res) { that.foo(res) })

ES6에 해당하는 것은 다음과 같습니다.

 doSomethingAsync().then( res => { this.foo(res) })

내가 할 수 있기 때문에 단항 콜백 주위에 괄호를 어떻게 생략했는지 주목하세요!

스테로이드에 대한 객체 리터럴

ES6의 객체 리터럴에는 몇 가지 심각한 성능 향상이 있습니다. 그들은 요즘 모든 종류의 일을 할 수 있습니다!

동적 속성 이름

나는 실제로 이 기사를 쓰기 전까지 CoffeeScript 1.9.1 이후로 우리가 이것을 할 수 있다는 것을 깨닫지 못했습니다:

 dynamicProperty = 'foo' obj = {"#{dynamicProperty}": 'bar'}

이전에 필요했던 이와 같은 것보다 훨씬 덜 고통스럽습니다.

 dynamicProperty = 'foo' obj = {} obj[dynamicProperty] = 'bar'

ES6에는 꽤 훌륭하지만 큰 승리는 아닌 대체 구문이 있습니다.

 let dynamicProperty = 'foo' let obj = { [dynamicProperty]: 'bar' }

펑키 단축키

이것은 실제로 CoffeeScript에서 가져온 것이지만 지금까지 내가 무지했던 것입니다. 어쨌든 매우 유용합니다.

 let foo = 'foo' let bar = 'bar' let obj = { foo, bar }

이제 obj 의 내용은 { foo: 'foo', bar: 'bar' } 입니다. 그것은 매우 유용하고 기억할 가치가 있습니다.

학급에서 할 수 있는 모든 것 나도 할 수 있다!

객체 리터럴은 이제 다음과 같이 클래스가 할 수 있는 거의 모든 것을 할 수 있습니다.

 let obj = { _foo: 'foo', get foo() { return this._foo }, set foo(str) { this._foo = str }, isFoo() { return this.foo === 'foo' } }

왜 그 일을 시작하고 싶은지 잘 모르겠지만 이제 할 수 있습니다! 물론 정적 메서드를 정의할 수는 없습니다. 전혀 의미가 없기 때문입니다.

당신을 혼란스럽게 하는 For 루프

ES6 for-loop 구문은 숙련된 CoffeeScripter를 위한 몇 가지 멋진 근육 메모리 버그를 도입할 것입니다. ES6의 for-of는 CoffeeSCript의 for-in으로 변환되고 그 반대도 마찬가지입니다.

ES6

 for (let i of [1, 2, 3]) { console.log(i) } // 1 // 2 // 3

커피스크립트

 for i of [1, 2, 3] console.log(i) # 0 # 1 # 2

ES6으로 전환해야 하나요?

저는 현재 100% CoffeeScript를 사용하여 상당히 큰 Node.js 프로젝트에서 작업하고 있으며 여전히 매우 만족하고 매우 생산적입니다. ES6에서 내가 정말로 부러워하는 것은 getter와 setter뿐입니다.

또한 오늘날 실제로 ES6을 사용하는 것은 여전히 ​​약간 고통스럽습니다. 최신 Node.js 버전을 사용할 수 있다면 거의 모든 ES6 기능을 사용할 수 있지만 이전 버전과 브라우저의 경우 여전히 장미빛이 덜합니다. 예, Babel을 사용할 수 있지만 물론 이는 Babel을 스택에 통합하는 것을 의미합니다.

나는 ES6이 내년 또는 2년 안에 많은 기반을 확보하는 것을 볼 수 있으며 ES7에서 더 큰 것을 볼 수 있기를 바랍니다.

내가 ES6에 대해 정말 만족하는 것은 "평범한 오래된 JavaScript"가 CoffeeScript만큼 친숙하고 강력하다는 것입니다. 이것은 프리랜서인 저에게 아주 좋습니다. 과거에는 JavaScript 프로젝트 작업을 조금 꺼려했지만 ES6에서는 모든 것이 조금 더 빛나 보입니다.