Jsdc_2014

剛參加完JSDC 2014, 做筆紀

Browserify

將原本跑在nodejs的javascript code以及dependencies打包後產生可以跑在browser上面的code. 很酷的是主講者還自己做了minecraft的Taipei 101版本.
主講者Github link

ZeroMQ

有兩個主題都跟ZeroMQ有關. 第一天的Casear簡單地介紹了在使用ZeroMQ遇到的一些問題,像是Security, Auth. 並且自己寫了一個ZeroMQ-SOA的module zmq-soa
第二天的主講者則是介紹了ZeroMQ訊息機制. PUSH/PULL, Pub/Sub. 以及在message queue滿了之後的MUTE機制. 該Block或Drop

HTML Accessibility

講者提到幾乎沒人注重的Accessibility並且用Mac safari裡的voice over功能演示去瀏覽有提供Accessibilty的網站時, 盲人也能夠透過語音導覽協助來使用網站. 作者web site 閱乎

Functional programming in javascript

很硬. 聽不太懂. 有提到Haskell及Monad

RxJS

主要是一個來解決非同步式問題的library. (Async is hard) 用裡面現成許多寫法的函數可以有效來處理非同步式問題.

leveldb

一種微型資料庫(micro database). 主要是由google開發. 方便在做一些小型project. 本身是key-value based所以也不會有什麼SQL support

KOA

一個架構在Express之上的nodejs web framework. 用ES6 generator的寫法來寫middleware並加進request flow裡面. 官網有example描述如何在不改動原本的程式碼之下log每一個request process time及增加response header

Teaching Git and GitHub with Node.js

主講者介紹她如何製作互動式教學教人使用Github. 用的工具是用nodeschool裡面製作課程的工具相同. 這是她的project Git-it

大型互聯網公司前端團隊的那些事兒

這個就跟技術沒有太多關係,主要是講管理一個團隊遇到的一些事情.

Node.js, p2p and MAD SCIENCE

主講者用javascript來實作p2p protocol並且還自己寫了幾種應用
1. torrent-stream p2p stream data
2. peerflix stream data to VLC
3. torrent-mount mount a virtual file using p2p.

Modal Dialog With Only CSS

Use CSS to create Modal Dialog. It’s fun. Click me to see it.

Put your elements inside div.modal

1
2
3
4
5
6
<div id="samplemodal" class="modal-contain">
  <div class="modal">
    <a href="#">X</a>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  </div>
</div>

Let’s see how css work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Hide the modal when nothing happen
.modal-contain {
  display: none;
}

//When modal-contain is targeted. Show it
.modal-contain:target {
  display: block;
  position: fixed;
  z-index: 10;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,.75);
}

//In order to vertical and horizon align
.modal {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  margin: 0 auto;
  width: 25em;
  padding: 1em;
  background: #fff;
}
css

Js/css Build Optimize Flow

I changed the my work frontend module build optimize flow few day ago. The original build flow is use wro4j plugin and my buildhelper. The cons is that there’re many trivial steps when adding a new page. I decide to update it with new way. I study the YEOMAN build flow and apply it in my module. The result is good.

YEOMAN uses nodejs and Grunt to do optimize. But the company module system is using Maven. The first step is to find a maven plugin to run grunt. I choose grunt-maven-plugin and it’s easy to integrate.

Continue reading →

Convert File Format by Cloudconvert

前陣子無意間看到cloudconvert這個beta service有提供API讓開發者 進行線上的轉檔並且支持AWS S3。過去常常需要轉檔時才在找有沒有軟體或線上服務,想想這次索性自己開發 一個簡單的web site然後使用別人的API來進行轉檔工作。
我已經在Github上開了一個project myconverter 並且也放上Heroku weichien-myconverter
一開始我使用aws-s3這個gem來做S3的操作,但不知道為什麼在放上heroku後它卻無法使用。只好緊急改 回用aws-sdk

在實作過程中的一些學習
1. 不要隨意操作Tempfile. 本來是想要在不同的controller之間操作的User upload的Tempfile. 但查了SO得到的建議是不要,最好在第一個接到request的那個controller就把Tempfile先寫進一個storage.
2. ActiveSupport Notification的使用. Reference
3. 直接下載在S3上的檔案. Reference
4. gem ‘browserlog’ github link 直接在browser上看log,對開發上很有幫助。

Use :target Pseudo-selector

Read a article and found css selector :target interested. I apply it to my blog. Please click the below link to see the difference.
To anchor

Try me

I set the css selector to change the word color to blue when anchor is become targeted.

sass
1
2
3
4
5
h3 {
  a:target {
    color: blue;
  }
}
css

Invoke Route From Controller in Emberjs

Emberjs official document doesn’t describe how to invoke route method/event from controller. Why we need this?
Some methods(like render,controllerFor) only exist in Route scope.

PostRoute
1
2
3
4
5
6
7
App.PostRoute = Ember.Route.extend({
  events: {
    showWarn: {
      this.render('warn', { "outlet": "msgView", "into": "post" })
    }
  }
});

Use this.get('target').send to trigger Route event.

PostController
1
2
3
4
5
6
7
8
9
10
App.PostController = Ember.Controller.extend({
  actions: {
    showMessage: {
      //some process here
      if (need_render) {
        this.get('target').send('showWarn');
      }
    }
  }
});

CSS Property Pointer-events

The CSS property pointer-events could control mouse/touch event fired or not. The default value is auto and you can set none to disable event firing. For example:
You have a Sign-in button.

HTML file
1
2
3
<div>
  <div id="signin" class="signin-btn">Signin</div>
</div>

The traditional way to prevent user clicks multiple times.

JS
1
2
3
4
5
6
$('#signin').on('click', function(event){
  if (is_signin_process) {
    return; //prevent multiple click during signin flow.
  }
  //doing signin flow
});

Now you could just add a processing className to prevent multiple click events. No more click event firing after set pointer-events: none;

CSS file
1
2
3
.signin-btn.processing {
  pointer-events: none;
}
JS
1
2
3
4
$('#signin').on('click', function(event){
  $('#signin').addClass('processing');
  //doing signin flow
});
css

Dependency Injection in Emberjs

I read some articles about Emberjs Dependency Injection(DI) and try to upgrade my old project food_ntpc to use DI. The pros of DI is to reduce gobal object and make code clean, testing easily. There are few changes.

before

I create a CategoryManager and put in FoodNtpc App.

category.js.coffee
1
2
3
4
5
6
class CategoryManager
  categorys: ->
    return categorys
  ...

FoodNtpc.CategoryManager = new CategoryManager

It’s very easy to access through FoodNtpc.CategoryManager everywhere. BUT it makes testing hardly and more coupling.

index_route.js.coffee
1
2
3
FoodNtpc.IndexRoute = Ember.Route.extend
  model: ->
    return FoodNtpc.CategoryManager.categorys()

after

Only the Ember.Object class can be registered. I change class CategoryManager to CategoryManager = Ember.Object.extend. Then create an initializer to register the CategoryManager in foodntpc:categoryManager naming. Set singleton to true because i want only one CategoryManager in system.

category.js.coffee
1
2
3
4
5
6
7
8
9
10
11
12
#class CategoryManager 
CategoryManager = Ember.Object.extend
  categorys: ->
    return categorys
  ...

#FoodNtpc.CategoryManager = new CategoryManager
Ember.Application.initializer
  name: 'categoryManager'
  initialize: (container, application) ->
    console.log 'register categoryManager'
    container.register('foodntpc:categoryManager', CategoryManager, {singleton: true})

Next, create another initializer to inject those dependency. We can use after or before to adjust initializer sequence.
In application.js line 6 means foodntpc:categoryManager is injected to IndexRoute and use CategoryManager reference. In line 7 means inject into all controller.

application.js
1
2
3
4
5
6
7
8
9
Ember.Application.initializer({
  name: 'injectModelManager',
  after: 'categoryManager',

  initialize: function(container, application) {
    application.inject('route:index', 'CategoryManager', 'foodntpc:categoryManager');
    application.inject('controller', 'CategoryManager', 'foodntpc:categoryManager');
  }
});

In IndexRoute, we use this.CategoryManager to access it.

index_route.js.coffee
1
2
3
FoodNtpc.IndexRoute = Ember.Route.extend
  model: ->
    return @CategoryManager.categorys()

Add Testing to Your Gem

There’s the old post about how to create a ruby gem. This post is focus on how to add testing to gem.
First, add rspec into your gem dependency. I like to use rspec as my testing framework. Here i also use webmock to mock my http request during spec testing.

your_gem.gemspec
1
2
3
4
5
Gem::Specification.new do |spec|
  ...
  spec.add_development_dependency 'rspec'
  spec.add_development_dependency 'webmock'
end

Create a rake task to run rspec. I create rspec.rake under {gem_root_folder}/tasks/. Set the default task to :spec then i can just type rake to run testing. In the meantime i set --color -f d to output result with color and document format.

rspec.rake
1
2
3
4
5
6
7
8
require 'rspec/core/rake_task'

desc 'Default: run specs.'
task :default => :spec

RSpec::Core::RakeTask.new do |task|
  task.rspec_opts = '--color -f d'
end

In order to exec our rspec.rake. Import all .rake file under tasks folder to Rakefile.

Rakefile
1
2
3
require "bundler/gem_tasks"

Dir.glob('tasks/**/*.rake').each(&method(:import))

Now we could start to write testing.
Create {gem_root_folder}/spec/ folder and spec_helper.rb under it.

spec_helper.rb
1
2
3
4
5
# require webmock/rspec to use webmock in rspec testing framework
require 'webmock/rspec'

# mvn_utils is the target class to be test
require 'mvn_utils'

Create mvn_utils_spec.rb under {gem_root_folder}/spec/ and require spec_helper. We could require all targets in spec_helper.rb and each spec.rb file just require spec_helper.

mvn_utils_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'spec_helper'
describe MvnUtils do

  let(:mvn_base) { "mvn.example.net/service/local/repositories/" }
  let(:header_hash) { {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'} }

  describe 'should find the latest version' do
    let(:response_xml) { "<metadata><groupId>a.b</groupId><artifactId>x.y</artifactId><versioning>" +
                         "<release>1.0.100</release><versions><version>1.0.98</version>" +
                         "<version>1.0.99</version></versions></versioning></metadata>" }

    it 'find_latest_version' do
      stub_request(:get, "http://#{mvn_base}a/b/x/y/maven-metadata.xml")
      .with(:headers => header_hash)
      .to_return(:status => 200, :body => response_xml, :headers => {})

      expect(MvnUtils.find_latest_version).to eq('1.0.100')
    end

end

In the above example, i test MvnUtils.find_latest_version and expect the value should be 1.0.100. This function will inovke http request to mvn server but i use stub request here to prevent real http connection. With stub request i could control the returned xml data.

ES6 Preview

這篇文章用來記錄ES6的一些新功能。參考Lukehoban的版本並加以中文化。 基本上所有的example codes我都有在ES6 fiddle或用Traceur執行過。

ES6包含這些新的功能:
- let + const
- enhanced object literals
- arrows
- classes
- template strings
- destructuring
- default + rest + spread
- iterators + for..of
- generators
- comprehensions
- unicode
- modules
- module loaders
- map + set + weakmap + weakset
- proxies
- symbols
- promises
- math + number + string + object APIs
- binary and octal literals
- reflect api
- tail calls

ECMAScript 6 Features

Let + Const

let可以想成是新的var,建立的變數只會存活在Block scope裡。 const是用來建立常數。

1
2
3
4
5
6
7
8
9
10
11
12
13
function x() {
  let a = 2;
  a = a + 3;
  return a;
}

console.log(x()); //5
console.log(a); // undefined

let x = 44;
let x = 55; //應該要報錯,但目前模擬器實作都不會報錯
const b = "hello";
b = "abc"; //應該要報錯,但目前模擬器實作都不會報錯

Enhanced Object Literals

增強的物件描述(Object Literal)支援在建構子設定prototype。可直接在定義的函數內呼叫父類別方法(super call)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var foo = "i am foo";

var obj = {
  __proto__: [],

  // 定義方法
  getLength() {
    return this.length;
  },

  // 動態設定變數/方法名稱
  [ 'prop_' + (() => 42)() ]: "Should be 42",

  //foo:foo 的縮寫
  foo
};

obj.push("1");
console.log(obj.getLength()); //1
console.log(obj.prop_42); //"Should be 42"
console.log(obj.foo); //"i am foo"

Arrows

箭號’=>’是一種定義函數的縮寫方式。 同時支援函數表達式(function expression)跟函數聲明(function statement)。 但跟傳統函數有點不一樣的是在箭號定義函數裡面的this是跟呼叫它的程式碼擁有一樣的lexical scope。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let square = x => x * x;
let add = (a, b) => a + b;
let pi = () => 3.1415;

console.log(square(5)); //25
console.log(add(3, 4)); //7
console.log(pi()); //3.1415

// Lexical this
var bob = {
  _name: "Bob",
  _friends: ['Ken'],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f)
    );
  }
}
bob.printFriends(); //Bob knows Ken

Classes

ES6類別提供一種簡單宣告方式讓class pattern更容易使用。 類別支援繼承(inheritance),父類別呼叫(super calls),建構子(constructor),個體方法(instance method)和類別方法(class method)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Polygon {
  constructor(height, width) { //class constructor
    this.name = 'Polygon';
    this.height = height;
    this.width = width;
  }

  static doIt() {  //class method
    console.log('Do it now');
  }
}

class Square extends Polygon {
  constructor(length) {
    super(length, length); //call the parent method with super
    this.name = 'Square';
  }

  get area() { //calculated attribute getter
    return this.height * this.width;
  }
}

let s = new Square(5);
console.log(s.area); //25
Square.doIt(); //"Do it now"

Template Strings

Template String提供許多方便方法建立字串。 還可以使用函式來預先處理Template String。被稱為Tagged Template String。可以避免被塞入非預期中的字串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var myString = `In JavaScript '\n' is a line-feed.`;

// Multiline strings
var myString2 = `In JavaScript this is
 not legal.`;

var name = "Bob", time = "today";
var myString3 = `Hello ${name}, how are you ${time}?`

console.log(myString); //"In JavaScript ' ' is a line-feed."
console.log(myString2); //"In JavaScript this is not legal."
console.log(myString3); //"Hello Bob, how are you today?"

function tag(strings, ...values) {
  if (!(strings[0] === 'a' && strings[1] === 'b')) {
    return 'bad';
  }
  return 'good';
}
console.log(tag `a${ 123 }b`);  // "good"
console.log(tag `c${ 123 }d`);  // "bad"

Destructuring

Destructuring assignment允許使用陣列或物件pattern來自動給予值。 Destructuring是錯誤安全(fail-soft)的。當配對不上時會給予undefined。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let [one, two, three] = [1, ,3];
let {four, five} = {four:4, five:  5};
console.log(two === undefined); //true
console.log(one, two, three, four, five); // 1 3 4 5

function today() { return { d: 11, m: 3, y: 2014 }; }
var { m: month, y: year } = today();
console.log(month); //3
console.log(year); //2014

books = [];
books.push({ title:"hello", author:"samuel" });
books.push({ title:"hello2", author:"samuel2" });
books.forEach(function ({ title: t, author: a }) {
  console.log(t, a);
  // hello samuel
  // hello2 samuel2
})

Default + Rest + Spread

函數參數可設定預設值(Default)

1
2
3
4
5
6
7
8
function f(x, y=12) {
  // y值會是12 當呼叫者沒有傳入值或傳入值為undefined
  return x + y;
}
console.log(f(3)); //15
console.log(f(3, undefined)); //15
console.log(f(3, null)); //3
console.log(f(3, 4)); //7

不定長度函數參數(Rest Parameter),函數傳入值當做陣列處理

1
2
3
4
5
6
function f(x, ...y) {
  // y 是一個陣列
  return x * y.length;
}
console.log(f(3, "hello", true)); //6
console.log(f(3, "hello", true, 444)); //9

Spread Operator可使用陣列當做函數參數,陣列內的值會被當做相對應的參數值

1
2
3
4
5
6
7
8
9
10
function f(x, y, z) {
  return x + y + z;
}
console.log(f(...[1,2,3])); //6
console.log(f(...[1,2,3,4,5])); //6

var x = [1, 2];
var y = [3, 4];
x.push(...y);
console.log(x); //1,2,3,4

Iterators + For..Of

Iterator Object讓使用者可以客製化iteration行為,就像Java的Iteratable。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//For..Of
let arr = [1, 2, 3, 4, 5];
let sum = 0;

for (let v of arr) {
  sum += v;
}

console.log('1 + 2 + 3 + 4 + 5 =', sum); // 1 + 2 + 3 + 4 + 5 = 15

//Basic Iterator
// fibonacci整個object就是iterator
let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur]; //每次循徊把pre值設成cur,cur值則為cur+pre
        return { done: pre > 100, value: pre } //當pre值大於100時終止
      }
    }
  }
}

for (var n of fibonacci) {
  console.log(n); // 1 1 2 3 5 8 13 21 34 55 89
}

// Iteration相關的介面
interface IteratorResult {
  done: boolean;
  value: any;
}
interface Iterator {
  next(): IteratorResult;
}
interface Iterable {
  [Symbol.iterator](): Iterator
}

Generators

Generator使用function*來宣告函數並且返回一個Generator實例。 Generator實例是一種Iterator。可以透過yieldnext可以一步一步執行函數內容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function* range(start, end, step) {
  while (start < end) {
    yield start;
    start += step;
  }
}

for (let i of range(0, 10, 2)) {
  console.log(i); // 0 2 4 6 8
}


//Fibonacci數列實作by Generator
var fibonacci = function*() {
  var pre = 0, cur = 1;
  while (true) {
    yield cur;
    var tmp = cur;
    cur += pre;
    pre = tmp;
  }
}

f = fibonacci();
for (var n of f) {
  if (n > 100)
    break;
  console.log(n); // 1 1 2 3 5 8 13 21 34 55 89
}

//你也可以自己使用next一步一步取值
//可以用done這個boolean值來判斷是否已經沒有next
f = fibonacci();
var obj;
while (!(obj = f.next()).done) {
  if (obj.value > 100) {
    break;
  }
  console.log(obj.value);  // 1 1 2 3 5 8 13 21 34 55 89
}

// Generator的介面
interface Generator extends Iterator {
  next(value?: any): IteratorResult;
  throw(exception: any);
}

Comprehensions

Array Comprehension和Generator Comprehension提供簡單方式來處理數列

1
2
3
4
5
6
7
8
9
10
11
12
//Array Comprehension
let arr = [1, 2, 3, 4, 5];
let squared = [for (x of arr) x * x];

console.log(squared); //1,4,9,16,25

//Generator Comprehension
let squared2 = (for (x of arr) x * x);

for (let z of squared2) {
  console.log(z); //1 4 9 16 25
}

Unicode

完整21bit的Unicode支援

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// same as ES5.1
console.log("𠮷".length == 2); //true

// new RegExp behaviour, opt-in ‘u’
//console.log("𠮷".match(/./u)[0].length == 2); //在ES6模擬器上無法使用

// new form
console.log("\uD842\uDFB7" == "𠮷"); //true
//console.log("\u{20BB7}" == "𠮷"); //在ES6模擬器上無法使用

// new String ops
console.log("𠮷".codePointAt(0) == 0x20BB7); //true

//在ES6模擬器上無法使用
// for-of iterates code points
//for(var c of "𠮷") {
//  console.log(c);
//}

Modules

像AMD,CommonJS一樣可以自行定義module以及彼此之間的相依性。

1
2
3
4
5
6
7
// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
var msg2 = "Ya";
export { msg2 as message};

使用module語法載入math.js

1
2
3
4
5
// app.js
module math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi)); //2π = 6.283186 
console.log(math.msg2); //undefined
console.log(math.message); //Ya

使用import語法來載入sum函數和pi變數

1
2
3
4
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi)); //2π = 6.283186
console.log(message); //undefined 

Module Loaders

Module Loader提供動態載入(Dynamic loading),命名空間(Namespace),狀態獨立(State isolation)等功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//更改預設js目錄
System.baseURL = '/lib/';

//動態載入math.js
System.import('math').then(function(m) {
  //m 是math的namespace
  console.log("2π = " + m.sum(m.pi, m.pi)); //2π = 6.283186
});

//理論上可以用Loader來執行程式碼在某一個context底下
//但模擬器找不到Loader..
// Create execution sandboxes – new Loaders
var loader = new Loader({
  global: fixup(window) // replace ‘console.log’
});
loader.eval("console.log('hello world!');");

Map+Set+Weakmap+Weakset

新的資料結構,由於目前模擬器都還沒有實作所以沒有親自試過。不過我相信這部份上手很快不難。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set

Proxies

Proxy故名思義就是讓對某一個物件(host object)的所有行為透過代理物件(proxy object)。 方便用來logging或效能分析(profiling)。目前模擬器還沒實作。

1
2
3
4
5
6
7
8
9
10
// Proxying a normal object
var target = {};
var handler = {
  get: function (receiver, name) {
    return `Hello, ${name}!`;
  }
};

var p = new Proxy(target, handler);
p.world === 'Hello, world!';
1
2
3
4
5
6
7
8
9
10
// Proxying a function object
var target = function () { return 'I am the target'; };
var handler = {
  apply: function (receiver, ...args) {
    return 'I am the proxy';
  }
};

var p = new Proxy(target, handler);
p() === 'I am the proxy';

Symbols

Symbol是一種新的primitive type。Symbol可以用來當做property的key值而且是唯一的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var key = Symbol("key");

function MyClass(privateData) {
  this[key] = privateData;
}

MyClass.prototype = {
  say: function() {
    console.log(this[key]);
  }
};

var c = new MyClass("hello")
console.log(c["key"]); //undefined 因為Symbol並不是String
console.log(c[key]); //hello
console.log(c.say()); //hello

Promises

Promise就不用再多提,目前已經有很多現成的ES5 library有實作Promise pattern了。 像RSVP.jsQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, then…

  if (/* everything turned out fine */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
});

promise.then(function(result) {
  console.log(result); // "Stuff worked!"
}, function(err) {
  console.log(err); // Error: "It broke"
});

Math+Number+String+Object APIs

ES6增加了許多好用的函數。底下範例在目前模擬器上尚無法完全支援。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false

Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2

"abcde".contains("cd") // true
"abc".repeat(3) // "abcabcabc"

Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"

//Object copying
Object.assign(Point, { origin: new Point(0,0) })

Binary and Octal Literals

新的二進位(b)和八進位(o)表示式

1
2
console.log(0b111110111 === 503); // true
console.log(0o767 === 503); // true

Reflect API

參考ECMAScript wiki

Tail Calls

Tail Call最佳化讓遞迴程式call stack不會無限增加導致記憶體用完。

1
2
3
4
5
6
7
8
9
function factorial(n, acc = 1) {
    'use strict';
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc);
}

// 在目前的browser幾乎都會stack overflow
// 未來ES6上面是安全的
factorial(100000);