深層フロントエンド

第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表層

  • 685