Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CS2] Update docs for classes, breaking changes #4438

Merged
merged 4 commits into from
Feb 9, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
895 changes: 447 additions & 448 deletions docs/v2/browser-compiler/coffee-script.js

Large diffs are not rendered by default.

166 changes: 133 additions & 33 deletions docs/v2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,9 @@
</li>
</ul>
</li>
<li class="nav-item">
<a href="#breaking-changes" class="nav-link" data-action="sidebar-nav">Breaking Changes From 1.x</a>
</li>
<li class="nav-item">
<a href="#literate" class="nav-link" data-action="sidebar-nav">Literate CoffeeScript</a>
</li>
Expand Down Expand Up @@ -2034,9 +2037,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>JavaScript’s prototypal inheritance has always been a bit of a brain-bender, with a whole family tree of libraries that provide a cleaner syntax for classical inheritance on top of JavaScript’s prototypes: <a href="http://code.google.com/p/base2/">Base2</a>, <a href="http://prototypejs.org/">Prototype.js</a>, <a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc. The libraries provide syntactic sugar, but the built-in inheritance would be completely usable if it weren’t for a couple of small exceptions: it’s awkward to call <strong>super</strong> (the prototype object’s implementation of the current function), and it’s awkward to correctly set the prototype chain.</p>
<p>Instead of repetitively attaching functions to a prototype, CoffeeScript provides a basic <code>class</code> structure that allows you to name your class, set the superclass, assign prototypal properties, and define the constructor, in a single assignable expression.</p>
<p>Constructor functions are named, to better support helpful stack traces. In the first class in the example below, <code>this.constructor.name is &quot;Animal&quot;</code>.</p>
<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>
<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 @@ -2077,35 +2078,21 @@ <h2>Classes, Inheritance, and Super</h2><p>JavaScript’s prototypal inheritance

};

Snake = (function(superClass) {
class Snake extends superClass {
move() {
alert("Slithering...");
return Snake.__super__.move.call(this, 5);
}

};

Snake.__super__ = superClass.prototype;

return Snake;

})(Animal);

Horse = (function(superClass) {
class Horse extends superClass {
move() {
alert("Galloping...");
return Horse.__super__.move.call(this, 45);
}

};
Snake = class Snake extends Animal {
move() {
alert("Slithering...");
return super.move(5);
}

Horse.__super__ = superClass.prototype;
};

return Horse;
Horse = class Horse extends Animal {
move() {
alert("Galloping...");
return super.move(45);
}

})(Animal);
};

sam = new Snake("Sammy the Python");

Expand All @@ -2125,7 +2112,53 @@ <h2>Classes, Inheritance, and Super</h2><p>JavaScript’s prototypal inheritance
</div>

</aside>
<p>If structuring your prototypes classically isn’t your cup of tea, CoffeeScript provides a couple of lower-level conveniences. The <code>extends</code> operator helps with proper prototype setup, and can be used to create an inheritance chain between any pair of constructor functions; <code>::</code> gives you quick access to an object’s prototype; and <code>super()</code> is converted into a call against the immediate ancestor’s method of the same name.</p>
<p>Static methods can be defined using <code>@</code> before the method name:</p>
<aside class="code-example container-fluid bg-ribbed-dark" data-example="static">
<div class="row">
<div class="col-md-6 coffeescript-input-column">
<textarea class="coffeescript-input" id="static-coffee">class Teenager
@say: (speech) ->
words = speech.split ' '
fillers = ['uh', 'um', 'like', 'actually', 'so', 'maybe']
output = []
for word, index in words
output.push word
output.push fillers[Math.floor(Math.random() * fillers.length)] unless index is words.length - 1
output.join ', '
</textarea>
</div>
<div class="col-md-6 javascript-output-column">
<textarea class="javascript-output" id="static-js">var Teenager;

Teenager = class Teenager {
static say(speech) {
var fillers, i, index, len, output, word, words;
words = speech.split(' ');
fillers = ['uh', 'um', 'like', 'actually', 'so', 'maybe'];
output = [];
for (index = i = 0, len = words.length; i < len; index = ++i) {
word = words[index];
output.push(word);
if (index !== words.length - 1) {
output.push(fillers[Math.floor(Math.random() * fillers.length)]);
}
}
return output.join(', ');
}

};
</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="static" data-run="Teenager.say%28%22Are%20we%20there%20yet%3F%22%29"><small>▶</small>&ensp;Teenager.say(&quot;Are we there yet?&quot;)</button>
</div>
</div>

</aside>
<p>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 All @@ -2148,8 +2181,6 @@ <h2>Classes, Inheritance, and Super</h2><p>JavaScript’s prototypal inheritance
</div>

</aside>
<p>Finally, class definitions are blocks of executable code, which make for interesting metaprogramming possibilities. Because in the context of a class definition, <code>this</code> is the class object itself (the constructor function), you can assign static properties by using
<code>@property: value</code>, and call functions defined in parent classes: <code>@attr &#39;title&#39;, type: &#39;text&#39;</code></p>

</section>
<section id="destructuring">
Expand Down Expand Up @@ -2938,6 +2969,66 @@ <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>
<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>
<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>
<p>Needs to be rewritten the old-fashioned way:</p>
<blockquote>
<pre><code>self = this
f = -> yield self</code></pre></blockquote>
<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>
<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>
<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>
<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>
<p>Arguments can be forwarded explicitly using splats:</p>
<blockquote>
<pre><code>class B extends A
foo: -> super arguments...</code></pre></blockquote>
<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 = ->
B extends A
B.prototype.foo = -> A::foo.apply this, arguments

# OR

class A
class B extends A
foo: -> super arguments...</code></pre></blockquote>
<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
name = 'method'
"#{name}": -> # This method will be named 'undefined'
@::[name] = -> # This will work; assigns to `A.prototype.method`</code></pre></blockquote>

</section>
<section id="literate">
<h2>Literate CoffeeScript</h2><p>Besides being used as an ordinary programming language, CoffeeScript may also be written in “literate” mode. If you name your file with a <code>.litcoffee</code> extension, you can write it as a Markdown document — a document that also happens to be executable CoffeeScript code. The compiler will treat any indented blocks (Markdown’s way of indicating source code) as code, and ignore the rest as comments.</p>
<p>Just for kicks, a little bit of the compiler is currently implemented in this fashion: See it <a href="https://gist.github.com/jashkenas/3fc3c1a8b1009c00d9df">as a document</a>, <a href="https://raw.github.com/jashkenas/coffeescript/master/src/scope.litcoffee">raw</a>, and <a href="http://cl.ly/LxEu">properly highlighted in a text editor</a>.</p>
Expand Down Expand Up @@ -3070,7 +3161,16 @@ <h2>Annotated Source</h2><p>You can browse the CoffeeScript 2.0.0-alpha source i
</section>
</section>
<section id="changelog">
<h2>Change Log</h2><div class="anchor" id="1.12.2"></div>
<h2>Change Log</h2><div class="anchor" id="1.12.3"></div>
<h2 class="header">
<a href="https://github.com/jashkenas/coffeescript/compare/1.12.2...1.12.3">1.12.3</a>
<span class="timestamp"> &mdash; <time datetime="2017-01-24">January 24, 2017</time></span>
</h2><ul>
<li><code>@</code> values can now be used as indices in <code>for</code> expressions. This loosens the compilation of <code>for</code> expressions to allow the index variable to be an <code>@</code> value, e.g. <code>do @visit for @node, @index in nodes</code>. Within <code>@visit</code>, the index of the current node (<code>@node</code>) would be available as <code>@index</code>.</li>
<li>CoffeeScript’s patched <code>Error.prepareStackTrace</code> has been restored, with some revisions that should prevent the erroneous exceptions that were making life difficult for some downstream projects. This fixes the incorrect line numbers in stack traces since 1.12.2.</li>
<li>The <code>//=</code> operator’s output now wraps parentheses around the right operand, like the other assignment operators.</li>
</ul>
<div class="anchor" id="1.12.2"></div>
<h2 class="header">
<a href="https://github.com/jashkenas/coffeescript/compare/1.12.1...1.12.2">1.12.2</a>
<span class="timestamp"> &mdash; <time datetime="2016-12-16">December 16, 2016</time></span>
Expand Down
9 changes: 9 additions & 0 deletions documentation/examples/static.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Teenager
@say: (speech) ->
words = speech.split ' '
fillers = ['uh', 'um', 'like', 'actually', 'so', 'maybe']
output = []
for word, index in words
output.push word
output.push fillers[Math.floor(Math.random() * fillers.length)] unless index is words.length - 1
output.join ', '
105 changes: 105 additions & 0 deletions documentation/sections/breaking_changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
## Breaking Changes

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
```

### 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
```

Needs to be rewritten the old-fashioned way:

> ```coffee
self = this
f = -> yield self
```

### Classes are compiled to ES2015 classes

ES2015 classes and their methods have some restrictions beyond those on regular functions.

Class constructors can’t be invoked without `new`:

> ```coffee
(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
```

Class methods can’t be used with `new` (uncommon):

> ```coffee
class Namespace
Klass: ->
new Namespace::Klass # throws a TypeError at runtime
```

### Bare `super`

Due to a syntax clash with `super` with accessors, bare `super` no longer compiles to a super call forwarding all arguments.

> ```coffee
class B extends A
foo: -> super
```

Arguments can be forwarded explicitly using splats:

> ```coffee
class B extends A
foo: -> super arguments...
```

### `super` in non-class methods

In CoffeeScript 1.x it is possible to use `super` in more than just class methods, such as in manually prototype-assigned functions:

> ```coffee
A = ->
B = ->
B extends A
B.prototype.foo = -> super arguments...
```

Due to the switch to ES2015 `super`, this is no longer supported. The above case could be refactored for 2.x 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...
```

### Dynamic class keys exclude executable class scope

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.

> ```coffee
class A
name = 'method'
"#{name}": -> # This method will be named 'undefined'
@::[name] = -> # This will work; assigns to `A.prototype.method`
```
15 changes: 7 additions & 8 deletions documentation/sections/classes.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
## Classes, Inheritance, and Super

JavaScript’s prototypal inheritance has always been a bit of a brain-bender, with a whole family tree of libraries that provide a cleaner syntax for classical inheritance on top of JavaScript’s prototypes: [Base2](http://code.google.com/p/base2/), [Prototype.js](http://prototypejs.org/), [JS.Class](http://jsclass.jcoglan.com/), etc. The libraries provide syntactic sugar, but the built-in inheritance would be completely usable if it weren’t for a couple of small exceptions: it’s awkward to call **super** (the prototype object’s implementation of the current function), and it’s awkward to correctly set the prototype chain.
CoffeeScript 1 provided the `class` and `extends` keywords as syntactic sugar for working with prototypal functions. With ES2015, JavaScript has adopted those keywords; so CoffeeScript 2 compiles its `class` and `extends` keywords to ES2015 classes.

Instead of repetitively attaching functions to a prototype, CoffeeScript provides a basic `class` structure that allows you to name your class, set the superclass, assign prototypal properties, and define the constructor, in a single assignable expression.
```
codeFor('classes', true)
```

Constructor functions are named, to better support helpful stack traces. In the first class in the example below, `this.constructor.name is "Animal"`.
Static methods can be defined using `@` before the method name:

```
codeFor('classes', true)
codeFor('static', 'Teenager.say("Are we there yet?")')
```

If structuring your prototypes classically isn’t your cup of tea, CoffeeScript provides a couple of lower-level conveniences. The `extends` operator helps with proper prototype setup, and can be used to create an inheritance chain between any pair of constructor functions; `::` gives you quick access to an object’s prototype; and `super()` is converted into a call against the immediate ancestor’s method of the same name.
And `::` gives you quick access to an object’s prototype:

```
codeFor('prototypes', '"one_two".dasherize()')
```

Finally, class definitions are blocks of executable code, which make for interesting metaprogramming possibilities. Because in the context of a class definition, `this` is the class object itself (the constructor function), you can assign static properties by using
`@property: value`, and call functions defined in parent classes: `@attr 'title', type: 'text'`
3 changes: 3 additions & 0 deletions documentation/v2/body.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@
<%= htmlFor('embedded') %>
</section>
</section>
<section id="breaking-changes">
<%= htmlFor('breaking_changes') %>
</section>
<section id="literate">
<%= htmlFor('literate') %>
</section>
Expand Down
3 changes: 3 additions & 0 deletions documentation/v2/sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@
</li>
</ul>
</li>
<li class="nav-item">
<a href="#breaking-changes" class="nav-link" data-action="sidebar-nav">Breaking Changes From 1.x</a>
</li>
<li class="nav-item">
<a href="#literate" class="nav-link" data-action="sidebar-nav">Literate CoffeeScript</a>
</li>
Expand Down