深層フロントエンド
第2層::JavaScript表層
JavaScriptの言語仕様と振る舞いについて
プリミティブ
§1 プリミティブ
JSが持つデータの種類
プリミティブデータ型
String Number Boolean Symbol
null undefined
複合型/参照型
Object (Array)
§1 プリミティブ
プリミティブデータ型
これ以上分解できないデータの形式
String Number Boolean Symbol
null undefined
§1 プリミティブ
複合型
複数の値を持つことのできるデータ型
Object (Array)
§1 プリミティブ
var obj = {
foo: 100,
bar: false,
baz: 'oro',
}
参照型
値そのものではなく
値への参照をもつデータ型
Object (Array)
・代入式は値ではなく
参照を複製する
・一方を書き換えると
(同じものを参照しているので)
他方も書き換わる
§1 プリミティブ
var obj_1 = {
foo: 100,
bar: false,
baz: 'oro',
}
var obj_2 = obj_1
obj_2.foo = 200
obj_1.foo == 200
第一級関数
§2 第一級関数
関数
引数、本体、戻り値 からなるひとまとまりの処理
数学でいう関数(写像)
・引数がないもの
・戻り値がないもの
・副作用のあるもの
§2 第一級関数
function foo (arg) {
return arg + 1
}
第一級関数
第一級であるということ
・無名で表現可能である
・変数や複合型のデータに格納できる
・関数の引数や戻り値として扱える
JSの関数は
第一級関数である。
§2 第一級関数
var bar = function (anotherFunc) {
return anotherFunc()
}
スコープと
クロージャ
§3 スコープとクロージャ
スコープ
変数の有効範囲のこと
関数(またはブロック)でスコープが区切られる
JSは静的スコープ
→処理の書かれた場所を基準に
スコープが決定される
§3 スコープとクロージャ
var foo = 100
console.log(foo, bar)
function func () {
var bar = true
console.log(foo, bar)
}
クロージャ
JSの関数は、変数に格納したり即時関数として扱ったとき
「クロージャ」となる
クロージャ = 関数そのものにスコープを覚えさせる
§3 スコープとクロージャ
スコープを連れ回す
barはクロージャとなり、
barがどれだけ遠くに行っても
4行目のfooは1行目のfooを指す
そもそもJSは
第一級関数の言語なので
クロージャの存在が見えづらい
§3 スコープとクロージャ
var foo = 100
var bar = function () {
console.log(foo)
}
...
...
bar()
プロトタイプ
チェーン
§4 プロトタイプチェーン
プロトタイプベース
JSは「プロトタイプベースのオブジェクト指向」
JSのすべてのオブジェクトは、
他のオブジェクトをプロトタイプとして持つ
§4 プロトタイプチェーン
Object.create()
プロトタイプとなるオブジェクトを指定して、
新しく空のオブジェクトを作る
§4 プロトタイプチェーン
var foo_1 = {}
var foo_2 = Object.create({})
var bar_1 = { num: 100 }
var bar_2 = Object.create({})
bar_2.num = 100
プロトタイプチェーン
barに存在しないものは
プロトタイプを参照する
共通のプロパティを持たせたい
→同じオブジェクトを
プロトタイプとして実現
§4 プロトタイプチェーン
var foo = { num: 100 }
Object.keys(foo) // ['num']
foo.num // 100
var bar = Object.create(foo)
Object.keys(bar) // []
bar.num // 100
bar.num = 200
§4 プロトタイプチェーン
base
Object.create(base)
Object.create(base)
Object.create(bar)
bar
foo
baz
クラス
§5 クラス
クラスベース
「クラスベースのオブジェクト指向」
すべてのオブジェクトは、クラスをもとに作成される
を、プロトタイプで真似することができる
§5 クラス
クラス
オブジェクトの設計図
=どんなプロパティを持つ
どんなメソッドを持つ
§5 クラス
class Foo {
constructor () {
this.val = 100
}
bar () {
return 'baz'
}
}
var foo = new Foo()
foo.val // 100
foo.bar() // 'baz'
クラスのマネごと
§5 クラス
function Foo {
this.val = 100
}
Foo.prototype.bar = function () {
return 'baz'
}
var foo = new Foo()
foo.val // 100
foo.bar() // 'baz'
class Foo {
constructor () {
this.val = 100
}
bar () {
return 'baz'
}
}
var foo = new Foo()
foo.val // 100
foo.bar() // 'baz'
プロトタイプを引きずるクラス
§5 クラス
class〜〜と書いても、その中身は
プロトタイプチェーンを使ったクラスのマネごとでしかない!
でもプロトタイプチェーンを乱用するとつらくなるので
class構文で書いたほうが良いよ。きっとね。
this
§6 this
thisとは何か
this === obj
§6 this
var obj = {
foo: 100,
bar: function () {
return this.foo * 2
}
}
obj.foo // 100
obj.bar() // 200
obj.foo = 200
obj.bar() // 400
thisとは何か(2)
this === obj
§6 this
var obj = {
foo: 100,
}
obj.bar = function () {
return this.foo * 2
}
obj.foo // 100
obj.bar() // 200
obj.foo = 200
obj.bar() // 400
thisとは何か(3)
"obj.bar"
と書いたときの
"obj"
を指す
§6 this
var obj = {
foo: 100,
bar: function () {
return this.foo * 2
}
}
var func = obj.bar
func() // Error!
プロトタイプとthis
objにfooを追加
objにbarは無いので
baseのbarを呼ぶ
thisはobjとして計算
クラスの実現方法が
想像できますか?
§6 this
var base = {
bar: function () {
return this.foo * 2
}
}
var obj = Object.create(base)
obj.foo = 100
obj.bar() // 200
関数とthis
§7 関数とthis
.bind()
「thisをバインドする」
=関数内のthisを指定する
§7 関数とthis
var obj = {
foo: 100,
}
function bar () {
return this.foo * 2
}
bar() // Error!
var bound = bar.bind(obj)
bound() // 200
thisをすげ替える
「thisをバインドする」
=関数内のthisを指定する
§7 関数とthis
var obj_1 = {
foo: 100,
bar: function () {
return this.foo * 2
}
}
var obj_2 = {
foo: 200,
}
var bound = obj_1.bar.bind(obj_2)
bound() // 400
アロー関数
そのスコープのthisを
自身のthisとする関数
() => { ... }
(function () { ... }).bind(this)
これらは等しい
thisが関係ないなら関係ない
§7 関数とthis
var obj = {
foo: 100,
bar: function () {
return () => {
return this.foo * 2
}
}
}
var func = obj.bar()
func() // 200
var bound = func.bind(/* ... */)
bound() // 200
.call() / .apply()
thisをbindした上で
関数を実行
§7 関数とthis
var obj = {
foo: 100,
}
function bar (a, b) {
return this.foo + a + b
}
bar.call(obj, 20, 4) // 124
bar.apply(obj, [20, 4]) // 124
Promise
§8 Promise
Promiseパターン
非同期処理/非同期IOの表現方法の1つ
状態が変化する
§8 Promise
Pending
Resolved
Rejected
new Promise()
1秒後に成功するPromise
setTimeoutにより、関数resolveは1000ミリ秒後に実行される
.then(...) は
関数resolveが
呼ばれたとき
§8 Promise
Fulfilled
var p = new Promise((resolve) => {
setTimeout(resolve, 1000)
})
p.then(() => {
console.log('fulfilled!')
})
1秒後に失敗するPromise
setTimeoutにより、関数rejectは1000ミリ秒後に実行される
.catch(...) は
関数rejectが
呼ばれたとき
§8 Promise
Fulfilled
var p = new Promise((resolve, reject) => {
setTimeout(reject, 1000)
})
p.catch(() => {
console.log('rejected!')
})
値をもって成功/失敗する
resolve, rejectには値を1つ渡せる
§8 Promise
Fulfilled
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo')
}, 1000)
})
p.then((result) => {
console.log(result) // foo
})
非同期I/Oをあらわす
HTTP通信のライブラリaxios
§8 Promise
var p = axios.get('https://...')
p.then((data) => {
// on succeeded
}).catch((error) => {
// on failed
})
Promiseと実行順序
”非同期”である
ということが
想像できますか?
§8 Promise
Fulfilled
console.log('A')
var p = new Promise((resolve) => {
console.log('B')
setTimeout(resolve, 1000)
console.log('C')
})
p.then(() => {
console.log('fulfilled!')
})
console.log('D')
A
B
C
D
fulfilled!
async
functions
§9 async functions
async
asyncキーワードと共に関数を宣言する
async関数はPromiseを
返す。
ちなみにアロー関数は
async () => ...
§9 async functions
async function foo () {
return 'bar'
}
var p = foo()
p.then((result) => {
console.log(result) // foo
})
await
async関数の中でawaitが使えるようになる
awaitは「Promiseのresolveを待つ」
待つ?同期?
§9 async functions
async function foo () {
var data = await axios.get('http://...')
return data
}
async/awaitと実行順序
async関数の
振る舞いに注意。
§9 async functions
async function foo () {
console.log('A')
var data = await axios.get('http://...')
console.log('B')
return data
}
console.log('C')
foo()
console.log('D')
C
A
D
B
深層フロントエンド
第2層::JavaScript表層
つづく
より詳しい現代JavaScriptの教科書→ https://jsprimer.net/
深層フロントエンド 第2層 JavaScript表層
By Shigeki Suwa
深層フロントエンド 第2層 JavaScript表層
- 1,110