CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again

CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again

FOWA 2011

5b9fe87ec1faa67a4599782930f45ec9?s=128

Sam Stephenson

October 05, 2011
Tweet

Transcript

  1. None
  2. Sam Stephenson 37signals

  3. Better JavaScript with CoffeeScript

  4. CoffeeScript is Beautiful & I Never Want to Write Plain

    JavaScript Again
  5. A Brief Personal History

  6. 2004

  7. names = people.map do |person| person.name.capitalize end

  8. var names = []; for (var i = 0, l

    = people.length; i < l; i++) { var person = people[i]; var name = person.name. slice(0, 1).toUpperCase() + person.name.slice(1); names.push(name); }
  9. . prototype .

  10. var names = people.map( function(person) { return person.name. capitalize(); }

    );
  11. var names = people.map( function(person) { return person.name. capitalize(); }

    );
  12. var names = people.map( function(person) { return person.name. capitalize(); }

    );
  13. 2010

  14. We’re Stuck With JavaScript

  15. Compile to JavaScript

  16. Google Web Toolkit

  17. package com.example.gwt.helloworld.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.Window;

    import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; public class HelloWorld implements EntryPoint { @Override public void onModuleLoad() { Label label = new Label("Hello world"); Button button = new Button("Say something"); button.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { Window.alert("Hello again"); } }); RootPanel.get().add(label); RootPanel.get().add(button); } }
  18. <?xml version="1.0" encoding="UTF-8"?> <module rename-to='com_example_gwt_helloworld'> <!-- Inherit the core Web

    Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/> <inherits name='com.google.gwt.user.theme.standard.Standard'/> <!-- Specify the app entry point class. --> <entry-point class='com.example.gwt.helloworld.client.HelloWorld'/> </module> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- Default page to serve --> <welcome-file-list> <welcome-file>Com_example_gwt_helloworld.html</welcome-file> </welcome-file-list> </web-app>
  19. None
  20. function hello(){var l='',F='" for "gwt:onLoadErrorFn"',D='" for "gwt:onPropert yErrorFn"',n='"><\/script>',p='#',r='/',vb='02EDD05CEF7D978C649BF91CC66AB635.ca che.html',sb='28600060343BDDEB1FD83487846D090C.cache.html',ub='9EDF9C067C3A475F 6EA7A16003CA4979.cache.html',Fb='<script

    defer="defer">hello.onInjectionDone(\' hello\')<\/script>',dc='<script id="',A='=',q='?',C='Bad handler "',tb='D69BADE 681FFFC6071DFA4FC25EF1DC5.cache.html',Eb='DOMContentLoaded',wb='F26BFE33DA14C23 176C7BC08D58AA43F.cache.html',o='SCRIPT',cc='__gwt_marker_hello',s='base',nb='b egin',cb='bootstrap',u='clear.cache.gif',z='content',bc='end',lb='gecko',mb='ge cko1_8',yb='gwt.hybrid',xb='gwt/standard/standard.css',E='gwt:onLoadErrorFn',B= 'gwt:onPropertyErrorFn',y='gwt:property',Db='head',qb='hosted.html?hello',Cb='h ref',kb='ie6',ab='iframe',t='img',bb="javascript:''",zb='link',pb='loadExternal Refs',v='meta',eb='moduleRequested',ac='moduleStartup',jb='msie',w='name',gb='o pera',db='position:absolute;width:0;height:0;border:none',Ab='rel',ib='safari', rb='selectingPermutation',x='startup',m='hello',Bb='stylesheet',ob='unknown',fb ='user.agent',hb='webkit';var fc=window,k=document,ec=fc.__gwtStatsEvent?functi on(a){return fc.__gwtStatsEvent(a)}:null,zc,pc,kc,jc=l,sc={},Cc=[],yc=[],ic=[], vc,xc;ec&&ec({moduleName:m,subSystem:x,evtGroup:cb,millis:(new Date()).getTime( ),type:nb});if(!fc.__gwt_stylesLoaded){fc.__gwt_stylesLoaded={}}if(!fc.__gwt_sc riptsLoaded){fc.__gwt_scriptsLoaded={}}function oc(){var b=false;try{b=fc.exter nal&&(fc.external.gwtOnLoad&&fc.location.search.indexOf(yb)==-1)}catch(a){}oc=f unction(){return b};return b}function rc(){if(zc&&pc){var c=k.getElementById(m) ;var b=c.contentWindow;if(oc()){b.__gwt_getProperty=function(a){return lc(a)}}h ello=null;b.gwtOnLoad(vc,m,jc);ec&&ec({moduleName:m,subSystem:x,evtGroup:ac,mil lis:(new Date()).getTime(),type:bc})}};function mc(){var j,h=cc,i;k.write(dc+h+ n);i=k.getElementById(h);j=i&&i.previousSibling;while(j&&j.tagName!=o){j=j.prev iousSibling}function f(b){var a=b.lastIndexOf(p);if(a==-1){a=b.length}var c=b.i ndexOf(q);if(c==-1){c=b.length}var d=b.lastIndexOf(r,Math.min(c,a));return d>=0 ?b.substring(0,d+1):l};if(j&&j.src){jc=f(j.src)}if(jc==l){var e=k.getElementsBy TagName(s);if(e.length>0){jc=e[e.length-1].href}else{jc=f(k.location.href)}}els e if(jc.match(/^\w+:\/\//)){}else{var g=k.createElement(t);g.src=jc+u;jc=f(g.sr
  21. Pyjamas

  22. import pyjd # this is dummy in pyjs. from pyjamas.ui.RootPanel

    import RootPanel from pyjamas.ui.Button import Button from pyjamas.ui.HTML import HTML from pyjamas.ui.Label import Label from pyjamas import Window import pygwt def greet(fred): fred.setText("No, really click me!") Window.alert("Hello, AJAX!") if __name__ == '__main__': pyjd.setup("public/Hello.html?fred=foo#me") b = Button("Click me", greet, StyleName='teststyle') h = HTML("<b>Hello World</b> (html)", StyleName='teststyle') l = Label("Hello World (label)", StyleName='teststyle') base = HTML("Hello from %s" % pygwt.getModuleBaseURL(), StyleName='teststyle') RootPanel().add(b) RootPanel().add(h) RootPanel().add(l) RootPanel().add(base) pyjd.run()
  23. I love/prefer {insert AJAX / Javascript framework here}, how do

    I use it? Not being funny or anything, but unless you have the resources of google or lots of money or lots of time, or you can gather enough people to make it so that everyone has less work to do: you don't. huh? why?? Some of the widgets in DojoX / Ext-JS are really cute! I want them! waaah! You are not in Kansas any more. Pyjamas is declarative- style programming, using a "real" programming language. All those widget-sets were designed to be driven from inside HTML (a style of web development which, using Pyjamas, you have just left far behind) and by inserting javascript snippets into the HTML. If you try that with a Pyjamas app, you are not only likely to get yourself into an
  24. I love/prefer {insert AJAX / Javascript framework here}, how do

    I use it? Not being funny or anything, but unless you have the resources of google or lots of money or lots of time, or you can gather enough people to make it so that everyone has less work to do: you don't. huh? why?? Some of the widgets in DojoX / Ext-JS are really cute! I want them! waaah! You are not in Kansas any more. Pyjamas is declarative- style programming, using a "real" programming language. All those widget-sets were designed to be driven from inside HTML (a style of web development which, using Pyjamas, you have just left far behind) and by inserting javascript snippets into the HTML. If you try that with a Pyjamas app, you are not only likely to get yourself into an
  25. I love/prefer {insert AJAX / Javascript framework here}, how do

    I use it? Not being funny or anything, but unless you have the resources of google or lots of money or lots of time, or you can gather enough people to make it so that everyone has less work to do: you don't. huh? why?? Some of the widgets in DojoX / Ext-JS are really cute! I want them! waaah! You are not in Kansas any more. Pyjamas is declarative- style programming, using a "real" programming language. All those widget-sets were designed to be driven from inside HTML (a style of web development which, using Pyjamas, you have just left far behind) and by inserting javascript snippets into the HTML. If you try that with a Pyjamas app, you are not only likely to get yourself into an
  26. Objective-J

  27. @implementation AppController : CPObject { } - (void)applicationDidFinishLaunching:(CPNotification)note { theWindow

    = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask]; contentView = [theWindow contentView]; var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; [label setStringValue:@"Hello World!"]; [label setFont:[CPFont boldSystemFontOfSize:24.0]]; [label sizeToFit]; [label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin]; [label setFrameOrigin:CGRectMake(100,100)]; [contentView addSubview:label]; [theWindow orderFront:self]; } @end
  28. None
  29. CoffeeScript is a little language that compiles into JavaScript. Underneath

    all of those embarrassing braces and semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way. The golden rule of CoffeeScript is: “It’s just JavaScript”. The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime.
  30. CoffeeScript is a little language that compiles into JavaScript. Underneath

    all of those embarrassing braces and semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way. The golden rule of CoffeeScript is: “It’s just JavaScript”. The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime.
  31. It’s Just JavaScript $(function() { $("body").html("Hello world"); });

  32. It’s Just JavaScript $ -> $("body").html "Hello world"

  33. CoffeeScript is a little language that compiles into JavaScript. Underneath

    all of those embarrassing braces and semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way. The golden rule of CoffeeScript is: “It’s just JavaScript”. The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime.
  34. None
  35. None
  36. Good Part: Private by default

  37. Good Part: Private by default (function() { /* your program

    here */ }).call(this);
  38. Good Part: No more var

  39. Good Part: No more var lastClick = 0 $("a").click ->

    now = new Date().getTime() if now - lastClick > 100 $("#message").show() lastClick = now
  40. Good Part: No more var var lastClick = 0 $("a").click

    -> var now = new Date().getTime() if now - lastClick > 100 $("#message").show() lastClick = now
  41. Good Part: Strict comparisons

  42. Good Part: Strict comparisons == vs. ===

  43. Good Part: Strict comparisons "true" == true // true "true"

    === true // false "0" == false // true "0" == 0 // true 0 === false // false "" == false // true
  44. Good Part: Strict comparisons "true" is true // false "true"

    is "true" // true "0" is false // false "0" is 0 // false 0 is 0 // true "" is false // false
  45. Good Part: Runs anywhere

  46. Good Part: Runs anywhere JSLint compliant

  47. 10 Things I Love About CoffeeScript

  48. 1. Function Syntax

  49. 1. Function Syntax function greet (name) { return "Hello "

    + name; }
  50. 1. Function Syntax greet = (name) -> "Hello " +

    name
  51. 1. Function Syntax $("a").click(function(event) { $(this).addClass("busy"); });

  52. 1. Function Syntax $("a").click (event) -> $(this).addClass "busy"

  53. 2. Significant Whitespace

  54. 2. Significant Whitespace if (url) { $.get(url, function(data) { return

    $("#result").html(data); }); } else { $("#error").show(); }
  55. 2. Significant Whitespace if url $.get url, (data) -> $("#result").html

    data else $("#error").show()
  56. 2. Significant Whitespace var readConfiguration = function(callback) { return path.exists(filename,

    function(err, exists) { if (exists) { return fs.readFile(filename, callback); } else { return callback(false); } }); };
  57. 2. Significant Whitespace readConfiguration = (callback) -> path.exists filename, (err,

    exists) -> if exists fs.readFile filename, callback else callback false
  58. 3. Bare Objects

  59. 3. Bare Objects $(this).css({ top: "20px", left: "-20px" });

  60. 3. Bare Objects $(this).css top: "20px", left: "-20px"

  61. 3. Bare Objects $.ajax({ url: path, timeout: 5, data: {

    from: "workspace" }, dataType: "html", success: function(data) { return $("#result").html(data); } });
  62. 3. Bare Objects $.ajax url: path, timeout: 5, data: from:

    "workspace", dataType: "html", success: (data) -> $("#result").html data
  63. 3. Bare Objects person = name: "Sam" age: 27 profession:

    "Programmer"
  64. 4. Everything’s an Expression

  65. 4. Everything’s an Expression switch keyCode when 38 command =

    "previous" when 40 command = "next" when 13 command = "select"
  66. 4. Everything’s an Expression command = switch keyCode when 38

    then "previous" when 40 then "next" when 13 then "select"
  67. 4. Everything’s an Expression getCommand = (keyCode) -> switch keyCode

    when 38 then "previous" when 40 then "next" when 13 then "select"
  68. 5. Comprehensions

  69. 5. Comprehensions names = [] for person in people names.push

    capitalize person.name
  70. 5. Comprehensions names = for person in people capitalize person.name

  71. 5. Comprehensions ages = (person.age for person in people)

  72. 5. Comprehensions names = for person in people when age

    > 27 capitalize person.name
  73. 5. Comprehensions var names, person; names = (function() { var

    _i, _len, _results; _results = []; for (_i = 0, _len = people.length; _i < _len; _i++) { person = people[_i]; if (age > 27) { _results.push(capitalize(person.name)); } } return _results; })();
  74. 6. Classes & Inheritance

  75. 6. Classes & Inheritance class Photo constructor: (url) -> this.url

    = url createElement: -> $("<img>").attr "src", this.url
  76. 6. Classes & Inheritance class Photo constructor: (url) -> this.url

    = url createElement: -> $("<img>").attr "src", this.url
  77. 6. Classes & Inheritance class Photo constructor: (url) -> @url

    = url createElement: -> $("<img>").attr "src", @url
  78. 6. Classes & Inheritance class Photo constructor: (@url) -> createElement:

    -> $("<img>").attr "src", @url
  79. 6. Classes & Inheritance class Thumbnail extends Photo createElement: ->

    $el = super $el.height 100
  80. 6. Classes & Inheritance var Thumbnail; var __hasProp = Object.prototype.hasOwnProperty,

    __extends = 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; child.__super__ = parent.prototype; return child; }; Thumbnail = (function() { __extends(Thumbnail, Photo); function Thumbnail() { Thumbnail.__super__.constructor.apply(this, arguments); } Thumbnail.prototype.createElement = function() { var $el; $el = Thumbnail.__super__.createElement.apply(this, arguments); return $el.height(100); }; return Thumbnail; })();
  81. 7. Bound Functions

  82. 7. Bound Functions class PersonView constructor: (@person, @el) -> request:

    -> $.get @person.url, (data) -> $(@el).html data
  83. 7. Bound Functions class PersonView constructor: (@person, @el) -> request:

    -> $.get @person.url, (data) -> $(@el).html data
  84. 7. Bound Functions function request () { $.get(this.person.url, (function(data) {

    $(this.el).html(data); }).bind(this) ) }
  85. 7. Bound Functions class PersonView constructor: (@person, @el) -> request:

    -> $.get @person.url, (data) => $(@el).html data
  86. 7. Bound Functions class PersonView constructor: (@person, @el) -> $(@el).bind

    "click", @showName showName: => $(@el).html @person.name
  87. 8. Conditionals

  88. 8. Conditionals @request() if @isActive() return unless $("li").length

  89. 8. Conditionals result + 10 if result?

  90. 8. Conditionals @panel?.restore()

  91. 8. Conditionals @panel.url ?= window.location

  92. 9. Destructuring Assignment

  93. 9. Destructuring Assignment name = person.name

  94. 9. Destructuring Assignment name = person.name {name} = person

  95. 9. Destructuring Assignment name = person.name {name} = person {name,

    age} = person
  96. 9. Destructuring Assignment [first, last] = person.name.split " "

  97. 10. String Syntax

  98. 10. String Syntax greet = (name) -> "Hello #{name}"

  99. 10. String Syntax greet = (name) -> "Hello #{name.toUpperCase()}"

  100. 10. String Syntax render = (person) -> """ <div class="person">

    <a href="#{person.url}">#{person.name}</a> <span>#{person.profession}</span> </div> """
  101. How To Use It

  102. None
  103. None
  104. Command Line $ npm install -g coffee-script

  105. Command Line $ coffee -c hello.coffee $ cat hello.js (function()

    { alert("hello"); }).call(this);
  106. Command Line $ coffee -cw hello.coffee

  107. Rails 3.1 Sprockets https://github.com/sstephenson/sprockets

  108. Node.js Stitch https://github.com/sstephenson/stitch

  109. Thank You @sstephenson

  110. http://coffeescript.org/