got extends back in the language -- use it together with super

This commit is contained in:
Jeremy Ashkenas
2009-12-24 16:49:23 -08:00
parent 47812d9ea6
commit 1c83e68292
10 changed files with 62 additions and 38 deletions

View File

@@ -3,13 +3,13 @@ Animal.prototype.move: meters =>
alert(this.name + " moved " + meters + "m."). alert(this.name + " moved " + meters + "m.").
Snake: name => this.name: name. Snake: name => this.name: name.
Snake.prototype: new Animal() Snake extends new Animal()
Snake.prototype.move: => Snake.prototype.move: =>
alert("Slithering...") alert("Slithering...")
super(5). super(5).
Horse: name => this.name: name. Horse: name => this.name: name.
Horse.prototype: new Animal() Horse extends new Animal()
Horse.prototype.move: => Horse.prototype.move: =>
alert("Galloping...") alert("Galloping...")
super(45). super(45).

View File

@@ -67,7 +67,7 @@
<a href="#while">While Loops</a><br /> <a href="#while">While Loops</a><br />
<a href="#array_comprehensions">Array Comprehensions</a><br /> <a href="#array_comprehensions">Array Comprehensions</a><br />
<a href="#slice">Array Slice Literals</a><br /> <a href="#slice">Array Slice Literals</a><br />
<a href="#super">Calling Super from a Subclass</a><br /> <a href="#inheritance">Inheritance, and Calling Super from a Subclass</a><br />
<a href="#embedded">Embedded JavaScript</a><br /> <a href="#embedded">Embedded JavaScript</a><br />
<a href="#switch">Switch/When/Else</a><br /> <a href="#switch">Switch/When/Else</a><br />
<a href="#try">Try/Catch/Finally</a><br /> <a href="#try">Try/Catch/Finally</a><br />
@@ -332,8 +332,8 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
</p> </p>
<%= code_for('slices', 'three_to_six') %> <%= code_for('slices', 'three_to_six') %>
<p id="super"> <p id="inheritance">
<b class="header">Calling Super from a Subclass</b> <b class="header">Inheritance, and Calling Super from a Subclass</b>
JavaScript's prototypal inheritance has always been a bit of a JavaScript's prototypal inheritance has always been a bit of a
brain-bender, with a whole family tree of libraries that provide a cleaner brain-bender, with a whole family tree of libraries that provide a cleaner
syntax for classical inheritance on top of JavaScript's prototypes: syntax for classical inheritance on top of JavaScript's prototypes:
@@ -341,9 +341,11 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
<a href="http://prototypejs.org/">Prototype.js</a>, <a href="http://prototypejs.org/">Prototype.js</a>,
<a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc. <a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc.
The libraries provide syntactic sugar, but the built-in inheritance would The libraries provide syntactic sugar, but the built-in inheritance would
be completely usable if it weren't for one small exception: be completely usable if it weren't for a couple of small exceptions:
it's very awkward to call <b>super</b>, the prototype object's it's awkward to call <b>super</b> (the prototype object's
implementation of the current function. CoffeeScript converts implementation of the current function), and it's awkward to correctly
set the prototype chain. CoffeeScript provides <tt>extends</tt>
to help with prototype setup, and converts
<tt>super()</tt> calls into calls against the immediate ancestor's <tt>super()</tt> calls into calls against the immediate ancestor's
method of the same name. method of the same name.
</p> </p>

View File

@@ -8,19 +8,19 @@
this.name = name; this.name = name;
return this.name; return this.name;
}; };
Snake.prototype = new Animal(); Snake.prototype.__proto__ = new Animal();
Snake.prototype.move = function() { Snake.prototype.move = function() {
alert("Slithering..."); alert("Slithering...");
return this.constructor.prototype.move.call(this, 5); return Snake.prototype.__proto__.move.call(this, 5);
}; };
var Horse = function(name) { var Horse = function(name) {
this.name = name; this.name = name;
return this.name; return this.name;
}; };
Horse.prototype = new Animal(); Horse.prototype.__proto__ = new Animal();
Horse.prototype.move = function() { Horse.prototype.move = function() {
alert("Galloping..."); alert("Galloping...");
return this.constructor.prototype.move.call(this, 45); return Horse.prototype.__proto__.move.call(this, 45);
}; };
var sam = new Snake("Sammy the Python"); var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino"); var tom = new Horse("Tommy the Palomino");

View File

@@ -145,13 +145,13 @@ Animal.prototype.move: meters =>
alert(this.name + " moved " + meters + "m."). alert(this.name + " moved " + meters + "m.").
Snake: name => this.name: name. Snake: name => this.name: name.
Snake.prototype: Animal Snake extends new Animal()
Snake.prototype.move: => Snake.prototype.move: =>
alert('Slithering...') alert('Slithering...')
super(5). super(5).
Horse: name => this.name: name. Horse: name => this.name: name.
Horse.prototype: Animal Horse extends new Animal()
Horse.prototype.move: => Horse.prototype.move: =>
alert('Galloping...') alert('Galloping...')
super(45). super(45).

View File

@@ -53,7 +53,7 @@
<a href="#while">While Loops</a><br /> <a href="#while">While Loops</a><br />
<a href="#array_comprehensions">Array Comprehensions</a><br /> <a href="#array_comprehensions">Array Comprehensions</a><br />
<a href="#slice">Array Slice Literals</a><br /> <a href="#slice">Array Slice Literals</a><br />
<a href="#super">Calling Super from a Subclass</a><br /> <a href="#inheritance">Inheritance, and Calling Super from a Subclass</a><br />
<a href="#embedded">Embedded JavaScript</a><br /> <a href="#embedded">Embedded JavaScript</a><br />
<a href="#switch">Switch/When/Else</a><br /> <a href="#switch">Switch/When/Else</a><br />
<a href="#try">Try/Catch/Finally</a><br /> <a href="#try">Try/Catch/Finally</a><br />
@@ -565,8 +565,8 @@ three_to_six<span class="Keyword">:</span> nums[<span class="Number">3</span>, <
var three_to_six = nums.slice(3, 6 + 1); var three_to_six = nums.slice(3, 6 + 1);
;alert(three_to_six);'>run: three_to_six</button><br class='clear' /></div> ;alert(three_to_six);'>run: three_to_six</button><br class='clear' /></div>
<p id="super"> <p id="inheritance">
<b class="header">Calling Super from a Subclass</b> <b class="header">Inheritance, and Calling Super from a Subclass</b>
JavaScript's prototypal inheritance has always been a bit of a JavaScript's prototypal inheritance has always been a bit of a
brain-bender, with a whole family tree of libraries that provide a cleaner brain-bender, with a whole family tree of libraries that provide a cleaner
syntax for classical inheritance on top of JavaScript's prototypes: syntax for classical inheritance on top of JavaScript's prototypes:
@@ -574,9 +574,11 @@ var three_to_six = nums.slice(3, 6 + 1);
<a href="http://prototypejs.org/">Prototype.js</a>, <a href="http://prototypejs.org/">Prototype.js</a>,
<a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc. <a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc.
The libraries provide syntactic sugar, but the built-in inheritance would The libraries provide syntactic sugar, but the built-in inheritance would
be completely usable if it weren't for one small exception: be completely usable if it weren't for a couple of small exceptions:
it's very awkward to call <b>super</b>, the prototype object's it's awkward to call <b>super</b> (the prototype object's
implementation of the current function. CoffeeScript converts implementation of the current function), and it's awkward to correctly
set the prototype chain. CoffeeScript provides <tt>extends</tt>
to help with prototype setup, and converts
<tt>super()</tt> calls into calls against the immediate ancestor's <tt>super()</tt> calls into calls against the immediate ancestor's
method of the same name. method of the same name.
</p> </p>
@@ -585,13 +587,13 @@ var three_to_six = nums.slice(3, 6 + 1);
alert(<span class="Variable">this</span>.name <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>). alert(<span class="Variable">this</span>.name <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>).
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name. <span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name.
Snake.prototype<span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>() Snake <span class="Variable">extends</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>()
<span class="FunctionName">Snake.prototype.move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span> <span class="FunctionName">Snake.prototype.move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>) alert(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>)
<span class="Variable">super</span>(<span class="Number">5</span>). <span class="Variable">super</span>(<span class="Number">5</span>).
<span class="FunctionName">Horse</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name. <span class="FunctionName">Horse</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name.
Horse.prototype<span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>() Horse <span class="Variable">extends</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>()
<span class="FunctionName">Horse.prototype.move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span> <span class="FunctionName">Horse.prototype.move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>) alert(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>)
<span class="Variable">super</span>(<span class="Number">45</span>). <span class="Variable">super</span>(<span class="Number">45</span>).
@@ -614,19 +616,19 @@ tom.move()
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name; <span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name;
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>; <span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
}; };
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>(); <span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">__proto__</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span>() { <span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span>() {
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>); <span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>);
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">constructor</span>.<span class="LibraryConstant">prototype</span>.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>); <span class="Keyword">return</span> Snake.<span class="LibraryConstant">prototype</span>.__proto__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>);
}; };
<span class="Storage">var</span> <span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) { <span class="Storage">var</span> <span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name; <span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name;
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>; <span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
}; };
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>(); <span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">__proto__</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span>() { <span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span>() {
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>); <span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>);
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">constructor</span>.<span class="LibraryConstant">prototype</span>.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">45</span>); <span class="Keyword">return</span> Horse.<span class="LibraryConstant">prototype</span>.__proto__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">45</span>);
}; };
<span class="Storage">var</span> sam <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Snake</span>(<span class="String"><span class="String">&quot;</span>Sammy the Python<span class="String">&quot;</span></span>); <span class="Storage">var</span> sam <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Snake</span>(<span class="String"><span class="String">&quot;</span>Sammy the Python<span class="String">&quot;</span></span>);
<span class="Storage">var</span> tom <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Horse</span>(<span class="String"><span class="String">&quot;</span>Tommy the Palomino<span class="String">&quot;</span></span>); <span class="Storage">var</span> tom <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Horse</span>(<span class="String"><span class="String">&quot;</span>Tommy the Palomino<span class="String">&quot;</span></span>);
@@ -641,19 +643,19 @@ var Snake = function(name) {
this.name = name; this.name = name;
return this.name; return this.name;
}; };
Snake.prototype = new Animal(); Snake.prototype.__proto__ = new Animal();
Snake.prototype.move = function() { Snake.prototype.move = function() {
alert("Slithering..."); alert("Slithering...");
return this.constructor.prototype.move.call(this, 5); return Snake.prototype.__proto__.move.call(this, 5);
}; };
var Horse = function(name) { var Horse = function(name) {
this.name = name; this.name = name;
return this.name; return this.name;
}; };
Horse.prototype = new Animal(); Horse.prototype.__proto__ = new Animal();
Horse.prototype.move = function() { Horse.prototype.move = function() {
alert("Galloping..."); alert("Galloping...");
return this.constructor.prototype.move.call(this, 45); return Horse.prototype.__proto__.move.call(this, 45);
}; };
var sam = new Snake("Sammy the Python"); var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino"); var tom = new Horse("Tommy the Palomino");

View File

@@ -229,7 +229,7 @@
</dict> </dict>
<dict> <dict>
<key>match</key> <key>match</key>
<string>\b(super|this)\b</string> <string>\b(super|this|extends)\b</string>
<key>name</key> <key>name</key>
<string>variable.language.cs</string> <string>variable.language.cs</string>
</dict> </dict>

View File

@@ -11,7 +11,7 @@ token BREAK CONTINUE
token FOR IN WHILE token FOR IN WHILE
token SWITCH WHEN token SWITCH WHEN
token DELETE INSTANCEOF TYPEOF token DELETE INSTANCEOF TYPEOF
token SUPER token SUPER EXTENDS
token NEWLINE token NEWLINE
token COMMENT token COMMENT
token JS token JS
@@ -29,8 +29,8 @@ prechigh
right '-=' '+=' '/=' '*=' right '-=' '+=' '/=' '*='
right DELETE INSTANCEOF TYPEOF right DELETE INSTANCEOF TYPEOF
left "." left "."
right THROW FOR IN WHILE NEW right THROW FOR IN WHILE NEW SUPER
left UNLESS IF ELSE left UNLESS IF ELSE EXTENDS
left ":" '||:' '&&:' left ":" '||:' '&&:'
right RETURN right RETURN
preclow preclow
@@ -81,6 +81,7 @@ rule
| While | While
| For | For
| Switch | Switch
| Extends
| Comment | Comment
; ;
@@ -254,6 +255,11 @@ rule
| Super { result = val[0] } | Super { result = val[0] }
; ;
# Extending an object's prototype.
Extends:
Value EXTENDS Expression { result = ExtendsNode.new(val[0], val[2]) }
;
# A generic function invocation. # A generic function invocation.
Invocation: Invocation:
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) } Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }

View File

@@ -14,7 +14,7 @@ module CoffeeScript
"break", "continue", "break", "continue",
"for", "in", "while", "for", "in", "while",
"switch", "when", "switch", "when",
"super", "super", "extends",
"delete", "instanceof", "typeof"] "delete", "instanceof", "typeof"]
# Token matching regexes. # Token matching regexes.

View File

@@ -215,6 +215,20 @@ module CoffeeScript
end end
end end
# Node to extend an object's prototype with an ancestor object.
class ExtendsNode < Node
attr_reader :sub_object, :super_object
def initialize(sub_object, super_object)
@sub_object, @super_object = sub_object, super_object
end
def compile(o={})
"#{@sub_object.compile(o)}.prototype.__proto__ = #{@super_object.compile(o)}"
end
end
# A value, indexed or dotted into, or vanilla. # A value, indexed or dotted into, or vanilla.
class ValueNode < Node class ValueNode < Node
attr_reader :literal, :properties, :last attr_reader :literal, :properties, :last

View File

@@ -3,17 +3,17 @@ Base.prototype.func: string =>
'zero/' + string. 'zero/' + string.
FirstChild: => . FirstChild: => .
FirstChild.prototype.__proto__: new Base() FirstChild extends new Base()
FirstChild.prototype.func: string => FirstChild.prototype.func: string =>
super('one/') + string. super('one/') + string.
SecondChild: => . SecondChild: => .
SecondChild.prototype.__proto__: new FirstChild() SecondChild extends new FirstChild()
SecondChild.prototype.func: string => SecondChild.prototype.func: string =>
super('two/') + string. super('two/') + string.
ThirdChild: => . ThirdChild: => .
ThirdChild.prototype.__proto__: new SecondChild() ThirdChild extends new SecondChild()
ThirdChild.prototype.func: string => ThirdChild.prototype.func: string =>
super('three/') + string. super('three/') + string.