ScalikeJDBCでページネーションに必要なクエリを発行する
ScalikeJDBCを使ってページネーションするにはどうすればよいか調べました。 DBはMySQLを使用します。
MySQLで発行するクエリ
SELECT SQL_CALC_FOUND_ROWS * -- LIMIT を付けなかった場合に返されるはずの行数を知るためのオプション FROM users LIMIT 10 -- 取得する行数を制限する OFFSET 5; -- 開始位置(行数) SELECT FOUND_ROWS(); -- 直前に実行したクエリの行数を取得する
ポイント:FOUND_ROWS()
直前に実行したクエリの行数を取得する
直前に実行したクエリにSQL_CALC_FOUND_ROWS
が指定されていた場合、「LIMIT を付けなかった場合に返されるはずの行数」を取得できる
ScalikeJDBCで書く
DSLを使って書くとこんな感じ
withSQL { select(sqls"sql_calc_found_rows *") .from(User as u) .limit(10) .offset(5) }.map(User(u)).single.apply() SQL("select found_rows() as total").map(_.long("total")).single.apply()
ハマったところ
メソッド内で上のコードを実行すると、SQL操作ごとに新しいセッションを開始するためか、select found_rows()
のクエリで直前のクエリを認識できない(totalCount
が1件になる)
def totalCount(implicit session: DBSession = AutoSession): Option[Long] = { withSQL... } res: Option[Long] = Some(1) // 1件になってしまう
解決策:DB.readOnly
で囲えば正しい結果を取得できた
def totalCount: Option[Long] = { DB readOnly { implicit s => withSQL... } } res: Option[Long] = Some(3) // 正しい結果がとれる
--- 12/25 追記 ---
Call back function after list.apply · Issue #63 · scalikejdbc/scalikejdbc · GitHub
AutoSession starts new session for each SQL operation
ScalikeJDBCの AutoSession
はSQL操作ごとに新しいセッションを開始する仕様とのことなので、
ReadOnlySession
を使ってクエリ実行後に session.close
する方法なら、DB.readOnly
で囲わなくても良さそうです。
Scala学習【エラー処理に使う型】
前回に引き続きScalaの学習。今回はエラー処理で使うであろう戻り値をラップする型について。
Option型
- 値をひとつだけ入れることができる
- 2種類の形を持っている
- Some(x) 値があるときの形
- None 値がないときの形
- 値があったりなかったりする値を表現するときに使う
- エラー内容を表現できないので、エラー処理では使わない
// Some: 値があるとき scala> val some: Option[String] = Option("hoge") some: Option[String] = Some(hoge) scala> some.get res7: String = hoge // None: 値がないとき scala> val none: Option[String] = Option(null) none: Option[String] = None scala> none.get java.util.NoSuchElementException: None.get at scala.None$.get(Option.scala:349) at scala.None$.get(Option.scala:347) ... 29 elided
None
を get
したときの例外は getOrElse
メソッドを使えば回避できる
scala> option.getOrElse("値がありません") res3: String = 値がありません
パターンマッチを使って処理することもできる
scala> val some: Option[String] = Option("hoge") some: Option[String] = Some(hoge) scala> some match { | case Some(str) => println(str) // Someの中身をstrという変数に束縛している | case None => print("None") | } hoge
Optionはコレクションの性質を持っているので、例えばmapを使える
// Some の場合 scala> Some(3).map(_ * 3) res10: Option[Int] = Some(9) // None の場合は関数を適用しても None のまま scala> val none: Option[Int] = None none: Option[Int] = None scala> none.map(_ * 3) res11: Option[Int] = None // foldメソッドを使えばデフォルト値を指定できる scala> none.fold(0)(_ * 3) res12: Int = 0
Either型
- Option型と違い、エラー時に任意のエラーの種類まで取得できる
- RightとLeftの2つの値を持つ
- Right: 正常な値
- Left: エラー値
scala> val right: Either[String, Int] = Right(123) right: Either[String,Int] = Right(123) scala> val left: Either[String, Int] = Left("abc") left: Either[String,Int] = Left(abc) // パターンマッチもできる scala> right match { | case Right(i) => println(i) | case Left(s) => println(s) | } 123
Option型と同様にmapメソッドを持っている
Either型の左右が平等に扱われる場合、mapメソッドはどちらに適用されるのだろう。となるが、暗黙的にrightを優先される(Scala2.12から)
Scala2.12より前は平等に扱われるため、RightProjection
型に変換する必要があった
// サジェストされるメソッドを確認 scala> right. canEqual filterOrElse forall isLeft joinRight merge productIterator swap toTry contains flatMap foreach isRight left productArity productPrefix toOption exists fold getOrElse joinLeft map productElement right toSeq // Scala2.12 scala> right.map(_ * 3) res0: scala.util.Either[String,Int] = Right(369) // Scala2.12より前 scala> right.right res1: scala.util.Either.RightProjection[String,Int] = RightProjection(Right(123)) scala> right.right.map(_ * 3) res0: scala.util.Either[String,Int] = Right(369)
Try型
- 2つの値をとる
- Success: 正常な値
- Failure: エラー値(ただし
Throwable
というException
のスーパークラスしか入れられない)
- applyで生成する際に例外をキャッチし、Failureにする
- NonFatal(致命的でない)という種類の例外だけキャッチする
scala> import scala.util.Try import scala.util.Try scala> val failure: Try[Int] = Try(throw new RuntimeException("to be caught")) failure: scala.util.Try[Int] = Failure(java.lang.RuntimeException: to be caught) scala> val success = Try(3) success: scala.util.Try[Int] = Success(3)
参考
Scala学習【コレクション】
最近Scalaをはじめたので、学習した内容をブログに書いていけば、知識が補強されて理解が深まるのでは?と思い書いていきます。 今回はScalaの豊富なコレクションライブラリーについて簡単に特徴をまとめました。おそらく基本的なものしか網羅できてません。
シーケンス
Seq型はただのトレイトであり、実際の実装は複数存在している
List型
- 先頭の値(head)とその後ろのリスト(tail)により構成される
- 不変
- 先頭の値の取得や削除は高速だが、任意の添字を持つ要素へのアクセスは、リストを線形に辿っていくため、高速ではない
- 先頭の値の取得や削除は高速なので、パターンマッチに適している
scala> List(1, 2, 3) res0: List[Int] = List(1, 2, 3) scala> 3::res0 res1: List[Int] = List(3, 1, 2, 3)
Array型
- Javaの配列と互換性を持った型
- 可変
- Javaの配列は、宣言時にメモリの確保を行う。
- そして、添字で数値を指定してそのメモリ上の値を取得するため、添字を使った特定の要素の参照は、一度のアクセスで行うことができ高速
- 一方挿入削除を行う際は、再度配列のメモリの確保を行うのでメモリ効率は悪くなる
scala> Array(1, 2, 3) res3: Array[Int] = Array(1, 2, 3) scala> res3(1) res4: Int = 2
ListBuffer
- 可変
- 要素を先頭に挿入するときも末尾に追加するときも一定時間で処理できるリスト
scala> import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer scala> val buf = new ListBuffer[Int] buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer() scala> buf += 1 res6: buf.type = ListBuffer(1) scala> buf += 2 res7: buf.type = ListBuffer(1, 2) scala> 3 +=: buf res8: buf.type = ListBuffer(3, 1, 2)
ArrayBuffer
- 可変
- 配列の先頭と末尾で要素を追加・削除できる
scala> import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer scala> val buf = new ArrayBuffer[Int]() buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer() scala> buf += 12 res9: buf.type = ArrayBuffer(12) scala> buf += 15 res10: buf.type = ArrayBuffer(12, 15) scala> 3 +=: buf res11: buf.type = ArrayBuffer(3, 12, 15)
Range
- 範囲を表すオブジェクト
scala> 1 to 3 res12: scala.collection.immutable.Range.Inclusive = Range 1 to 3 scala> 1 to 17 by 4 res14: scala.collection.immutable.Range = Range 1 to 17 by 4 scala> 1 until 3
文字列(StringOps)
- Predef(Scalaソースファイルに暗黙のうちにimportされているオブジェクト)がStringからStringOpsへ暗黙の型変換を提供しているため、すべての文字列はシーケンスのように扱うことが出来る。
scala> val s = "hoge" s: String = hoge scala> s(1) res5: Char = o
集合
Set
- 要素の集合を表す型
- 要素は重複しない
- 可変と不変があり、デフォルトは不変
- 順番を維持しない(順序が必要な際はTreeSetを用いる)
scala> val s = Set(1, 1, 2, 2, 3, 3) s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
連想配列
Map
- キーと値を持つ連想配列
- 可変と不変があり、デフォルトは不変
- キーと値のセットは2要素のタプルで表現されている
- 添字を使って中身の値を取得することができるが、nullが発生する可能性があるため、基本はgetメソッドを利用してOptionの値を取得する
- 順番を維持しない(順序が必要な際はTreeMapを用いる)
scala> val m = Map("a" -> 1, "b" -> 2, "c" -> 3) m: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3)
コレクション
Stack
- 後入れ先出しのコレクション
- 可変と不変がある
- pushメソッドで要素を入れ、pop2メソッドで先頭の値と残った要素を持つStackをタプルとして取得できる
scala> scala.collection.immutable.Stack() res0: scala.collection.immutable.Stack[Nothing] = Stack() scala> res0.push(1) res1: scala.collection.immutable.Stack[Int] = Stack(1) scala> res1.push(2) res2: scala.collection.immutable.Stack[Int] = Stack(2, 1) scala> res2.pop2 res3: (Int, scala.collection.immutable.Stack[Int]) = (2,Stack(1))
Queue
- 先入れ先出しのコレクション
- 可変と不変がある
- enqueueメソッドで要素を入れ、dequeueメソッドで先頭の値と残った要素を持つQueueをタプルとして取得できる
scala> scala.collection.immutable.Queue() res4: scala.collection.immutable.Queue[Nothing] = Queue() scala> res4.enqueue(1) res5: scala.collection.immutable.Queue[Int] = Queue(1) scala> res5.enqueue(2) res6: scala.collection.immutable.Queue[Int] = Queue(1, 2) scala> res6.dequeue res7: (Int, scala.collection.immutable.Queue[Int]) = (1,Queue(2))
PriorityQueue
- enqueueした順によらず、自動的に中身をソートして並べてくれるQueue(優先度つきキュー)
- 可変のみ
- 挿入削除時に並べ替えるコストが低く、先頭の値取得も高速に行うことができる
- newBuilderメソッドの引数にOrdering型のオブジェクトを渡すことでソートする順番を変更できる
- ソートにコストがかかり、先頭の値だけを取得すれば良いような処理を、非常に高速に処理することができるコレクション
タプル
- 異なる型のオブジェクトを結合して、1単位として渡せるようにしたもの
scala> (1, "hello", Console) res21: (Int, String, Console.type) = (1,hello,scala.Console$@2de22e7)
参考
【React】親コンポーネント更新による子コンポーネントの更新処理を減らす
Reactでは親コンポーネントが更新されると、その子コンポーネント全てに更新処理が走るのですが、値に変更がない子コンポーネントは更新処理を行わないようにできないものかと調べたら、良さそうな方法が見つかったので備忘録として残します。
参考:
ReactでshouldComponentUpdateを使ったチューニングの効果と注意どころ - Qiita Reactの再レンダリングをなるべく減らす - Aqutras Members' Blog
Reactコンポーネントの更新処理をキャンセルするためには、shouldComponentUpdate
内でfalse
を返してあげれば良い。(デフォルトではtrue
)
なので、子コンポーネントのprops
とstate
に変更がなければ、shouldComponentUpdate
内でfalse
を返す処理を実装すれば、データの更新がない子コンポーネントの更新を行わないようにできる。
props
とstate
が全て値型か、参照型を含むかで異なる実装を行う。
全て値型の場合
react-addons-pure-render-mixinを使うと簡単に実装できて良い。
import PureRenderMixin from 'react-addons-pure-render-mixin'; class HogeComponent extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); } render() { return ( ... ) } }
値型だけでなく参照型を含む場合
lodashの _.isEqual 関数を用いてオブジェクトを比較する。 もちろん値型も見てくれるので、react-addons-pure-render-mixinを使わずに、この方法に統一しても良さそう。
import isEqual from 'lodash/isEqual'; class HogeComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { const propsDiff = isEqual(nextProps, this.props); const stateDiff = isEqual(nextState, this.state); return !(propsDiff && stateDiff); } render() { return ( ... ) } }
とりあえず今の自分の環境では↑の2通りの方法を使って実装しています。
GulpとBabelでES2015をES5に変換する環境をつくる
前回の記事でES2015の文法について覚えたので、今回はES2015をES5に変換するための環境をつくります。
参考:
Node.js
Node.jsとは
サーバーサイドのJavaScript、非同期の処理が得意。
インストール
方法は色々とありますが、今回はnodebrewというバージョン管理ツールを利用してインストールします。 ターミナルを開いて以下のコマンドを実行。 nodebrewのインストールが開始されます。
$ curl -L git.io/nodebrew | perl - setup
======================================== Export a path to nodebrew: export PATH=$HOME/.nodebrew/current/bin:$PATH ========================================
インストール後、環境設定ファイルにnodebrewのパスを通してねと言われるので、.bash_profile
をviエディタで開き、コードを追記します。
$ vi ~/.bash_profile
export PATH=$HOME/.nodebrew/current/bin:$PATH
sourceコマンドで.bash_profile
をリロードします。
$ source ~/.bash_profile
nodebrewがインストールされているか確認。
$ nodebrew help nodebrew 0.9.5 Usage: nodebrew help Show this message ...
nodebrewをインストールし終わったら、Node.jsの使用するバージョンを指定します。今回は安定版を指定しました。 -vコマンドでバージョンを確認してNode.jsのインストール完了です。
$ nodebrew use stable use v5.10.1 $ node -v v5.10.1
npmをインストール
Node.jsで書かれたツールを管理するnpmというパッケージ管理ツールをインストールします。Rubyでいうとgemのようなやつです。 プロジェクト用のディレクトリを作成し、ターミナルでその中へ移動します。今回はpractice/es2015devというディレクトリを作成しました。
$ cd practice/es2015dev
移動したら以下のコマンドでpackage.json
を作成します。
対話形式で任意の項目を入力します。今回は全てEnterキーを押しました。
$ npm init ... name: (es2015dev) version: (1.0.0) description: entry point: (index.js) test command: git repository: keywords: author: license: (ISC) About to write to /Users/XXXX/practice/es2015dev/package.json: { "name": "es2015dev", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Is this ok? (yes) yes
入力を終えると、ディレクトリ内にpackage.json
が作成され、中には上の入力内容が記載されています。
Gulp
Gulpとは
様々な作業を自動化できる「タスクランナー」と呼ばれるツールです。Web制作においては、css,js,画像などの圧縮、Sassのコンパイル、ベンダープレフィックスの付与、LiveReloadなどを全て高速に行ってくれます。
インストール
以下のコマンドでGulpをインストールします。-gはグローバル領域にインストールするコマンドです。
$ npm install gulp -g
--save-devコマンドでローカルにもインストールします。
$ npm install gulp --save-dev
するとpackage.json
にGulpが記載されます。
"devDependencies": { "gulp": "^3.9.1" }
Gulpがグローバルとローカル両方に入っているか確認します。
$ gulp -v [22:01:40] CLI version 3.9.1 [22:01:40] Local version 3.9.1
Babelをインストール
ES2016をES5記法に変換するBabelをローカルにインストールします。
$ npm install babel babel-preset-es2015 gulp-babel --save-dev
.babelrc
ファイルを作成し、利用するpresetsを記述します。
{ "presets": ["es2015"] }
Gulpの設定ファイルをpackage.json
と同じディレクトリに作成します。
Gulpはバージョン3.9.0からBabelを標準サポートするようになりました。設定ファイルの名前をgulp.js
ではなくgulp.babel.js
にすることで、ES2015で設定ファイルを記述できるようになります。
'use strict'; const gulp = require('gulp'); const babel = require('gulp-babel'); // タスクの登録 gulp.task('js', () => gulp.src('src/js/**/*.js') // 読み込むファイル .pipe(babel()) // babelを実行 .pipe(gulp.dest('dist/js')) // 出力先 );
src/jsディレクトリを作成し、その中にES2015で記述したjsファイルを作成します。
const obj = { add: (a,b) => a + b };
準備ができたので、gulpを実行します。
$ gulp js [22:18:57] Requiring external module babel-register [22:18:57] Using gulpfile ~/practice/es2015dev/gulpfile.babel.js [22:18:57] Starting 'js'... [22:18:57] Finished 'js' after 107 ms
/dist/jsディレクトリにES5変換後のjsファイルが生成されました。
"use strict"; var obj = { add: function add(a, b) { return a + b; } };
ES2015(ECMAScript2015)の文法を覚える
JavaScriptについての知識は初心者から抜け出せない程度のレベルですが、フロントエンド界隈では現在キャッチアップする価値がある技術の1つとしてES2015が挙げられていたので、勉強したときのメモです。 ES2015の仕様全ては理解しきれていないので、今回は使用頻度の高そうな文法に絞って書いています。
参考:
ES2015とは
ES2015とは、JavaScriptが採用している標準化された言語仕様、ECMAScriptの第6版です。ES2015は2015年6月に発行され、現在(2016年4月)最新バージョンのブラウザでも全ての機能を実装できていないこともあり、Babel等のトランスパイラを用いてES5に変換して使用する方法が一般的です。変換方法については次回の記事で書きます。
letとconst
varのように変数宣言に使用します。上書きリスクが減ることから、今後はletとconstを主に使いそうです。 それぞれの違いは、
再宣言 | 再代入 | スコープ | 巻き上げ | |
---|---|---|---|---|
var | できる | できる | 関数スコープ | あり |
let | できない | できる | ブロックスコープ | なし |
const | できない | できない | ブロックスコープ | なし |
var a = 0; // var宣言 let b = 1; // let宣言 const c = 2; // const宣言 // const d; // constは宣言のみはできない var a = 3; // varは再宣言できる // let b = 4; // letは再宣言できない // const c = 5; // constは再宣言できない a = 6; // varは再代入できる b = 7; // letは再代入できる // c = 8; // constは再代入できない { var a = 9; // {}外で宣言したaを上書きする let b = 10; // {}内でのみ有効なbを宣言する const c = 11; // {}内でのみ有効なcを宣言する console.log(a); // 9 console.log(b); // 10 console.log(c); // 11 } console.log(a); // 9 console.log(b); // 7 console.log(c); // 2
function以外の中括弧(ここではif)でもスコープが切れる。
// 今まで function foo() { var bar = 1; if (true) { var bar = 2; } console.log(bar); } foo(); // 2 ifでスコープが切られないので上書きされる // ES2015 function foo2() { let bar = 1; if (true) { let bar = 2; } console.log(bar); } foo2(); // 1 ifでスコープが切られるので上書きされない
巻き上げ(宣言よりも前で変数を参照すること)が発生しない。
// 今まで console.log(e); // undefined 宣言前で変数を参照できてしまう var e = 'hoge'; // ES2015 // console.log(f); // エラー // let f = 'hoge';
for内でsetTimeoutが含まれる場合でも正しく動作する。
// 今まで for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // 3が3回表示されてしまう }, 1000); } // ES2015 for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // 0,1,2 と表示される }, 1000); }
アロー関数
function宣言を() =>という記述に省略できます。
// 今まで var sum = function(a, b) { return a + b; }; // ES2015 const sum = (a, b) => { return a + b; }; // 関数本体が式だけで表せる場合、{}とreturnを省略できる const sum = (a, b) => a + b; // 引数が1つの場合、()を省略できる const func = a => a * a;
さらに、アロー関数を使うとthisは自動的に補足されるため、setTimeout関数の引数に指定された無名関数の内部でthisがグローバルオブジェクトを指してしまう問題を回避することができます。
// 今まで var takashi = { name: 'Takashi', sayHello: function() { var self = this; // thisをselfに保存しておく setTimeout(function() { // thisはグローバルオブジェクトを指す console.log("Hello, I'm " + self.name); }, 1000); } } takashi.sayHello(); // Hello, I'm Takashi // ES2015 const takashi = { name: 'Takashi', sayHello() { // functionを省略できる(メソッド定義記法) setTimeout(() => { // アロー関数を使うとthisはtakashiを指す console.log("Hello, I'm " + this.name); }, 1000); } } takashi.sayHello(); // Hello, I'm Takashi
クラス
これまでJavaScriptにはクラスという概念がなく、prototypeを継承するなどして冗長な記述でクラスを定義していましたが、ES2015でクラスは正式な文法となりました。
// クラスの定義 class Person { constructor(name) { // コンストラクタ this.name = name; // プロパティ } sayHello() { // インスタンスメソッド console.log("Hello, I'm " + this.name); } static create(name) { // スタティックメソッド return new Person(name); } } // インスタンスの生成 const saki = new Person('Saki'); saki.sayHello(); // Hello, I'm Saki // スタティックメソッドの呼び出し const kota = Person.create('Kota');
クラスの継承はextendsを使い、継承元のコンストラクタやメソッドを呼び出すためにはsuperを使います。
// クラスの継承 class Teacher extends Person { constructor(name, age) { super(name); // 継承元のコンストラクタを呼び出す this.age = age; } sayHello() { super.sayHello(); // 継承元のメソッドを呼び出す console.log(this.age + ' years old'); } static create(name, age) { // スタティックメソッドの上書き return new Person(name, age); } } const ellen = new Teacher('Ellen', '28'); ellen.sayHello();
引数の拡張
関数に指定する引数のデフォルト値を設定することができるようになりました。
const sum = (a = 1, b = 2) => { return a + b; }; console.log(sum()); // 3 console.log(sum(2)); // 4 console.log(sum(2,3)) // 5
さらに可変長の引数を取ることができるようになりました。(レストパラメータ)
const foo = (a,b,...rest) => { console.log('a:', a); console.log('b:', b); console.log('rest:', rest); }; foo(1,2,3,4,5); // a: 1 // b: 2 // rest: [3, 4, 5]
オブジェクト記法の拡張
様々な省略記法が可能になりました。 - オブジェクトのプロパティのキー名と値の変数名が同じ場合の省略記法
var foo = 1; // 今まで var obj = {'foo': foo}; // ES2015 const obj = {foo};
- プロパティのキー名が変数に入った文字列である場合の省略記法
var key = 'foo'; // 今まで var obj = {}; obj[key] = 1; console.log(obj.foo); // 1 // ES2015 const obj = { [key]: 1 }; console.log(obj.foo); // 1
- オブジェクトのメソッド定義の省略記法
// 今まで var obj = { add: function(a,b) { return a + b; } }; console.log(obj.add(1,2)); // 3 // ES2015 const obj = { add(a,b) { return a + b; } }; console.log(obj.add(1,2)); // 3 // ES2015 アロー関数を使う場合 const obj = { add: (a,b) => a + b }; console.log(obj.add(1,2)); // 3
展開演算子(スプレッドオペレータ)
可変長の引数を配列として受け取るレストパラメータに対して、配列を複数の引数に展開する機能がスプレッドオペレータです。
var arr = [1, 3, 2]; // 今まで var max = Math.max.apply(null, arr); console.log(max); // 3 // ES2015 const max2 = Math.max(...arr); console.log(max2); // 3
分割代入
分割代入は、配列やオブジェクトからデータを抽出できる機能です。
// 配列の分割代入 const week = ['月', '火', '水', '木', '金', '土', '日',]; const [a, b, c, d, e, f, g] = week; console.log(a, b, c, d, e, f, g); // 月 火 水 木 金 土 日 // 2番目と5番目の値だけを代入 const [,b,,,e] = week; console.log(b, e); // 火 金 // レストパラメータの活用 const [a, b,...cdefg] = week; console.log(a, b, cdefg); // 月 火 ["水", "木", "金", "土", "日"] // 値の交換 let a = 1, b = 2; [a, b] = [b, a]; console.log(a, b); // 2 1 // オブジェクトの分割代入 const obj = { name: 'たかし', age: 15 }; const {name: a, age: b} = obj; console.log(a, b); // たかし 15 // オブジェクト代入のプロパティ省略記法 const {name, age} = obj; console.log(name, age); // たかし 15 // デフォルトの値の指定 const {name, age = 15} = {name: 'たかし'}; console.log(name, age); // たかし 15
イテレータ
イテレータとは、集合から要素を繰り返し取り出すためのオブジェクトです。現在の位置を記録しつつ、中の要素へ1つずつアクセスするnextメソッドを持っています。
const arr = [1, 2, 3]; const iter = arr[Symbol.iterator](); console.log(iter.next()); // {value: 1, done: false} console.log(iter.next()); // {value: 2, done: false} console.log(iter.next()); // {value: 3, done: false} console.log(iter.next()); // {value: undefined, done: true}
for of文
反復処理可能なオブジェクトの値を列挙することができます。for inはプロパティ名を取得するのに対して、for ofはプロパティの値を取得します。
const arr = [3, 4, 5]; arr.foo = 'hoge'; console.log(arr); for (let a in arr) { console.log(a); // 0 1 2 foo } for (let b of arr) { console.log(b); // 3 4 5 }