CoffeeScript learning notes

December 01, 2014

Reading time ~10 minutes

overview

#赋值
number = 42
opposite = true

#条件
number = -42 if opposite

#函数
square = (x) -> x*x

#数组
list = [1, 2, 3, 4, 5]

#对象
math =
  root:   Math.sqrt
  square: square
  cube: (x) -> x * square x

#Splats:
race = (winner, runners...) ->
  print winner,runners

#存在性
alert "I knew it!" if elvis?

# 数组 推导(comprehensions):
cubes = (math.cube num for num in list)

语言手册

不需要{}包裹代码块 在 函数, if表达式,switch,和try/catch当中使用缩进。

传参 不需要使用圆括号表明函数被执行。 隐式函数调用作用范围直到行尾或者一个块级表达式。

console.log sys.inspect object – console.log(sys.inspect(object))

函数

square = (x) -> x * x
cube = (x) -> square(x) * x

fill = (container, liquid = "coffee") ->
	"Filling the #{container} with #{linquid}..."

对象和数组

单个属性写在自己的一行里,逗号可以省略

song = ["do", "re", "mi", "fa", "so"]

singers = {Jagger: "Rock", Elvis:"Roll"}

bitlist = [
	1,0,1
	0,0,1
	1,1,0
  ]

kids =
  brother:
    name: "Max"
	age: 11
  sister:
    name: "Ida"
	age: 9

#JavaScript不能使用不添加引号的保留字段作为属性名称,如 class.
#CoffeeScript会被识别并补上引号

$('.account').attr class: 'active'
log object.class

词法作用域和变量安全

永远不用写var

outer =1
changeNumbers = ->
  inner = -1
  outer = 10
inner = changeNumbers()

if, else, unless 和条件赋值

mood = greatlyImproved if singing

if happy and knowIt
  clapsHands()
  chaChaCha()
else
  showIt()

date = if friday then sue else jill

变参(splats)

JavaScript: arguments

CoffeeScript: …

gold = silver = rest = "unknown"

awardMedeals = (first, second, others...) ->
	gold = first
	silver = second
	rest = others

contenders = [
	"Michael Phelps"
	"Liu Xiang"
	"Yao Ming"
	"Allyson Felix"
	"Shawn Jothson"
	"Guo Jingjing"
	"Tyson Gay"
	"Asafa Powell"
	"Usain Bolt"
]

awardMedals contenders...

alert "Gold: " + gold
alert "Silver: " + silver
alert "The Field" + rest

循环和推导式

eat food for food in ['toast', 'chese', 'wine']

courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu i + 1, dish for dish, i in courses

foods = ['broccoli','spinach','chocolate']
eat food for food in foods when food isnt 'chocolate'

推导式可以适用于其他一些使用循环的地方,如each/farEach, map,select/filtere.g.

shortNames = (name for name in list when name.length < 5)

如果希望以固定跨度迭代,可以在范围推导式中指定开始与介绍。

countdown = (num for num in [10..1])

使用by子句,实现以固定跨度迭代范围值

evens = (x for x in [0..10] by 2)

推导式也可以用于迭代对象中的key和value,在推导式中使用 of 来取出对象中的属性,而不是数组中的值

yearsOld = max: 10, ida: 9, tim: 11

ages = for child, age of yersOld
  "#{child} is #{age}"

如果希望仅迭代当前对象定义的属性,通过hasOwnProperty检查并 避免属性是继承来的,可以这样来写

for own key, value of object

while可以作为表达式来用,而且可以返回一个数组,该数组包含每个迭代项的迭代结果。

if this.studyingEconomics
  buy() while supply > demand
  sell() until supply > demand

num = 6
lyrics = while num -= 1
  "#{num} little monkeys, jumping on the bed,
    One fell and bumped his head."

until 等同于 while not

loop 等同于 while true

do 提供闭包

for filename in list
  do (filename) ->
    fs.readFile filename, (err,contents) ->
	  compile filename, contents.toString()

Array Slicing and Splicing with Ranges

a..b 闭区间 [a,b]

a…b [a,b)

default: start: 0 , end: length-1

Everything is an Expression(at least, as much as possible)

grade = (student) ->
  if student.excellentWork
    "A+"
  else if student.okayStuff
    if student.triedHard then "B" else "B-"
  else
    "C"

eldest = if 24 > 21 then "Liz" else "Ike"
var eldest,grade;

grade = function(student) {
  if (student.excellentWork) {
    return "A+";
  } else if (student.okayStuff) {
    if (student.triedHard) {
	  return "B";
	} else {
	  return "B-";
	}
  } else {
    return "C";
  }
};

eldest = 24 > 21 ? "Liz" : "Ike" ;

Things that would otherwise be statements in JavaScript, when used as part of an expression in CoffeeScript, are converted into expressions by wrapping them in a closure.

globals = (name for name fo window)[0...10]
alert(
  try
    nonexistent/ undefined
  catch error
    "And the error is ... #{error}"
)

break, continue, and return won’t try to perform the conversion.

Operators and Aliases

CoffeeScript JavaScript is === isnt !== not ! and && or || true,yes,no true flase,no,off false @,this this of in in —— a**b Math.pow(a, b) a//b Math.floor(a / b) a%%b (a % b + b ) % b

The Existential Operator

? : check the existance of a variable

returns true unless a variable is null or undefined

solipsism = true if mind? and not world?

speed = 0
speed ?= 15
footprints = yeti ? "bear"

zip = lottery.drawWinner?().address?.zipcode

if the chain is broken , undefined is returned instead of the TypeError.

Classes, Inheritance, and Super

class Animal
  constructor: (@name) ->

  move: (meter) ->
    alert @name + " moved #{meters}m."


class Snake extends Animal
  move: ->
    alert "Slithering..."
	super 5

class Horse extends Animal
  move: ->
    alert "Galloping..."
	super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the palomino"

sam.move()
tom.move()
String::dasherize = ->
  this.replace /_/g, "-"
String.prototype.dasherize = function() {
  return this.replace(/_/g, "-");
};

Destructuring Assignment

theBait = 1000
theSwitch = 0

[theBait, theSwitch] = [theSwitch, theBait]

weatherReport = (location) ->
  [location, 72, "Mostly Sunny"]

[city, temp, forecast] = weatherReport "Berkeley, CA"



futurists =
  sculptor: "Umberto Boccioni"
  painter: "Vladmir Burliuk"
  poet:
    name: "F.T. Marinetti"
	address: [
	  "Via Roma 42R"
	  "Bellagio, Italy 22021"
	]


{poet: {name, address: [street, city]}} = futurists



tag = "<impossible>"

[open,contents..., close] =tag.split("")

 

text = "Every literary critic believes he will outwit history and have the last word"
[first, ..., last] = text.split " "



class Person
  constructor: (options) ->
    {@name, @age, @height} = options

tim = new Person age:4

Function Binding

Account = (customer, cart) ->
  @customer = customer
  @cart = cart

  $('.shopping_cart').bind 'click',(event) =>
    @customer.purchase @cart

Embedded JavaScript

hi = `function(){
  return [documnet.title, "HelloJavaScript"].join(": ");
}`

Switch/When/Else

switch day
  when "Mon" then go work
  when "Tue" then go relax
  when "Thu" then go iceFishing
  when "Fri", "Sat"
    if day is bingoDay
	  go bingo
	  go dancing
	when "Sun" then go church
	else go work



score = 76
grade = switch
  when score < 60 then 'F'
  when score < 70 then 'D'
  when score < 80 then 'C'
  when score < 90 then 'B'
  else 'A'
 # grade == 'C'

Try/Catch/Finally

try
  allHellBreaksLoose()
  catsAndDogsLivingTogether()
catch error
  print error
finally
  clearUp()

#Chained Comparisons
cholesterol = 127

healthy = 200 > cholesterol > 60
#In JavaScript
# healthy = (200 > cholesteral && cholesteral > 60)


#String Interpolation, Block Strings, and Block Comments
author = "Wittgenstein"
quote = "A picture is a fact. -- #{ author}"

sentence = "#{ 22 / 7 } is a decent approximation of pai"



html = """
	   <strong>
	     cup of coffeescript
	   </strong>
	   """

###
Multiple Comments
...
###

Block Regular Expressions

block-regexes – extended regular expressions that ignore internal whitespace and can cantain comments and intepolation.

delimited by /// .

OPERATOR = /// ^ (
  ?: [-=]>
   | [-+*/%<>&|^!?=]=
   | >>>=?
   | ([-+:])\1
   | ([&|<>])\2=?
   | \?\.
   | \.{2,3}
) ///

Cake, and CakeFiles

fs = require 'fs'

option '-o', '--output [DIR]', 'directory for compiled code'

task 'build:parser', 'rebuild the Jison parser', (options) ->
  require 'jison'
  code = require('./lib/grammer').parser.generate()
  dir = options.output or 'lib'
  fs.writeFile "#{dir}/parser.js", code

Source Maps

text/coffeescript Script Tags

coffee-script.js

<script type="text/coffeescript">