Skip to content

Commit

Permalink
Update re @connec’s notes; split classes section into two sections fo…
Browse files Browse the repository at this point in the history
…r classes and working with prototypes; make breaking changes examples editable whenever possible
  • Loading branch information
GeoffreyBooth committed Feb 9, 2017
1 parent 72b623e commit e3c22f7
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 65 deletions.
184 changes: 157 additions & 27 deletions docs/v2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,10 @@
<a href="#existential-operator" class="nav-link" data-action="sidebar-nav">Existential Operator</a>
</li>
<li class="nav-item">
<a href="#classes" class="nav-link" data-action="sidebar-nav">Classes, Inheritance, and Super</a>
<a href="#classes" class="nav-link" data-action="sidebar-nav">Classes</a>
</li>
<li class="nav-item">
<a href="#prototypal-inheritance" class="nav-link" data-action="sidebar-nav">Prototypal Inheritance</a>
</li>
<li class="nav-item">
<a href="#destructuring" class="nav-link" data-action="sidebar-nav">Destructuring Assignment</a>
Expand Down Expand Up @@ -743,7 +746,7 @@ <h2>Overview</h2><p><em>CoffeeScript on the <span class="hidden-md-up">top</span
</section>
<section id="coffeescript-2">
<h2>CoffeeScript 2</h2><p>CoffeeScript 2 generates JavaScript that uses the latest ES2015+ features. It is your responsibility to ensure that your target JavaScript runtime(s) support all these features, or that you pass the output through another transpiler like <a href="http://babeljs.io/">Babel</a>, <a href="https://github.com/rollup/rollup">Rollup</a> or <a href="https://github.com/google/traceur-compiler">Traceur Compiler</a>. In general, <a href="http://node.green/">CoffeeScript 2’s output is fully supported by Node.js 7+</a>, although async functions require the <code>--harmony</code> or <code>--harmony-async-await</code> flags; and ES2015 modules require an additional transpiler. Output JavaScript intended for browsers generally requires additional transpilation.</p>
<p>If you’re looking for a single tool that takes CoffeeScript input and generates JavaScript output that runs in any JavaScript runtime, assuming you opt out of certain newer features, stick to the <a href="v1/">CoffeeScript 1.x branch</a>. CoffeeScript 2 <a href="https://github.com/jashkenas/coffeescript/wiki/%5BWIP%5D-Breaking-changes-in-CoffeeScript-2">breaks compatibility</a> with certain CoffeeScript 1.x features in order to conform with the ES2015+ specifications, and generate more idiomatic output (a CoffeeScript <code>=&gt;</code> becomes an ES <code>=&gt;</code>; a CoffeeScript <code>class</code> becomes an ES <code>class</code>; and so on).</p>
<p>If you’re looking for a single tool that takes CoffeeScript input and generates JavaScript output that runs in any JavaScript runtime, assuming you opt out of certain newer features, stick to the <a href="v1/">CoffeeScript 1.x branch</a>. CoffeeScript 2 <a href="#breaking-changes">breaks compatibility</a> with certain CoffeeScript 1.x features in order to conform with the ES2015+ specifications, and generate more idiomatic output (a CoffeeScript <code>=&gt;</code> becomes an ES <code>=&gt;</code>; a CoffeeScript <code>class</code> becomes an ES <code>class</code>; and so on).</p>

</section>
<section id="installation">
Expand Down Expand Up @@ -2037,7 +2040,7 @@ <h2>The Existential Operator</h2><p>It’s a little difficult to check for the e

</section>
<section id="classes">
<h2>Classes, Inheritance, and Super</h2><p>CoffeeScript 1 provided the <code>class</code> and <code>extends</code> keywords as syntactic sugar for working with prototypal functions. With ES2015, JavaScript has adopted those keywords; so CoffeeScript 2 compiles its <code>class</code> and <code>extends</code> keywords to ES2015 classes.</p>
<h2>Classes</h2><p>CoffeeScript 1 provided the <code>class</code> and <code>extends</code> keywords as syntactic sugar for working with prototypal functions. With ES2015, JavaScript has adopted those keywords; so CoffeeScript 2 compiles its <code>class</code> and <code>extends</code> keywords to ES2015 classes.</p>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="classes">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
Expand Down Expand Up @@ -2158,7 +2161,11 @@ <h2>Classes, Inheritance, and Super</h2><p>CoffeeScript 1 provided the <code>cla
</div>

</aside>
<p>And <code>::</code> gives you quick access to an object’s prototype:</p>
<p>Finally, class definitions are blocks of executable code, which make for interesting metaprogramming possibilities. In the context of a class definition, <code>this</code> is the class object itself; therefore, you can assign static properties by using <code>@property: value</code>.</p>

</section>
<section id="prototypal-inheritance">
<h2>Prototypal Inheritance</h2><p>In addition to supporting ES2015 classes, CoffeeScript provides a few shortcuts for working with prototypes. The <code>extends</code> operator can be used to create an inheritance chain between any pair of constructor functions, and <code>::</code> gives you quick access to an object’s prototype:</p>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="prototypes">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
Expand Down Expand Up @@ -2970,57 +2977,180 @@ <h2>Embedded JavaScript</h2><p>Hopefully, you’ll never need to use it, but if
</section>
</section>
<section id="breaking-changes">
<h2>Breaking Changes</h2><p>CoffeeScript 2 aims to output as much idiomatic ES2015+ syntax as possible with as few breaking changes from CoffeeScript 1.x as possible. Some breaking changes, unfortunately, were unavoidable.</p>
<h2>Breaking Changes from CoffeeScript 1.x to 2</h2><p>CoffeeScript 2 aims to output as much idiomatic ES2015+ syntax as possible with as few breaking changes from CoffeeScript 1.x as possible. Some breaking changes, unfortunately, were unavoidable.</p>
<h3>Function parameter default values</h3><p>Per the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">ES2015 spec regarding default parameters</a>, default values are only applied when a parameter value is missing or <code>undefined</code>. In CoffeeScript 1.x, the default value would be applied in those cases but also if the parameter value was <code>null</code>.</p>
<blockquote>
<pre><code>f = (a = 1) -> a
f(null) # Returns 1 in CoffeeScript 1.x, null in CoffeeScript 2</code></pre></blockquote>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="breaking_change_function_parameter_default_values">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
<textarea class="coffeescript-input" id="breaking_change_function_parameter_default_values-coffee">f = (a = 1) -> a

f(null) # Returns 1 in CoffeeScript 1.x, null in CoffeeScript 2
</textarea>
</div>
<div class="col-md-6 javascript-output-column">
<textarea class="javascript-output" id="breaking_change_function_parameter_default_values-js">var f;

f = function(a = 1) {
return a;
};

f(null);
</textarea>
</div>
</div>

<div class="row">
<div class="col-xs-12 text-xs-right">
<button type="button" class="btn btn-primary" data-action="run-code-example" data-example="breaking_change_function_parameter_default_values" data-run="f%28null%29"><small></small>&ensp;f(null)</button>
</div>
</div>

</aside>
<h3>Bound generator functions</h3><p>Bound generator functions, a.k.a. generator arrow functions, <a href="http://stackoverflow.com/questions/27661306/can-i-use-es6s-arrow-function-syntax-with-generators-arrow-notation">aren’t allowed in ECMAScript</a>. You can write <code>function*</code> or <code>=&gt;</code>, but not both. Therefore, CoffeeScript code like this:</p>
<blockquote>
<pre><code>f = => yield this</code></pre></blockquote>
<pre><code>f = => yield this # Throws a compiler error</code></pre></blockquote>
<p>Needs to be rewritten the old-fashioned way:</p>
<blockquote>
<pre><code>self = this
f = -> yield self</code></pre></blockquote>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="breaking_change_bound_generator_function">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
<textarea class="coffeescript-input" id="breaking_change_bound_generator_function-coffee">self = this
f = -> yield self
</textarea>
</div>
<div class="col-md-6 javascript-output-column">
<textarea class="javascript-output" id="breaking_change_bound_generator_function-js">var f, self;

self = this;

f = function*() {
return (yield self);
};
</textarea>
</div>
</div>

</aside>
<h3>Classes are compiled to ES2015 classes</h3><p>ES2015 classes and their methods have some restrictions beyond those on regular functions.</p>
<p>Class constructors can’t be invoked without <code>new</code>:</p>
<blockquote>
<pre><code>(class)() # throws a TypeError at runtime</code></pre></blockquote>
<pre><code>(class)() # Throws a TypeError at runtime</code></pre></blockquote>
<p>Derived (extended) class <code>constructor</code>s cannot use <code>this</code> before calling <code>super</code>:</p>
<blockquote>
<pre><code>class B extends A
constructor: -> this # throws a compiler error</code></pre></blockquote>
constructor: -> this # Throws a compiler error</code></pre></blockquote>
<p>Class methods can’t be used with <code>new</code> (uncommon):</p>
<blockquote>
<pre><code>class Namespace
Klass: ->
new Namespace::Klass # throws a TypeError at runtime</code></pre></blockquote>
@Klass = ->
new Namespace.Klass # Throws a TypeError at runtime</code></pre></blockquote>
<h3>Bare <code>super</code></h3><p>Due to a syntax clash with <code>super</code> with accessors, bare <code>super</code> no longer compiles to a super call forwarding all arguments.</p>
<blockquote>
<pre><code>class B extends A
foo: -> super</code></pre></blockquote>
foo: -> super # Throws a compiler error</code></pre></blockquote>
<p>Arguments can be forwarded explicitly using splats:</p>
<blockquote>
<pre><code>class B extends A
foo: -> super arguments...</code></pre></blockquote>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="breaking_change_super_with_arguments">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
<textarea class="coffeescript-input" id="breaking_change_super_with_arguments-coffee">class B extends A
foo: -> super arguments...
</textarea>
</div>
<div class="col-md-6 javascript-output-column">
<textarea class="javascript-output" id="breaking_change_super_with_arguments-js">var B;

B = class B extends A {
foo() {
return super.foo(...arguments);
}

};
</textarea>
</div>
</div>

</aside>
<p>Or if you know that the parent function doesn’t require arguments, just call <code>super()</code>:</p>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="breaking_change_super_without_arguments">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
<textarea class="coffeescript-input" id="breaking_change_super_without_arguments-coffee">class B extends A
foo: -> super()
</textarea>
</div>
<div class="col-md-6 javascript-output-column">
<textarea class="javascript-output" id="breaking_change_super_without_arguments-js">var B;

B = class B extends A {
foo() {
return super.foo();
}

};
</textarea>
</div>
</div>

</aside>
<h3><code>super</code> in non-class methods</h3><p>In CoffeeScript 1.x it is possible to use <code>super</code> in more than just class methods, such as in manually prototype-assigned functions:</p>
<blockquote>
<pre><code>A = ->
B = ->
B extends A
B.prototype.foo = -> super arguments...</code></pre></blockquote>
<p>Due to the switch to ES2015 <code>super</code>, this is no longer supported. The above case could be refactored for 2.x to:</p>
<blockquote>
<pre><code>A = ->
B.prototype.foo = -> super arguments... # Throws a compiler error</code></pre></blockquote>
<p>Due to the switch to ES2015 <code>super</code>, this is no longer supported. The above case could be refactored to:</p>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="breaking_change_super_in_non-class_methods_refactor_with_apply">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
<textarea class="coffeescript-input" id="breaking_change_super_in_non-class_methods_refactor_with_apply-coffee">A = ->
B = ->
B extends A
B.prototype.foo = -> A::foo.apply this, arguments
</textarea>
</div>
<div class="col-md-6 javascript-output-column">
<textarea class="javascript-output" id="breaking_change_super_in_non-class_methods_refactor_with_apply-js">var A, B,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); return child; },
hasProp = {}.hasOwnProperty;

A = function() {};

B = function() {};

# OR
extend(B, A);

class A
B.prototype.foo = function() {
return A.prototype.foo.apply(this, arguments);
};
</textarea>
</div>
</div>

</aside>
<p>or</p>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="breaking_change_super_in_non-class_methods_refactor_with_class">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
<textarea class="coffeescript-input" id="breaking_change_super_in_non-class_methods_refactor_with_class-coffee">class A
class B extends A
foo: -> super arguments...</code></pre></blockquote>
foo: -> super arguments...
</textarea>
</div>
<div class="col-md-6 javascript-output-column">
<textarea class="javascript-output" id="breaking_change_super_in_non-class_methods_refactor_with_class-js">var A, B;

A = class A {};

B = class B extends A {
foo() {
return super.foo(...arguments);
}

};
</textarea>
</div>
</div>

</aside>
<h3>Dynamic class keys exclude executable class scope</h3><p>Due to the hoisting required to compile to ES2015 classes, dynamic keys in class methods can’t use values from the executable class body unless the methods are assigned in prototype style.</p>
<blockquote>
<pre><code>class A
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
self = this
f = -> yield self
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
f = (a = 1) -> a

f(null) # Returns 1 in CoffeeScript 1.x, null in CoffeeScript 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
A = ->
B = ->
B extends A
B.prototype.foo = -> A::foo.apply this, arguments
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class A
class B extends A
foo: -> super arguments...
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class B extends A
foo: -> super arguments...
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class B extends A
foo: -> super()
60 changes: 30 additions & 30 deletions documentation/sections/breaking_changes.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
## Breaking Changes
## Breaking Changes from CoffeeScript 1.x to 2

CoffeeScript 2 aims to output as much idiomatic ES2015+ syntax as possible with as few breaking changes from CoffeeScript 1.x as possible. Some breaking changes, unfortunately, were unavoidable.

### Function parameter default values

Per the [ES2015 spec regarding default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters), default values are only applied when a parameter value is missing or `undefined`. In CoffeeScript 1.x, the default value would be applied in those cases but also if the parameter value was `null`.

> ```coffee
f = (a = 1) -> a
f(null) # Returns 1 in CoffeeScript 1.x, null in CoffeeScript 2
```
codeFor('breaking_change_function_parameter_default_values', 'f(null)')
```

### Bound generator functions

Bound generator functions, a.k.a. generator arrow functions, [aren’t allowed in ECMAScript](http://stackoverflow.com/questions/27661306/can-i-use-es6s-arrow-function-syntax-with-generators-arrow-notation). You can write `function*` or `=>`, but not both. Therefore, CoffeeScript code like this:

> ```coffee
f = => yield this
f = => yield this # Throws a compiler error
```
Needs to be rewritten the old-fashioned way:

> ```coffee
self = this
f = -> yield self
```
codeFor('breaking_change_bound_generator_function')
```

### Classes are compiled to ES2015 classes
Expand All @@ -33,22 +31,22 @@ ES2015 classes and their methods have some restrictions beyond those on regular
Class constructors can’t be invoked without `new`:

> ```coffee
(class)() # throws a TypeError at runtime
(class)() # Throws a TypeError at runtime
```
Derived (extended) class `constructor`s cannot use `this` before calling `super`:

> ```coffee
class B extends A
constructor: -> this # throws a compiler error
constructor: -> this # Throws a compiler error
```
Class methods can’t be used with `new` (uncommon):

> ```coffee
class Namespace
Klass: ->
new Namespace::Klass # throws a TypeError at runtime
@Klass = ->
new Namespace.Klass # Throws a TypeError at runtime
```
### Bare `super`
Expand All @@ -57,14 +55,19 @@ Due to a syntax clash with `super` with accessors, bare `super` no longer compil

> ```coffee
class B extends A
foo: -> super
foo: -> super # Throws a compiler error
```
Arguments can be forwarded explicitly using splats:

> ```coffee
class B extends A
foo: -> super arguments...
```
codeFor('breaking_change_super_with_arguments')
```

Or if you know that the parent function doesn’t require arguments, just call `super()`:

```
codeFor('breaking_change_super_without_arguments')
```

### `super` in non-class methods
Expand All @@ -75,22 +78,19 @@ In CoffeeScript 1.x it is possible to use `super` in more than just class method
A = ->
B = ->
B extends A
B.prototype.foo = -> super arguments...
B.prototype.foo = -> super arguments... # Throws a compiler error
```
Due to the switch to ES2015 `super`, this is no longer supported. The above case could be refactored for 2.x to:
Due to the switch to ES2015 `super`, this is no longer supported. The above case could be refactored to:

> ```coffee
A = ->
B = ->
B extends A
B.prototype.foo = -> A::foo.apply this, arguments
>
> # OR
>
class A
class B extends A
foo: -> super arguments...
```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_apply')
```

or

```
codeFor('breaking_change_super_in_non-class_methods_refactor_with_class')
```

### Dynamic class keys exclude executable class scope
Expand All @@ -102,4 +102,4 @@ class A
name = 'method'
"#{name}": -> # This method will be named 'undefined'
@::[name] = -> # This will work; assigns to `A.prototype.method`
```
```
Loading

0 comments on commit e3c22f7

Please sign in to comment.