Chrome でアロー関数と Promise がつかえるようになっていました

ということで 少し使ってみました

アロー関数は chrome45 から使えます
Promise はいつから詳しくわからないですが Chrome44では使えました

アロー関数

私的には これが一番 Ecma6 の大きい機能です
var fn = function (e){return e*e}
var fn = e => e*e
と書けます

map とかの引数に関数を書くときに便利です
> [1,2,3,4,5].map(e=>e*e).reduce((a,b)=>a+b)
< 55

無名関数って 中身が1つの式で return することが多いと思いますが 複数の式を書きたい場合もあります
そういうときは {} を使います
var fn = (a,b) =>{
var t = a + b
return t
}
fn(100,200) // 300
{} があると return が必要です

逆にいうと返り値がいらないときは {} で囲めばいいです
var fn = e => {e.pop()}
fn([1,2,3]) // undefined


短く書けるようになるだけじゃなくて いくつか違いもあるので アロー関数で置き換わるのじゃなくて function での関数定義と両方を使っていくことになると思います

this が変わらない

今まで無名関数の中に入ると this が変わってしまうので bind したり that self と言った変数に this を退避させておかないとダメでした
var obj = {
fn: function(val){
var self = this
console.log(this.data)
console.log("bind なし")
val.forEach(function(e){
console.log(this === window)
console.log("this.data", this.data)
console.log("self.data", self.data)
})
console.log("bind あり")
val.forEach(function(e){
console.log(this === window)
console.log("this.data", this.data)
console.log("self.data", self.data)
}.bind(this))
},
data : 100
}
obj.fn([1])
100
bind なし
true
this.data undefined
self.data 100
bind あり
false
this.data 100
self.data 100
function の無名関数は何かのメソッド(オブジェクトのプロパティ)じゃないので 関数内では this window になります

アロー関数だと this が関数の外側と一緒になります
さらに this の値は実行する場所で変わらないです
関数が作られたところの this にずっと固定になります
var obj = {
afn: () => this,
fn: function (){return this},
afn2: function(){return ()=>this}
}
console.log(obj.afn() === window) // true
console.log(obj.fn() === obj) // true

var fn2 = function(){return this}
console.log(fn2() === window) // true
obj.fn2 = fn2
console.log(obj.fn2() === obj) // true

var fn = obj.afn2()
console.log(fn() === obj) // true
こんな感じです
obj.afn を作った時の this window です
obj.fn のように this obj にはなりません

function でつくると fn2 のようにプロパティに入れて 実行する場所を変えれば this も変わります
アロー関数では obj.afn2 のようにグローバルに持ってきても this obj から変わらないです

なので
var obj = {
fn: function(val){
val.forEach(e => {this.data += e})
},
data: 0
}
obj.fn([2,3,4])
obj.data // 9
こういうことができます

this を特別扱いにしないで 普通にレキシカルスコープとして定義されたところから見える this を見ると考えればわかりやすいかもしれません

arguments が作られない

this 以外に arguments の扱いも違います
アロー関数では arguments が作られないので 外側の arguments が見れます
グローバルだと定義されていないのでエラーになります
var fn = ()=> arguments
fn(1,2)
Uncaught ReferenceError: arguments is not defined(…)

var fn2 = ()=>()=>()=>arguments
fn2()()()
Uncaught ReferenceError: arguments is not defined(…)

function fn3(){
(()=>{console.log(arguments)})()
}
fn3(1,2,3)
// [1, 2, 3]

function fn4(){
return ()=>arguments
}
fn4(1,2,3,4)()
// [1, 2, 3, 4]

こっちも arguments を特別扱いせずに定義されたところの外側の arguments がレキシカルスコープなので見れると考えればいいと思います

Firefox では昔からアロー関数が使えましたがアロー関数でも arguments  が作られてしまっていて arguments が外側の見れるようになったのは最近だったと思います


map などのメソッドに渡すような中身の短い関数では arguments this もそのままであってほしいことが多いので 使いやすくされてますね

ほとんどの場合では アロー関数で良さそうですが function で関数定義しないといけないときもあります
一番よくあるのは prototype にメソッドを追加する時かと思います
Array.prototype.last = function(){return this[this.length-1]}
これを アロー関数にしてしまうと 配列の last メソッドを呼んだ時の this window なので 配列とは全く関係ない ページ内にある最後のフレームの window undefined が返ってくることになります

Promise

非同期処理が便利になるというやつです

とりあえず new します
new Promise()
Uncaught TypeError: Promise resolver undefined is not a function(…)
エラーです

さすがにこれはどう使うのか知らないとどうしようもなかったので軽くググッてみました
最初に実行する関数をコンストラクタに渡すようです
> new Promise(function(){console.log("promise")})
promise
< Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
返り値は Promise オブジェクトというまた特殊なものです

コンストラクタに渡した 引数の関数自体も setTimeout のように非同期実行されると書いてるところもありましたがそんなことはなく new したら即実行されます
↑の結果でも返り値よりさきに 「promise」 がでてますし


Promise では返り値のオブジェクトに対して then catch のメソッドで完了したら次に何をやるかをチェーンさせます
イメージはこんな感じです…… がこれじゃいつまでまっても 「promise」 しかでません
new Promise(function(){console.log("promise")})
.then(function(){console.log("then")})
.catch(function(){console.log("catch")})

最初に引数で渡した関数は 2つの引数を与えられて実行されます
new Promise(function(a,b){console.log(a,b)})
function () { [native code] } funciton () { [native code] }
まったくなにやってるかわからないですね

1つめは 成功したときに呼べばいい関数
2つめは 失敗したときに呼べば良い関数
です

1つめを resolve 2つめを reject と書くことが多いですが 引数の名前なので好きに決めても大丈夫です
new Promise(function(success, fail){
console.log("promise")
success("success")
fail("fail")
})
.then(function(){console.log("then", ...arguments)})
.catch(function(){console.log("catch", ...arguments)})
promise
then success
両方実行しても最初の方に対応した関数が実行されてます

逆に catch する方を先に呼んでみます
new Promise(function(success, fail){
console.log("promise")
fail("fail")
success("success")
})
.then(function(){console.log("then", ...arguments)})
.catch(function(){console.log("catch", ...arguments)})
promise
catch fail
then の方は実行されてません


成功か失敗した関数を呼んだ時に then catch が無くても大丈夫です
then をつけたときに成功関数が呼ばれると実行されます
var p = new Promise(function(success, fail){
console.log("promise")
success("success")
})
setTimeout(function(){
p.then(function(){
console.log("then")
})
console.log("add then")
}, 1000);
promise
(1秒後)
add then
then
このときは then を実行したときに 関数が呼ばれるのじゃなくて 非同期でいったん実行中の処理が終わってから then で設定した関数が呼ばれます


try catch がなくても エラーがあると catch が実行されます
var p = new Promise(function(s, f){
s()
})
var p2 = p.then(function(){
console.log("then1")
})
p.catch(function(){
console.log("catch1")
})
var p3 = p2.then(function(){
console.log("then2")
console.log(null.a)
console.log("then2-2")
})
p2.catch(function(){
console.log("catch2")
})
p3.catch(function(){
console.log("catch3")
})
then1
then2
catch3

エラーが起きる p2.then() の返り値 p3 に対して catch メソッドをつけていないときや p3.then() をつけていると
Uncaught (in promise) TypeError: Cannot read property 'a' of null(…)
とエラーが出ます

最初の関数で第二引数の関数(reject の方)を実行していて catch なしでもエラーが出ます
var p = new Promise(function(s, f){
f()
})
Uncaught (in promise) undefined

Promise はまだまだ機能があって複雑そうですので とりあえず今回はこの辺にしておきます