ES6 - ECMAScript2015
日本語 : エクマススクリプト
arrow function
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/arrow_functions
- https://github.com/lukehoban/es6features#arrows
When an arrow function is written without block braces, with or without multiple parameters, the expression that constitutes the function's body is implicitly returned 애로우 펑션을 블록이 없게 사용하는 경우 function의 body를 암묵적으로 리턴한다.
return
The return statement is implicitly added.
curried function
連続の複数のarrow functionを利用する形?
- http://stackoverflow.com/questions/32782922/what-do-multiple-arrow-functions-mean-in-javascript
- https://en.wikipedia.org/wiki/Currying
handleChange = field => e => {
e.preventDefault();
}
Syntax
// Basic syntax / 基本syntax :
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
// equivalent to: => { return expression; }
// returnが自動でつく
// Parentheses are optional when there's only one argument:
// argumentがひとつの場合には[]波かっこはoption
singleParam => { statements }
singleParam => expression
// A function with no arguments requires parentheses:
() => { statements }
// Advanced:
// Parenthesize the body to return an object literal expression:
params => ({foo: bar})
// Rest parameters are supported
(param1, param2, ...rest) => { statements }
Shorter function
var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
// old
var a2 = a.map(function(s){ return s.length});
// new
var a3 = a.map(s => s.lenght) // s.length を自動でreturnする
let
- let is block-scoped, it only exists within the current block.
- var is function-scoped.
const
letと似っている const is quite similar to let.
- const is also block-scoped
- const also enjoys the marvels of TDZ semantics
There’s also a couple of major differences.
- const variables must be declared using an initializer
- const variables can only be assigned to once, in said initializer
- const variables don’t make the assigned value immutable
- Assigning to const will fail silently
- Redeclaration of a variable by the same name will throw
一度assignされたら変更できないが、
const cool = { people: ['you', 'me', 'tesla', 'musk'] }
cool = {}
// <- "cool" is read-only
完全にimmutableではない
const cool = { people: ['you', 'me', 'tesla', 'musk'] }
cool.people.push('berners-lee')
console.log(cool)
// <- { people: ['you', 'me', 'tesla', 'musk', 'berners-lee'] }
// You can also make other references to the const that can, in fact, change.
const cool = { people: ['you', 'me', 'tesla', 'musk'] }
var uncool = cool
uncool = { people: ['edison'] } // so uncool he's all alone
console.log(uncool)
// <- { people: ['edison'] }
完全にimmutableにするためには
Hoisting
The scope of a var in JavaScript is like the bucket-of-paint tool in Photoshop. It extends in both directions from the declaration, forwards and backwards, and it just keeps going until it reaches a function boundary. Since this variable t’s scope extends so far backwards, it has to be created as soon as we enter the function. This is called hoisting. I like to imagine the JS engine lifting each var and function to the top of the enclosing function with a tiny code crane.
decorator - Javaのannotationみたいな
symbol
sym1 = Symbol()
sym2 = Symbol('name')
#typeof演算子で評価すると"symbol"が返される
typeof sym1 // "symbol"
new Symbol() // TypeError
symobj = Object(sym2)
typeof symobj // "object"
by object property
var foo = Symbol();
var hoge[foo] = {}
hoge[foo] // 5
hoge.foo // undefined
hoge['foo'] // undefined
- 一旦作ったシンボルは、それ自身とのみ等しくなる、ユニークなIDとして機能する。( === や == で評価して true になるものを、後から再生成することはできない。)そういう意味ではオブジェクトに似ている
- シンボルはオブジェクトのプロパティキーとして使用できる(そういう意味では文字列に似ている)。
- つまり
var foo = Symbol(); hoge[foo] = 5;
というコードが動作する(これはhoge.foo = 5
つまりhoge['foo'] = 5
とは全く別物)
- つまり
example
列挙・定数
const LOG_LEVEL_INFO = Symbol('log_level_info');
const LOG_LEVEL_WARN = Symbol('log_level_warn');
var suits = {
HEART: Symbol('heart'),
DIAMOND: Symbol('diamond'),
SPADE: Symbol('spade'),
CLUB: Symbol('club')
};
短所
JSONと相性が悪いこと。現状、JSON.stringify() はシンボル(キー側にあっても値側にあっても)を完全に無視してしまう。今でもreplacerで頑張れば部分的にはなんとかできるが、個人的にはまだ使いたくない。
DOMにプライベート情報を保存
ライブラリ作者などがDOM要素に一時データを保存したい場合、これまでは他のライブラリとの名前の衝突を防ぐため、 __mylib_private_isHogehoge のような、他のライブラリと干渉しないであろうユニークなキー名を使うなどしてしのいでいたが、シンボルを使えばそれがエレガントになるし、デバッグも少ししやすいかもしれない
// こうする代わりに
var isChecked = 'mylib_private_isChecked_j8z54eomjlcz';
domElement[isChecked] = true;
// シンボルを使えば、絶対に他人と衝突しない
var isChecked = Symbol('isChecked');
domElement[isChecked] = true;
注意
- Symbols and for...in iteration
- Symbols and JSON.stringify()
symbol reference
proto
- proto はすべてのオブジェクトが持つプロパティ
- Object.prototype (すべてのオブジェクトの元) の proto には null が入っている
- new したときに、コンストラクタの prototype への参照(アドレス)を格納する
- proto には prototype のアドレスが格納されている。prototype は proto のアドレスを参照し、プロトタイプチェーンが実現している。
- 最上位の Object.prototype.proto までプロパティを探索する
- Object.prototype の proto は null
- new でインスタンスを生成したとき、関数(コンストラクタ)の prototype へのアドレスを格納する
function SampleA() {}
SampleA.prototype.a = 'a';
console.log( SampleA.prototype.__proto__ );
var SampleB = function() {}
SampleB.prototype = new SampleA();
console.log( SampleB.prototype.__proto__ );
var sample_obj = new SampleB();
console.log( sample_obj.__proto__ );
console.log( sample_obj.__proto__.a );
Object
SampleA
SampleA
a
prototypeについて
prototype とは、関数オブジェクトが定義された時点で自動的に作成されるメンバ変数です
string
Rest Parameter
function concat (...words) {
return words.join(' ')
}
var result = concat('this', 'is', 'okay')
console.log(result)
// <- 'this is okay'
Spread Operator
- rest parameterとは違う?
- http://ponyfoo.com/articles/es6-spread-and-butter-in-depth
function concat(...words) {
return words.join(' ')
}
var result = concat('this', 'is', 'okay')
console.log(result);
Use Case | ES5 | ES6 |
---|---|---|
Concatenation | [1, 2].concat(more) | [1, 2, ...more] |
Push onto list | list.push.apply(list, [3, 4]) | list.push(...[3, 4]) |
Destructuring | a = list[0], rest = list.slice(1) | [a, ...rest] = list |
new + apply | new (Date.bind.apply(Date, [null,2015,31,8])) | new Date(...[2015,31,8]) |
Generator
- generator functionは
function*
で宣言する function*
で宣言されたfunctionはgeneratorをreturnするyield
を使ったfunctionがfunction*
つまり、*
がない場合にはエラーになる- generator functionは任意の時点で処理を中断/再開することができる関数
- Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.(from mdn)
generatorを理解するための3つのキーワード
- Generator Function
- 処理の中断/再開が行われる特殊な関数
function* generatorFunction(){}
のようにfunction*
を使って定義する
- Generator Object
- 中断された処理を再開したり、値を取得し対するオブジェクト
var generatorObject = generatorFunction()
のように取得する
- yield
- Generator Functionの中で使われる処理の中断を指定するキーワード
var result = yield request('http://example.com')
のように使う
references for generator
Object initializer (object初期化)
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Object_initializer
ECMAScript第6版での新しい表記法
- Shorthand property names
- Shorthand method names
- Computed property names
- これはvuexのmutation function name定義にも利用されている
// Shorthand property names (ES6)
var a = "foo", b = 42, c = {};
var o = { a, b, c };
// Shorthand method names (ES6)
var o = {
property([parameters]) {},
get property() {},
set property(value) {},
* generator() {}
};
// Computed property names (ES6)
var prop = "foo";
var o = {
[prop]: "hey",
["b" + "ar"]: "there",
};
Arrays
First, Array.from creates Array instances from array-like and iterable objects. Examples of array-like objects include:
Array.from
let itemElements = document.querySelectorAll('.items');
let items = Array.from(itemElements);
items.forEach(function(element) {
console.log(element.nodeType)
});
// A workaround often used in ES5:
let items = Array.prototype.slice.call(itemElements);
An interesting feature of Array.from is the second optional mapFunction argument. This allows you to create a new mapped array in a single invocation:
let navElements = document.querySelectorAll('nav li');
let navTitles = Array.from(navElements, el => el.textContent);
Array.of
let x = new Array(3); // [undefined, undefined, undefined]
let y = Array.of(8); // [8]
let z = [1, 2, 3]; // Array literal
find, findIndex, fill
[5, 1, 10, 8].find(n => n === 10) // 10
[5, 1, 10, 8].findIndex(n => n === 10) // 2
[0, 0, 0].fill(7) // [7, 7, 7]
[0, 0, 0, 0, 0].fill(7, 1, 3) // [0, 7, 7, 7, 0]
Other tips
lodashを使わなくても実装できるes6 method
Object.assign
& lodash._assign
これはなからずチェックしておくこと。 めっちゃつかう。
- Object.assign
- lodash assign
- This method mutates object and is based on Object.assign.
Syntax
Object.assign(target, ...sources)
Feature
- Cloning an object
- sources objectを値をcopyしてtarget objectを変更する。
- なので新しいobjectをreturnしたい場合には
Object.assign({}, source_obj);
このように - targetにmergeしたい場合には
var merged_target_obj = Object.assign(target_obj, source_obj);
にする
- Merging objects
- Copying symbol-typed properties
- symbolもいける
- Inherit properties and non-enumerable properties cannot be copied
- 継承したプロパティとnon-enumerableプロパティはcopyされない
- Primitives will be wrapped to objects
- primitive系はobjectにラップされる
- null and undefinedは無視される
- Exceptions will interrupt the ongoing copying task
- Copying accessors
Inherit properties and non-enumerable properties cannot be copied
var obj = Object.create({ foo: 1 }, { // foo is an inherit property.
bar: {
value: 2 // bar is a non-enumerable property.
},
baz: {
value: 3,
enumerable: true // baz is an own enumerable property.
}
});
var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
Primitives will be wrapped to objects
var v1 = '123';
var v2 = true;
var v3 = 10;
var v4 = Symbol('foo')
var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// Primitives will be wrapped, null and undefined will be ignored.
// Note, only string wrappers can have own enumerable properties.
console.log(obj); // { "0": "1", "1": "2", "2": "3" }