3章　型、値、変数

# 3.0 概要
JavaScriptの型には大きく分けて以下の２つがある。

## 型

* 基本型
 * 数値
 * 文字列
 * 論理値
 * null, undefined
* オブジェクト型
 * 上記以外のものは全てオブジェクト型 


## 関数
特殊なオブジェクト型として関数がある。関数自体が値を持ち、通常のオブジェクトと同じように操作できる。

## コンストラクタ
new演算子とともに、新たに生成されるオブジェクトを初期化するために使われる関数をコンストラクタという。(rubyでいうinitializeメソッド)

## オブジェクト指向言語
JavaScriptはオブジェクト指向言語。グローバルに定義された関数を使ってさまざまな型の値を処理するのではなく、その型で定義されたメソッドを使って値を処理する。
メソッドを持てるのはオブジェクトのみだが、基本型の数値、文字列、論理値もメソッドを持つような振る舞いができる。

# 3.1 数値
JavaScriptでは整数と浮動小数点数を区別しない。すべての数値を浮動小数点数で表す。JavaScriptプログラム内に直接記述されている数字を数値リテラルという。

## 算術演算
加算、減算、乗算、除算、除余といった基本的なもの以外にも複雑な数学演算を行う関数をもつMathオブジェクトが提供されている。

例)

```javascript
Math.pow(2, 53)   // => 2の53乗
Math.round(0.6)   // => 1.0: もっとも近い整数
Math.ceil(0.3)    // => 1.0: 整数に切り上げ
Math.floor(0.7)   // => 0.0: 整数に切り捨て
Math.abs(-5)      // => 5: 絶対値
などなど
```

JavaScriptでは、算術演算中にオーバーフローやアンダーフローが発生した場合や０除算を行った場合でもエラーが発生しない。

* オーバーフローが発生
 * 無限大を表す「Infinity」を返す
* アンダーフローが発生
 * 「0」を返す 

ただし、0を0で割ったり、負の値の平方根を求めた時など、数値にできない算術演算をおこなったときは、NaN(Not a Number)エラーが発生する。

## 日時
日付と時刻を表現するDateオブジェクトが用意されている。

```javascript
var now = new Date();                           // Sat Jan 09 2016 11:41:26 GMT+0900 (JST)
var later = new Date(2015, 12, 31, 23, 00, 00)  // 2015/12/31 23:00:00
var elapsed = now - later;  // Date型の減算、ミリ秒単位
now.getFullYear()           // => 2016
now.getMonth()              // => 0: 1月を0とする
now.getDate()               // => 9: 日にち
now.getDay()                // => 6: 曜日、0が日曜、6が土曜
now.getHours()              // => 11: 時間
などなど
```

# 3.2 テキスト
## 文字列リテラル
ECMAScript5から文字列を複数行に分割して書けるようになった。分割する場合は各行の末尾にバックスラッシュ「\」を記述する。

```javascript
"one\
two\
three"
```

## 文字列操作
文字列を連結するときは「+」演算子を使う。

```javascript
var msg = 'Hello,' + 'JavaScript';
var name = 'JavaScript';
var greeting = 'Helo,' + name;
```

その他にも文字列に対して何かしらの操作をするメソッドが用意されている。

```javascript
var str = "Hello, World!";
str.charAt(0)        // => H
str.substring(1, 4)  // => ell: 2番目から4番目までの文字
str.length           // => 13: 文字列の長さ
str.replace("H","h") // => 文字列を置き換える
などなど
```
他に正規表現を扱うRegExpオブジェクトなどもある。

# 3.3 論理値
論理値には「true」と「false」がある。
JavaScriptの値はすべて論理値に変換できる。

falseに変換されるもの
* undefined
* null
* 0
* -0
* NaN
* "" // 空文字列

上記以外のものは全てtrueとして扱われる。

# 3.4 nullとundefined
nullはrubyでいうnilと同じ。オブジェクトが存在しないことを意味する。
undefined(未定義値)は値が存在しないことを意味する。
例えば、
* 初期化されていない変数の値
* 存在しないオブジェクトプロパティや配列の要素
* 戻り値のない関数が返す値もundefined


nullとundefinedは等値演算子「==」では等しい(true)と判定される。

# 3.5 グローバルオブジェクト
グローバルオブジェクトのプロパティはグローバルに定義された名前となり、JavaScriptプログラム中のどこからでも利用できる。

JavaScriptインタプリタが起動したとき、Webブラウザが新しいページを読み込んだときに以下のようなプロパティ群をもつように初期化される。

* グローバルプロパティ: undefined, Infinity, NaN
* グローバル関数: isNaN(), parseInt(), eval()
* コンストラクタ関数: Date(), RegExp(), Object(), Array()
* グローバルオブジェクト: MathとJSON

トップレベルコード(関数の一部でないJavaScriptコード)では、JavaScriptのthisキーワードを使ってグローバルオブジェクトを参照できる。

```javascript
var global = this;
```

クライアントサイドJavaScriptの場合には、Windowオブジェクトにグローバルプロパティやグローバル関数などが定義される。Windowオブジェクトには自己を参照するwindowプロパティが用意されている。

# 3.6 ラッパーオブジェクト
JavaScriptのオブジェクトは複合型の値
=> プロパティ(名前付きの値)の集合体、プロパティの値を参照するには「.」を使う。

プロパティが関数の場合は、メソッドと呼ぶ。

```javascript
var str = "hello, world!";
var word = str.substring(str.indexOf(" ")+1, str.length);  // world!
```

文字列はオブジェクトではないのに、上記のようにメソッドを呼べる理由は、
文字列strのプロパティを参照すると、文字列値をオブジェクトに変換する。new String(str)を呼び出すのと同じ変換を行う。
このオブジェクトは、文字列用のメソッドを継承するので、このオブジェクトを使ってプロパティを参照できる。プロパティにアクセスできたら、新たに生成されたオブジェクトは破棄される。
文字列と同じ理由で、数値や論理値もメソッドを持つ。

このように文字列や数値、論理値のプロパティにアクセスするときに生成される一時的なオブジェクトをラッパーオブジェクトという。

# 3.7 不変な基本型、可変なオブジェクト参照
## 不変な基本型
基本型(undefined、null、論理値、数値、文字列)は不変。これは基本型値を変更する方法がないから。

```javascript
var s = "hello";
s.toUpperCase();  // => HELLO
s                 // => hello
```
上記の例のように、文字列を変更しているのではなく、新たな文字列を返しているだけ。

また、基本型は値で比較する。
=> 2つの基本型値が同じ値をもつ場合のみ、2つの値は等しいと判定される。

## 可変なオブジェクト
これに対して、オブジェクトは可変。値を変更できる。

```javascript
var o = { x: 1};
o.x = 2;
o.y = 1;

var a = [1, 2, 3];
a[0] = 10;
a[3] = 4;
```
オブジェクトは値では比較しない。同じプロパティを持って、プロパティの値が同じだったとしても異なるオブジェクトとして判定される。

```javascript
var o = {x:1}, p = {x:1};
o === p  // => false
var a = [], b = [];
a === b  // => false
```

オブジェクトは基本型と区別するために参照型と言われることもある。2つのオブジェクト値は、両方が同じオブジェクトを参照している場合のみ同一と判定される。

```javascript
var a = [];
var b = a;
b[0] = 100;
a[0]        // => 100
a === b     // => true
```
この例のように変数にオブジェクトを代入することは、単に参照を代入しているだけで、新たなオブジェクトのコピーを作っているわけではない。

もし、2つのオブジェクトを比較しながら何かしらの処理を行いたい場合は、プロパティや要素を比較する必要がある。

```javascript
function equalArrays(a, b) {
  if (a.length != b.length) return false;
  for (var i = 0; i < a.length; i++)
    if (a[i] !== b[i]) return false;
  return true;
}
```

# 3.8 型の変換
JavaScriptは必要とする値の型について柔軟な対応をとる。
こんな感じ↓

```javascript
10 + "string"    // => "10string"
"2" * "3"        // => 6
var n = 1 - "x"; // => NaN: Not a Number
n + "objects"    // => "NaNobjects"
```
文字列"x"は数値に変換できないのでNaNに変換される。

## 3.8.1 変換と比較
等値演算子「==」でも等しいかどうかの判定が柔軟に行われる。

```javascript
null == undefined   // => true
"0" == 0            // => true
0 == false          // => true
"0" == false        // => true
"" == false         // => true
"" == 0             // => true
```

## 3.8.2 明示的な型変換
明示的に型変換を行うには、Boolean(), Number(), String(), Object()関数を使う。

```javascript
Number("3")    // => 3
String(false)  // => "false"
Boolean([])    // => true
Object(3)      // => new Number(3)
```

## 3.8.3 オブジェクトから基本型への変換
すべてのオブジェクトは以下の2つの変換メソッドをもつ。

* toString()
* valueOf()


### toString()
toString()メソッドはオブジェクトを表す文字列を返す。

```javascript
var obj = {x: 1, y: 2};
obj.toString()          // => [object Object]

var array = ["javascript", "node.js", "AngularJS"];
array.toString()        // => "javascript,node.js,AngularJS"

new Date(2016, 1, 12).toString() // => "Fri Feb 12 2016 00:00:00 GMT+0900 (JST)"
```

### valueOf()
toString()ほどはっきりと定義されていないが、基本的には、オブジェクトを表す基本型値に変換するメソッド。オブジェクトは複合型の値なので、実際には大半のオブジェクトは1つの基本型で表すことができない。そのため、デフォルトのvalueOf()メソッドは基本型の値を返すのではなく、単純にオブジェクトそのものを返す。

例えば、オブジェクトを文字列に変換する場合、次のような手順をとる。

* オブジェクトがtoString()メソッドをもつ場合、toString()メソッドを呼び出す。このとき、基本型値が返されたら、JavaScriptはその値を文字列に変換し、文字列を返す。
* オブジェクトにtoString()メソッドが定義されていない場合や基本型値が返されてこなかった場合は、valueOf()メソッドが定義されているかを調べ、存在する場合はvalueOf()メソッドを呼び出す。戻り値が基本型の場合は、この値を文字列に変換して、値を返す。
* 上記以外の場合は、例外TypeErrorをスローする。

オブジェクトを数値に変換する場合は以下の手順をふむ。

* オブジェクトがvalueOf()メソッドを持ち、基本型値を返すとき、返された値を数値に変換する。
* 上記以外の場合、オブジェクトが基本型値を返すtoString()メソッドを持つなら、型変換を行い、その値を返す。
* 上記以外の場合は、例外TypeErrorをスローする。

# 3.9 変数の宣言
変数を宣言するときはキーワード「var」を使う。

```javascript
var i;
var sum;

var i, sum;

var message = "hello";
var i = 0, j = 0, k = 1;
```

var文で変数の初期値を設定しなかった場合は、宣言された変数は何か値が代入されるまでundefined(未定義値)になる。

# 3.10 変数のスコープ

* グローバル変数 => プログラム全体
* ローカル変数 => 変数が宣言された関数の中だけ

関数の中にグローバル変数と同じ名前のローカル変数があった場合は、ローカル変数が優先される。

```javascript
var scope = "global";
function checkscope() {
  var scope = "local";
  return scope;
}
checkscope() // => "local"
scope        // => "global"
```

* グローバル変数の場合は、var文を省略することができる
* 入れ子にした関数の場合も考え方は同じで、それぞれの関数ごとに独自のローカルスコープをもつ

## 3.10.1 関数のスコープとホスティング
JavaScriptでは関数スコープを使っている。

* 変数は、その変数が定義された関数とその関数に入れ子にされている関数内からアクセスできる
* 関数内で宣言された変数は全て関数全体からアクセスできる
* 変数宣言よりも前のコードからもアクセスできる
	* ホスティング(巻き上げ)といわれる
	* 宣言と同時に行われる代入についてはホスティングされない

1. 関数全体からアクセスできる例

```javascript
function test(o) {
  var i = 0;                           // i は関数全体で定義
  if (typeof o == "object") {
    var j = 0;                         // j はブロック内だけでなく、関数全体で定義
    for (var k = 0; k < 10; k++) {     // k はループ内だけでなく、関数全体で定義
      console.log(k);                  // 0~9まで出力
    }
    console.log(k);                    // 10が出力される
  }
  console.log(j);                      // j は定義されているが、初期化されていない場合がある
}
```

2. ホスティングの例

```javascript
var scope = "global";
function f() {
  console.log(scope);  // => ローカル変数の定義はホスティングによって関数全体で有効なので"undefined"が出力。"global"ではない
  var scope = "local"; // => 変数はここで初期化される
  console.log(scope);  // => "local"を出力
}
```

## 3.10.2 プロパティとしての変数
JavaScriptのグローバル変数を宣言するということは、実際にはグローバルオブジェクトのプロパティを定義することである。
varを使って変数宣言をすると、作成されるプロパティは再定義負荷になる。これは、delete演算子を使って削除できないという意味。

非strictモードでは宣言していない変数に対して値を代入すると自動的にグローバル変数を生成する。このような方法で生成された変数はグローバルオブジェクトの通常の再定義可能なプロパティとして生成されるので、削除することができる。

```javascript
var truevar = 1;
this.truevar          // => 1: グローバルオブジェクトのプロパティ
fakevar = 2;
this.fakevar2 = 3;
delete truevar        // => false: 削除されない
delete fakevar        // => true: 削除される
delete this.fakevar2  // => true: 削除される
```