回答編集履歴
6
「Undefined 型と Null 型」の説明が ES6 に準拠したものではなかった為、修正
answer
CHANGED
@@ -168,29 +168,28 @@
|
|
168
168
|
|
169
169
|
### Undefined 型と Null 型
|
170
170
|
|
171
|
-
この節は私の個人的な考え(イメージ)なので戯言とでも思って読んでください(前述の通り、私は初心者にイメージで語るべきではないと考えている為、この節は非初心者向けです)。
|
172
|
-
|
171
|
+
ECMAScript 6 では `undefined`, `null` 値を次のように定義しています。
|
173
172
|
|
174
|
-
```JavaScript
|
175
|
-
(function () {
|
176
|
-
|
173
|
+
- [4.3.10 undefined value – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-undefined-value)
|
174
|
+
- [4.3.12 null value – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-null-value)
|
177
175
|
|
176
|
+
> **4.3.10 undefined 値**
|
177
|
+
>
|
178
|
+
> 変数に値が割り当てられる前に使われるプリミティブ値。
|
179
|
+
>
|
180
|
+
> **4.3.12 null 値**
|
181
|
+
>
|
178
|
-
|
182
|
+
> 任意のオブジェクト値が意図的に存在しないことを表すプリミティブ値。
|
179
183
|
|
184
|
+
`getElementById` は存在しない id を指定した場合、対象のオブジェクト(要素ノード)が存在しない事を表す `null` を返します。
|
180
|
-
|
185
|
+
`document.onclick` は初期値としてオブジェクト(イベントハンドラ関数)が存在しない事を表す `null` を持ちます。
|
181
|
-
|
186
|
+
**(2016/05/04 23:15 追記)** この節には別の説明を書いていましたが、ES6 で適切な説明があった為、修正しました。)
|
182
|
-
```
|
183
187
|
|
184
|
-
- `getElementById` は存在しない id を指定した場合、対象の要素を探し、存在しない事を確認してから返り値を既定値(初期値)の undefined から null に変更して返します。
|
185
|
-
- `document.onclick` は初期値が `null` ですが、`onclick` プロパティを定義したという意味で `null` を代入していると考えられます。(ただし、この考えはちょっと苦しいとも考えています。「プロパティは定義済だが、プロパティ値は未定義のはず」と考える事も可能です。)
|
186
|
-
|
187
|
-
実際のところ、`null` は**Object 型が存在しない時に返す値**とする方が自然なのかもしれません。
|
188
|
-
`NodeList` 等の配列的性質を持つオブジェクトを除き、私の知る範囲ではオブジェクトが存在しない時に `null` を返しています。
|
189
|
-
|
190
188
|
### 更新履歴
|
191
189
|
|
192
190
|
- 2016/05/03 17:02 コード事例を追記
|
193
191
|
- 2016/05/04 22:17 typeof 演算子、「存在しない」を表す値、Undefined 型と Null 型の説明追加
|
194
192
|
- 2016/05/04 22:38 Symbol 型が増えた事による typeof 演算子による Object 型の判定方法の複雑化について説明&コード追記
|
193
|
+
- 2016/05/04 23:15 「Undefined 型と Null 型」の説明が ES6 に準拠したものではなかった為、修正
|
195
194
|
|
196
195
|
Re: a_yanyan さん
|
5
Symbol 型が増えた事による typeof 演算子による Object 型の判定方法の複雑化について説明&コード追記
answer
CHANGED
@@ -66,8 +66,45 @@
|
|
66
66
|
|
67
67
|
- [get-type.js : ECMAScript5 準拠のデータ型を返す](https://gist.github.com/think49/862085/a8146347d959b95832ea6c4c2e82b11c4957c6e8)
|
68
68
|
|
69
|
+
```JavaScript
|
70
|
+
/**
|
71
|
+
* get-type.js
|
72
|
+
*
|
73
|
+
* @version 1.0.2
|
74
|
+
* @author think49
|
75
|
+
* @url https://gist.github.com/862085
|
76
|
+
* @license http://www.opensource.org/licenses/mit-license.php (The MIT License)
|
77
|
+
*/
|
78
|
+
|
79
|
+
function getType (value) {
|
80
|
+
var type, typeofValue;
|
81
|
+
|
69
|
-
|
82
|
+
if (value === null) { // Null Type
|
83
|
+
return 'Null';
|
84
|
+
}
|
85
|
+
|
86
|
+
typeofValue = typeof value;
|
87
|
+
|
88
|
+
switch (typeofValue) {
|
89
|
+
case 'undefined': // Undefined Type
|
90
|
+
case 'boolean': // Boolean Type
|
91
|
+
case 'number': // Number Type
|
92
|
+
case 'string': // String Type
|
93
|
+
type = typeofValue.charAt(0).toUpperCase() + typeofValue.slice(1);
|
94
|
+
break;
|
95
|
+
case 'object': // Object Type (native and does not implement [[Call]])
|
96
|
+
case 'function': // Object Type (native or host and does implement [[Call]])
|
97
|
+
default: // Object Type (host and does not implement [[Call]])
|
98
|
+
type = 'Object';
|
99
|
+
break;
|
100
|
+
}
|
101
|
+
|
102
|
+
return type;
|
103
|
+
}
|
104
|
+
```
|
105
|
+
|
70
|
-
ES5 当時には `typeof` 演算子
|
106
|
+
ES5 当時には `typeof` 演算子が "symbol" を返した場合、それを「Object Type (host and does not implement [[Call]])」と見なして Object 型と判定する動作が正しい動作でした。
|
107
|
+
ところが、ES6 で Symbol 型が増えた事によって `"symbol"` という新しく定義済文字列が出来た為、**上記コードは ES6 準拠の実装において Symbol 型を Object 型と判定してしまいます**。
|
71
108
|
新しい型が増える度にコードを修正するいたちごっこには付き合ってられませんので `ToObject` で Object 型に変換して変換前と照合する事にしました。
|
72
109
|
|
73
110
|
```JavaScript
|
@@ -76,7 +113,7 @@
|
|
76
113
|
}
|
77
114
|
```
|
78
115
|
|
79
|
-
このコードも `ToObject` の動作次第では破綻しますが、「"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列は Object 型である」の判定ロジックよりはマシです。
|
116
|
+
このコードも `ToObject` の動作次第では破綻しますが、「"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列は Object 型である」の判定ロジックに修正するよりはマシです。
|
80
117
|
ES6 では Object 型を判定する関数として `Object.isObject()` の導入が検討されたようですが、残念ながら見送られました。
|
81
118
|
|
82
119
|
- [7.1.13 ToObject ( argument ) – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-toobject)
|
@@ -154,5 +191,6 @@
|
|
154
191
|
|
155
192
|
- 2016/05/03 17:02 コード事例を追記
|
156
193
|
- 2016/05/04 22:17 typeof 演算子、「存在しない」を表す値、Undefined 型と Null 型の説明追加
|
194
|
+
- 2016/05/04 22:38 Symbol 型が増えた事による typeof 演算子による Object 型の判定方法の複雑化について説明&コード追記
|
157
195
|
|
158
196
|
Re: a_yanyan さん
|
4
typeof 演算子、「存在しない」を表す、Undefined 型と Null 型の説明追加
answer
CHANGED
@@ -18,19 +18,101 @@
|
|
18
18
|
下手に固定観念を植え付けると自由な発想でコーディングできなくなります。
|
19
19
|
漠然とした疑問ならまずはコードを書かせてより具体的な疑問点まで昇華してから解消させるべきだと思います。
|
20
20
|
|
21
|
+
### 型の違い
|
22
|
+
|
23
|
+
「型」を認識しているか確認して認識していないようだったら一通りの型を教えてあげてください。
|
24
|
+
JavaScript は「暗黙の型変換」が頻繁に発生する言語なので現在の型を意識しないといろいろとはまります。
|
25
|
+
|
26
|
+
- Undefined 型
|
27
|
+
- Null 型
|
28
|
+
- Boolean 型
|
29
|
+
- String 型
|
30
|
+
- Symbol 型 (ES6 規定)
|
31
|
+
- Number 型
|
32
|
+
- Object 型
|
33
|
+
|
34
|
+
### typeof 演算子
|
35
|
+
|
36
|
+
`typeof` 演算子はその名称から「型を表す文字列を返す演算子」と読み取れますが、実際には型と一致しない文字列を返す場合があります。
|
37
|
+
|
38
|
+
- [12.5.6 The typeof Operator – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-typeof-operator)
|
39
|
+
|
21
40
|
```JavaScript
|
41
|
+
console.log(typeof undefined); // "undefined"
|
42
|
+
console.log(typeof null); // "object" (Null型: typeof 演算子は Null 型に対して "object" を返す仕様バグがあります)
|
43
|
+
console.log(typeof true); // "boolean" (Boolean 型)
|
44
|
+
console.log(typeof 1); // "number" (Number 型)
|
45
|
+
console.log(typeof "sample"); // "string" (String 型)
|
46
|
+
console.log(typeof Symbol()); // "symbol" (Symbol 型: ES6 既定)
|
47
|
+
console.log(typeof {}); // "object" (Object 型: [[Call]] を持たない一般的なオブジェクト)
|
48
|
+
console.log(typeof document); // "object" (Object 型: [[Call]] を持たず、標準的な外来のオブジェクト)
|
49
|
+
console.log(typeof new Function); // "function" (Object 型: [[Call]] を持つオブジェクト)
|
50
|
+
console.log(typeof hoge); // "hoge" (Object 型: 非標準で外来の [[Call]] を持たないオブジェクト。詳細は後述の (*1) を参照。)
|
51
|
+
|
22
52
|
/**
|
23
|
-
*
|
53
|
+
* (*1) Object (non-standard exotic and does not implement [[Call]])
|
54
|
+
* typeof 演算子はこのオブジェクトに対して "undefined", "boolean", "function", "number", "symbol", "string" を返してはならない。
|
55
|
+
* つまり、"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列であれば何を返しても良い。
|
24
56
|
*/
|
25
|
-
console.log(Number('')); // 0 (空文字を Number 型に変換すると 0 を返す)
|
26
|
-
console.log('JavaScript'.lastIndexOf('J', 0)); // 0 (String#lastInddexOf は Number 型の値を返す。String#lastIndexOf の第二引数は Number 型を指定しなければならない。厳密には Number 型を指定しなくても良いが、内部的に Number 型に変換される。)
|
27
|
-
|
57
|
+
```
|
28
|
-
console.log(Object.keys({}).length); // 0 (直属のプロパティを持たないオブジェクトを Object.keys で処理させると length 値が 0 の空の配列を返す)
|
29
|
-
console.log(document.querySelectorAll('.hogehoge').length); // 0 (querySelectorAll はマッチしなかった場合空の NodeList (擬似配列) を返す。この NodeList の length 値は 0 である。)
|
30
58
|
|
59
|
+
typeof null === 'object' は仕様バグだそうですが、今更修正できないのでそのままになってしまっています。
|
60
|
+
|
61
|
+
- [typeof null === 'object' は ECMAScript 3 の仕様バグ - @think49の日記](http://d.hatena.ne.jp/think49/20120114/1326554107)
|
62
|
+
|
63
|
+
関数に対して "function" を返す動作も困りものですが、一番対処に困るのは「Object (non-standard exotic...)」です。
|
64
|
+
このオブジェクトは既定文字列以外の全ての文字列を取りうるので `typeof` 演算子で真面目に Object 型を判定しようとすると「既定文字列以外の全て」を判定する事になります。
|
65
|
+
実際に私は ES5 当時にそのようにコーディングしていました。
|
66
|
+
|
67
|
+
- [get-type.js : ECMAScript5 準拠のデータ型を返す](https://gist.github.com/think49/862085/a8146347d959b95832ea6c4c2e82b11c4957c6e8)
|
68
|
+
|
69
|
+
ところが、このコードは ES6 で Symbol 型が増えた途端に破綻しました。
|
70
|
+
ES5 当時には `typeof` 演算子は "symbol" を返した場合に Object 型と判定する動作が正しい為、**Symbol 型に対してObject 型と判定してしまう**のです。
|
71
|
+
新しい型が増える度にコードを修正するいたちごっこには付き合ってられませんので `ToObject` で Object 型に変換して変換前と照合する事にしました。
|
72
|
+
|
73
|
+
```JavaScript
|
74
|
+
if (Object(arg) === arg) { // ToObject で Object 型に変換する
|
75
|
+
console.log(arg + ' は Object 型である');
|
76
|
+
}
|
77
|
+
```
|
78
|
+
|
79
|
+
このコードも `ToObject` の動作次第では破綻しますが、「"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列は Object 型である」の判定ロジックよりはマシです。
|
80
|
+
ES6 では Object 型を判定する関数として `Object.isObject()` の導入が検討されたようですが、残念ながら見送られました。
|
81
|
+
|
82
|
+
- [7.1.13 ToObject ( argument ) – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-toobject)
|
83
|
+
- [19.1.1.1 Object ( [ value ] ) – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-object-value)
|
84
|
+
|
85
|
+
### 「存在しない」を表す値
|
86
|
+
|
87
|
+
変数や返り値には既定の型があり、「存在しない事を表す値」は型によって変化します。
|
88
|
+
|
89
|
+
```JavaScript
|
31
90
|
/**
|
32
|
-
*
|
91
|
+
* Number 型
|
33
92
|
*/
|
93
|
+
Number('hoge'); // NaN (Number 型へ変換不可能な場合、NaN (Not a Number)を返す、NaN は Number ではない事を表す)
|
94
|
+
''.charCodeAt(); // NaN (存在しない場所の文字を指定した場合、Unicodeコードポイントは NaN となる)
|
95
|
+
'sample'.indexOf('hoge'); // -1 (存在しない index は -1 で表す)
|
96
|
+
console.log([].length); // 0 (空の配列の length 値は 0 である)
|
97
|
+
console.log(Object.keys({}).length); // 0 (直属のプロパティを持たないオブquerySelectorAll は存在しない要素のセレクタを指定した場合、空の NodeList (擬似配列) を返す。この NodeList の length 値は 0 である。)
|
98
|
+
|
99
|
+
/**
|
100
|
+
* String 型
|
101
|
+
*/
|
102
|
+
[].join(); // "" (対象の文字列が存在しない場合、空文字を返す)
|
103
|
+
''.charAt(); // "" (対象の文字列が存在しない場合、空文字を返す)
|
104
|
+
|
105
|
+
/**
|
106
|
+
* Undefined 型
|
107
|
+
*/
|
108
|
+
(function () {}()); // undefined (return 節がない関数は未定義値を返す)
|
109
|
+
var a; // undefined (var 宣言の既定値は未定義値である)
|
110
|
+
void true; // undefined (void 演算子は常に undefined を返す)
|
111
|
+
document.hoge; // undefined (プロパティアクセス演算子は存在しないプロパティを指定した場合、undefined を返す)
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Null 型
|
115
|
+
*/
|
34
116
|
console.log(document.onclick); // null (onclick の既定値は null である)
|
35
117
|
document.onclick = function (event) { console.log(event.type); }; // onclickイベントハンドラを定義する
|
36
118
|
document.onclick = null; // onclickイベントハンドラを削除するには null を代入する
|
@@ -38,35 +120,39 @@
|
|
38
120
|
console.log(/hoge/.exec('test')); // null (RegExp#exec はマッチしなかった場合に null を返す)
|
39
121
|
console.log('test'.match(/hoge/)); // null (String#match はマッチしなかった場合に null を返す)
|
40
122
|
|
123
|
+
/**
|
124
|
+
* Object 型
|
125
|
+
*/
|
126
|
+
new Object; // {} (引数なしで new Object が呼び出された場合、直属のプロパティを持たない空のオブジェクトを返す)
|
127
|
+
new function () {}; // {} (this 値を操作せず、prototype も設定しない関数が new 演算子で呼び出された場合、直属のプロパティを持たない空のオブジェクトを返す)
|
128
|
+
new Array; // [] (引数無しで new Array が呼び出された場合、length 値が 0 の空の配列を返す)
|
129
|
+
document.querySelectorAll('.hoge'); // [] (querySelectorAll は存在しない要素のセレクタを指定した場合、空の NodeList (擬似配列) を返す)
|
130
|
+
```
|
131
|
+
|
132
|
+
### Undefined 型と Null 型
|
133
|
+
|
134
|
+
この節は私の個人的な考え(イメージ)なので戯言とでも思って読んでください(前述の通り、私は初心者にイメージで語るべきではないと考えている為、この節は非初心者向けです)。
|
135
|
+
Undefined 型は未定義ですが、Null 型は処理が終わった後に定義するという意味で「定義済(処理済)」と考える事も出来ます。
|
136
|
+
|
137
|
+
```JavaScript
|
41
138
|
(function () {
|
42
|
-
|
139
|
+
console.log(a); // undefined (未定義)
|
43
|
-
console.log(getThisArg.call(null)); // グローバルオブジェクト (非 Strict Mode で Function#call で第1引数に null が指定された場合の this 値はグローバルオブジェクトである)
|
44
|
-
}());
|
45
140
|
|
46
|
-
(function () {
|
47
|
-
|
141
|
+
var a = null; // 存在しないを表す値として null を定義する
|
142
|
+
|
48
|
-
|
143
|
+
console.log(a); // null (定義済)
|
49
|
-
console.log(getThisArg.call(null)); // null (Strict Mode で Function#call で第1引数に null が指定された場合の this 値は null である)
|
50
144
|
}());
|
51
145
|
```
|
52
146
|
|
53
|
-
|
147
|
+
- `getElementById` は存在しない id を指定した場合、対象の要素を探し、存在しない事を確認してから返り値を既定値(初期値)の undefined から null に変更して返します。
|
148
|
+
- `document.onclick` は初期値が `null` ですが、`onclick` プロパティを定義したという意味で `null` を代入していると考えられます。(ただし、この考えはちょっと苦しいとも考えています。「プロパティは定義済だが、プロパティ値は未定義のはず」と考える事も可能です。)
|
54
149
|
|
55
|
-
|
150
|
+
実際のところ、`null` は**Object 型が存在しない時に返す値**とする方が自然なのかもしれません。
|
151
|
+
`NodeList` 等の配列的性質を持つオブジェクトを除き、私の知る範囲ではオブジェクトが存在しない時に `null` を返しています。
|
56
152
|
|
57
|
-
「型」を認識しているか確認して認識していないようだったら一通りの型を教えてあげてください。
|
58
|
-
JavaScript は「暗黙の型変換」が頻繁に発生する言語なので現在の型を意識しないといろいろとはまります。
|
59
|
-
|
60
|
-
- Undefined 型
|
61
|
-
- Null 型
|
62
|
-
- Boolean 型
|
63
|
-
- String 型
|
64
|
-
- Symbol 型 (ES6 規定)
|
65
|
-
- Number 型
|
66
|
-
- Object 型
|
67
|
-
|
68
153
|
### 更新履歴
|
69
154
|
|
70
155
|
- 2016/05/03 17:02 コード事例を追記
|
156
|
+
- 2016/05/04 22:17 typeof 演算子、「存在しない」を表す値、Undefined 型と Null 型の説明追加
|
71
157
|
|
72
158
|
Re: a_yanyan さん
|
3
誤字修正
answer
CHANGED
@@ -31,7 +31,7 @@
|
|
31
31
|
/**
|
32
32
|
* null の事例
|
33
33
|
*/
|
34
|
-
console.log(document.onclick); // null (onclick の
|
34
|
+
console.log(document.onclick); // null (onclick の既定値は null である)
|
35
35
|
document.onclick = function (event) { console.log(event.type); }; // onclickイベントハンドラを定義する
|
36
36
|
document.onclick = null; // onclickイベントハンドラを削除するには null を代入する
|
37
37
|
console.log(document.getElementById('hogehoge')); // null (getElementById は存在しない要素を検索した場合、null を返す)
|
2
typo修正
answer
CHANGED
@@ -32,7 +32,7 @@
|
|
32
32
|
* null の事例
|
33
33
|
*/
|
34
34
|
console.log(document.onclick); // null (onclick の規定値は null である)
|
35
|
-
document.onclick = function (event) { console.log(event.type); }; // onclickイベント
|
35
|
+
document.onclick = function (event) { console.log(event.type); }; // onclickイベントハンドラを定義する
|
36
36
|
document.onclick = null; // onclickイベントハンドラを削除するには null を代入する
|
37
37
|
console.log(document.getElementById('hogehoge')); // null (getElementById は存在しない要素を検索した場合、null を返す)
|
38
38
|
console.log(/hoge/.exec('test')); // null (RegExp#exec はマッチしなかった場合に null を返す)
|
1
コード事例を追記
answer
CHANGED
@@ -18,6 +18,40 @@
|
|
18
18
|
下手に固定観念を植え付けると自由な発想でコーディングできなくなります。
|
19
19
|
漠然とした疑問ならまずはコードを書かせてより具体的な疑問点まで昇華してから解消させるべきだと思います。
|
20
20
|
|
21
|
+
```JavaScript
|
22
|
+
/**
|
23
|
+
* 0 の事例
|
24
|
+
*/
|
25
|
+
console.log(Number('')); // 0 (空文字を Number 型に変換すると 0 を返す)
|
26
|
+
console.log('JavaScript'.lastIndexOf('J', 0)); // 0 (String#lastInddexOf は Number 型の値を返す。String#lastIndexOf の第二引数は Number 型を指定しなければならない。厳密には Number 型を指定しなくても良いが、内部的に Number 型に変換される。)
|
27
|
+
console.log([].length); // 0 (空の配列の length 値は 0 である)
|
28
|
+
console.log(Object.keys({}).length); // 0 (直属のプロパティを持たないオブジェクトを Object.keys で処理させると length 値が 0 の空の配列を返す)
|
29
|
+
console.log(document.querySelectorAll('.hogehoge').length); // 0 (querySelectorAll はマッチしなかった場合空の NodeList (擬似配列) を返す。この NodeList の length 値は 0 である。)
|
30
|
+
|
31
|
+
/**
|
32
|
+
* null の事例
|
33
|
+
*/
|
34
|
+
console.log(document.onclick); // null (onclick の規定値は null である)
|
35
|
+
document.onclick = function (event) { console.log(event.type); }; // onclickイベントは野良を定義する
|
36
|
+
document.onclick = null; // onclickイベントハンドラを削除するには null を代入する
|
37
|
+
console.log(document.getElementById('hogehoge')); // null (getElementById は存在しない要素を検索した場合、null を返す)
|
38
|
+
console.log(/hoge/.exec('test')); // null (RegExp#exec はマッチしなかった場合に null を返す)
|
39
|
+
console.log('test'.match(/hoge/)); // null (String#match はマッチしなかった場合に null を返す)
|
40
|
+
|
41
|
+
(function () {
|
42
|
+
function getThisArg () { return this; }
|
43
|
+
console.log(getThisArg.call(null)); // グローバルオブジェクト (非 Strict Mode で Function#call で第1引数に null が指定された場合の this 値はグローバルオブジェクトである)
|
44
|
+
}());
|
45
|
+
|
46
|
+
(function () {
|
47
|
+
'use strict';
|
48
|
+
function getThisArg () { return this; }
|
49
|
+
console.log(getThisArg.call(null)); // null (Strict Mode で Function#call で第1引数に null が指定された場合の this 値は null である)
|
50
|
+
}());
|
51
|
+
```
|
52
|
+
|
53
|
+
実際のところ、これらのコードを見せても理解するのは難しいと思いますので、やはり自分からコードを書いてもらって疑問が生まれた時に質問に答えてあげるのが良いと思います。
|
54
|
+
|
21
55
|
### 型の違い
|
22
56
|
|
23
57
|
「型」を認識しているか確認して認識していないようだったら一通りの型を教えてあげてください。
|
@@ -31,4 +65,8 @@
|
|
31
65
|
- Number 型
|
32
66
|
- Object 型
|
33
67
|
|
68
|
+
### 更新履歴
|
69
|
+
|
70
|
+
- 2016/05/03 17:02 コード事例を追記
|
71
|
+
|
34
72
|
Re: a_yanyan さん
|