回答編集履歴

6

「Undefined 型と Null 型」の説明が ES6 に準拠したものではなかった為、修正

2016/05/04 14:16

投稿

think49
think49

スコア18164

test CHANGED
@@ -338,41 +338,37 @@
338
338
 
339
339
 
340
340
 
341
- この節は私の個人的な考え(イメージ)なので戯言とでも思って読んでください(前述の通り、私は初心者にイメージで語るべきではないと考えている為、この節は非初心者向けです)。
342
-
343
- Undefined 型は未定義ですが、Null 型は処理が終わった後に定義するとう意味で「定義済(処理済)」と考える事も出来ます。
341
+ ECMAScript 6 では `undefined`, `null` 値を次のように定義しています。
344
-
345
-
346
-
342
+
343
+
344
+
347
- ```JavaScript
345
+ - [4.3.10 undefined value – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-undefined-value)
348
-
346
+
349
- (function () {
347
+ - [4.3.12 null value – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-null-value)
350
-
348
+
349
+
350
+
351
- console.log(a); // undefined (未定義)
351
+ > **4.3.10 undefined 値**
352
-
353
-
354
-
355
- var a = null; // 存在しないを表す値として null を定義する
352
+
356
-
357
-
358
-
359
- console.log(a); // null (定義済)
360
-
361
- }());
353
+ >
354
+
362
-
355
+ > 変数に値が割り当てられる前に使われるプリミティブ値。
356
+
363
- ```
357
+ >
358
+
364
-
359
+ > **4.3.12 null 値**
360
+
365
-
361
+ >
362
+
366
-
363
+ > 任意のオブジェクト値が意図的に存在しないことを表すプリミティブ値。
364
+
365
+
366
+
367
- - `getElementById` は存在しない id を指定した場合、対象の要素を探し、存在しない事を確認してから返り値を既定値(初期値)の undefined から null に変更して返します。
367
+ `getElementById` は存在しない id を指定した場合、対象のオブジェクト(要素ノード)が存在しない事を表す `null` 返します。
368
-
369
- - `document.onclick` は初期値が `null` ですが、`onclick` プロパティを定義したという意味で `null` を代入していると考えられます。(ただし、この考えはちょっと苦しいとも考えています。「プロパティは定義済だが、プロパティ値は未定義のはず」と考える事も可能です。)
368
+
370
-
371
-
372
-
373
- 実際のところ、`null` は**Object 型が存在しない時に返す値**とする方が自然なのかもしれません。
374
-
375
- `NodeList` 等の配列的性質を持つオブジェクトを除き、私の知る範囲ではオブジェクトが存在しない時に `null` を返しています。
369
+ `document.onclick` は初期値としてオブジェクト(イベントハンドラ関数)が存在しない事を表す `null` を持ちます。
370
+
371
+ **(2016/05/04 23:15 追記)** この節には別の説明を書いていましたが、ES6 で適切な説明があった為、修正しました。)
376
372
 
377
373
 
378
374
 
@@ -386,6 +382,8 @@
386
382
 
387
383
  - 2016/05/04 22:38 Symbol 型が増えた事による typeof 演算子による Object 型の判定方法の複雑化について説明&コード追記
388
384
 
385
+ - 2016/05/04 23:15 「Undefined 型と Null 型」の説明が ES6 に準拠したものではなかった為、修正
386
+
389
387
 
390
388
 
391
389
  Re: a_yanyan さん

5

Symbol 型が増えた事による typeof 演算子による Object 型の判定方法の複雑化について説明&コード追記

2016/05/04 14:16

投稿

think49
think49

スコア18164

test CHANGED
@@ -134,9 +134,83 @@
134
134
 
135
135
 
136
136
 
137
+ ```JavaScript
138
+
139
+ /**
140
+
141
+ * get-type.js
142
+
143
+ *
144
+
145
+ * @version 1.0.2
146
+
147
+ * @author think49
148
+
149
+ * @url https://gist.github.com/862085
150
+
151
+ * @license http://www.opensource.org/licenses/mit-license.php (The MIT License)
152
+
153
+ */
154
+
155
+
156
+
157
+ function getType (value) {
158
+
159
+ var type, typeofValue;
160
+
161
+
162
+
137
- ところが、このコードは ES6 Symbol 型が増えた途端に破綻しました。
163
+ if (value === null) { // Null Type
164
+
138
-
165
+ return 'Null';
166
+
167
+ }
168
+
169
+
170
+
171
+ typeofValue = typeof value;
172
+
173
+
174
+
175
+ switch (typeofValue) {
176
+
177
+ case 'undefined': // Undefined Type
178
+
179
+ case 'boolean': // Boolean Type
180
+
181
+ case 'number': // Number Type
182
+
183
+ case 'string': // String Type
184
+
185
+ type = typeofValue.charAt(0).toUpperCase() + typeofValue.slice(1);
186
+
187
+ break;
188
+
189
+ case 'object': // Object Type (native and does not implement [[Call]])
190
+
191
+ case 'function': // Object Type (native or host and does implement [[Call]])
192
+
193
+ default: // Object Type (host and does not implement [[Call]])
194
+
195
+ type = 'Object';
196
+
197
+ break;
198
+
199
+ }
200
+
201
+
202
+
203
+ return type;
204
+
205
+ }
206
+
207
+ ```
208
+
209
+
210
+
139
- ES5 当時には `typeof` 演算子 "symbol" を返した場合Object 型と判定する動作が正しい為、**Symbol 型に対してObject 型と判定しまう**のです
211
+ ES5 当時には `typeof` 演算子 "symbol" を返した場合、それを「Object Type (host and does not implement [[Call]])」と見なして Object 型と判定する動作が正い動作で
212
+
213
+ ところが、ES6 で Symbol 型が増えた事によって `"symbol"` という新しく定義済文字列が出来た為、**上記コードは ES6 準拠の実装において Symbol 型を Object 型と判定してしまいます**。
140
214
 
141
215
  新しい型が増える度にコードを修正するいたちごっこには付き合ってられませんので `ToObject` で Object 型に変換して変換前と照合する事にしました。
142
216
 
@@ -154,7 +228,7 @@
154
228
 
155
229
 
156
230
 
157
- このコードも `ToObject` の動作次第では破綻しますが、「"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列は Object 型である」の判定ロジックよりはマシです。
231
+ このコードも `ToObject` の動作次第では破綻しますが、「"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列は Object 型である」の判定ロジックに修正するよりはマシです。
158
232
 
159
233
  ES6 では Object 型を判定する関数として `Object.isObject()` の導入が検討されたようですが、残念ながら見送られました。
160
234
 
@@ -310,6 +384,8 @@
310
384
 
311
385
  - 2016/05/04 22:17 typeof 演算子、「存在しない」を表す値、Undefined 型と Null 型の説明追加
312
386
 
387
+ - 2016/05/04 22:38 Symbol 型が増えた事による typeof 演算子による Object 型の判定方法の複雑化について説明&コード追記
388
+
313
389
 
314
390
 
315
391
  Re: a_yanyan さん

4

typeof 演算子、「存在しない」を表す、Undefined 型と Null 型の説明追加

2016/05/04 13:39

投稿

think49
think49

スコア18164

test CHANGED
@@ -38,29 +38,193 @@
38
38
 
39
39
 
40
40
 
41
+ ### 型の違い
42
+
43
+
44
+
45
+ 「型」を認識しているか確認して認識していないようだったら一通りの型を教えてあげてください。
46
+
47
+ JavaScript は「暗黙の型変換」が頻繁に発生する言語なので現在の型を意識しないといろいろとはまります。
48
+
49
+
50
+
51
+ - Undefined 型
52
+
53
+ - Null 型
54
+
55
+ - Boolean 型
56
+
57
+ - String 型
58
+
59
+ - Symbol 型 (ES6 規定)
60
+
61
+ - Number 型
62
+
63
+ - Object 型
64
+
65
+
66
+
67
+ ### typeof 演算子
68
+
69
+
70
+
71
+ `typeof` 演算子はその名称から「型を表す文字列を返す演算子」と読み取れますが、実際には型と一致しない文字列を返す場合があります。
72
+
73
+
74
+
75
+ - [12.5.6 The typeof Operator – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-typeof-operator)
76
+
77
+
78
+
41
79
  ```JavaScript
42
80
 
43
- /**
44
-
45
- * 0 の事例
46
-
47
- */
48
-
49
- console.log(Number('')); // 0 (空文字を Number 型に変換すると 0 を返す)
50
-
51
- console.log('JavaScript'.lastIndexOf('J', 0)); // 0 (String#lastInddexOf Number 型の値を返す。String#lastIndexOf の第二引数は Number を指定しなければならない。厳密には Number 型を指定しなくても良いが、内部的に Number 型に変換される。)
52
-
53
- console.log([].length); // 0 (空の配列の length 値は 0 である)
54
-
55
- console.log(Object.keys({}).length); // 0 (直属のプロパティを持たないオブジェクトを Object.keys で処理させると length 値が 0 の空の配列を返す)
56
-
57
- console.log(document.querySelectorAll('.hogehoge').length); // 0 (querySelectorAll はマッチしなかった場合空の NodeList (擬似配列) を返す。この NodeList length 値は 0 である。)
58
-
59
-
60
-
61
- /**
62
-
63
- * null の事例
81
+ console.log(typeof undefined); // "undefined"
82
+
83
+ console.log(typeof null); // "object" (Null型: typeof 演算子は Null 型に対して "object" を返す仕様バグがあります)
84
+
85
+ console.log(typeof true); // "boolean" (Boolean 型)
86
+
87
+ console.log(typeof 1); // "number" (Number 型)
88
+
89
+ console.log(typeof "sample"); // "string" (String 型)
90
+
91
+ console.log(typeof Symbol()); // "symbol" (Symbol 型: ES6 既定)
92
+
93
+ console.log(typeof {}); // "object" (Object 型: [[Call]] を持たない一般的なオブジェクト)
94
+
95
+ console.log(typeof document); // "object" (Object 型: [[Call]] を持たず、標準的な外来のオブジェクト)
96
+
97
+ console.log(typeof new Function); // "function" (Object 型: [[Call]] を持つオブジェクト)
98
+
99
+ console.log(typeof hoge); // "hoge" (Object 型: 非標準で外来の [[Call]] を持たないオブジェクト。詳細は後述の (*1) を参照。)
100
+
101
+
102
+
103
+ /**
104
+
105
+ * (*1) Object (non-standard exotic and does not implement [[Call]])
106
+
107
+ * typeof 演算子はこのオブジェクトに対して "undefined", "boolean", "function", "number", "symbol", "string" を返してはならない。
108
+
109
+ * つまり、"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列であれば何を返しても良い。
110
+
111
+ */
112
+
113
+ ```
114
+
115
+
116
+
117
+ typeof null === 'object' は仕様バグだそうですが、今更修正できないのでそのままになってしまっています。
118
+
119
+
120
+
121
+ - [typeof null === 'object' は ECMAScript 3 の仕様バグ - @think49の日記](http://d.hatena.ne.jp/think49/20120114/1326554107)
122
+
123
+
124
+
125
+ 関数に対して "function" を返す動作も困りものですが、一番対処に困るのは「Object (non-standard exotic...)」です。
126
+
127
+ このオブジェクトは既定文字列以外の全ての文字列を取りうるので `typeof` 演算子で真面目に Object 型を判定しようとすると「既定文字列以外の全て」を判定する事になります。
128
+
129
+ 実際に私は ES5 当時にそのようにコーディングしていました。
130
+
131
+
132
+
133
+ - [get-type.js : ECMAScript5 準拠のデータ型を返す](https://gist.github.com/think49/862085/a8146347d959b95832ea6c4c2e82b11c4957c6e8)
134
+
135
+
136
+
137
+ ところが、このコードは ES6 で Symbol 型が増えた途端に破綻しました。
138
+
139
+ ES5 当時には `typeof` 演算子は "symbol" を返した場合に Object 型と判定する動作が正しい為、**Symbol 型に対してObject 型と判定してしまう**のです。
140
+
141
+ 新しい型が増える度にコードを修正するいたちごっこには付き合ってられませんので `ToObject` で Object 型に変換して変換前と照合する事にしました。
142
+
143
+
144
+
145
+ ```JavaScript
146
+
147
+ if (Object(arg) === arg) { // ToObject で Object 型に変換する
148
+
149
+ console.log(arg + ' は Object 型である');
150
+
151
+ }
152
+
153
+ ```
154
+
155
+
156
+
157
+ このコードも `ToObject` の動作次第では破綻しますが、「"undefined", "boolean", "function", "number", "symbol", "string" 以外の文字列は Object 型である」の判定ロジックよりはマシです。
158
+
159
+ ES6 では Object 型を判定する関数として `Object.isObject()` の導入が検討されたようですが、残念ながら見送られました。
160
+
161
+
162
+
163
+ - [7.1.13 ToObject ( argument ) – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-toobject)
164
+
165
+ - [19.1.1.1 Object ( [ value ] ) – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-object-value)
166
+
167
+
168
+
169
+ ### 「存在しない」を表す値
170
+
171
+
172
+
173
+ 変数や返り値には既定の型があり、「存在しない事を表す値」は型によって変化します。
174
+
175
+
176
+
177
+ ```JavaScript
178
+
179
+ /**
180
+
181
+ * Number 型
182
+
183
+ */
184
+
185
+ Number('hoge'); // NaN (Number 型へ変換不可能な場合、NaN (Not a Number)を返す、NaN は Number ではない事を表す)
186
+
187
+ ''.charCodeAt(); // NaN (存在しない場所の文字を指定した場合、Unicodeコードポイントは NaN となる)
188
+
189
+ 'sample'.indexOf('hoge'); // -1 (存在しない index は -1 で表す)
190
+
191
+ console.log([].length); // 0 (空の配列の length 値は 0 である)
192
+
193
+ console.log(Object.keys({}).length); // 0 (直属のプロパティを持たないオブquerySelectorAll は存在しない要素のセレクタを指定した場合、空の NodeList (擬似配列) を返す。この NodeList の length 値は 0 である。)
194
+
195
+
196
+
197
+ /**
198
+
199
+ * String 型
200
+
201
+ */
202
+
203
+ [].join(); // "" (対象の文字列が存在しない場合、空文字を返す)
204
+
205
+ ''.charAt(); // "" (対象の文字列が存在しない場合、空文字を返す)
206
+
207
+
208
+
209
+ /**
210
+
211
+ * Undefined 型
212
+
213
+ */
214
+
215
+ (function () {}()); // undefined (return 節がない関数は未定義値を返す)
216
+
217
+ var a; // undefined (var 宣言の既定値は未定義値である)
218
+
219
+ void true; // undefined (void 演算子は常に undefined を返す)
220
+
221
+ document.hoge; // undefined (プロパティアクセス演算子は存在しないプロパティを指定した場合、undefined を返す)
222
+
223
+
224
+
225
+ /**
226
+
227
+ * Null 型
64
228
 
65
229
  */
66
230
 
@@ -78,57 +242,63 @@
78
242
 
79
243
 
80
244
 
245
+ /**
246
+
247
+ * Object 型
248
+
249
+ */
250
+
251
+ new Object; // {} (引数なしで new Object が呼び出された場合、直属のプロパティを持たない空のオブジェクトを返す)
252
+
253
+ new function () {}; // {} (this 値を操作せず、prototype も設定しない関数が new 演算子で呼び出された場合、直属のプロパティを持たない空のオブジェクトを返す)
254
+
255
+ new Array; // [] (引数無しで new Array が呼び出された場合、length 値が 0 の空の配列を返す)
256
+
257
+ document.querySelectorAll('.hoge'); // [] (querySelectorAll は存在しない要素のセレクタを指定した場合、空の NodeList (擬似配列) を返す)
258
+
259
+ ```
260
+
261
+
262
+
263
+ ### Undefined 型と Null 型
264
+
265
+
266
+
267
+ この節は私の個人的な考え(イメージ)なので戯言とでも思って読んでください(前述の通り、私は初心者にイメージで語るべきではないと考えている為、この節は非初心者向けです)。
268
+
269
+ Undefined 型は未定義ですが、Null 型は処理が終わった後に定義するという意味で「定義済(処理済)」と考える事も出来ます。
270
+
271
+
272
+
273
+ ```JavaScript
274
+
81
275
  (function () {
82
276
 
83
- function getThisArg () { return this; }
277
+ console.log(a); // undefined (未定義)
278
+
279
+
280
+
84
-
281
+ var a = null; // 存在しないを表す値として null を定義する
282
+
283
+
284
+
85
- console.log(getThisArg.call(null)); // グローバルオブジェクト (非 Strict Mode で Function#call で第1引数に null が指された場合の this 値はグローバルオブジェクトである)
285
+ console.log(a); // null (義済)
86
286
 
87
287
  }());
88
288
 
89
-
90
-
91
- (function () {
92
-
93
- 'use strict';
94
-
95
- function getThisArg () { return this; }
96
-
97
- console.log(getThisArg.call(null)); // null (Strict Mode で Function#call で第1引数に null が指定された場合の this 値は null である)
98
-
99
- }());
100
-
101
289
  ```
102
290
 
103
291
 
104
292
 
105
- 実際のところこれらコード見せても理解するのは難しいと思いますので、やはり自分からコード書いてもって疑問が生まれた時質問に答えあげるのが良いと思います。
293
+ - `getElementById` は存在しない id を指定した場合対象要素、存在しな事を確認してから返り値既定値(初期値)の undefined か null 変更し返します。
106
-
107
-
108
-
294
+
109
- ###
295
+ - `document.onclick` は初期値が `null` ですが、`onclick` プロパティを定義したという意味で `null` を代入していると考えられます。(ただし、こ考えはちょっと苦しとも考えています。「プロパティは定義済だが、プロパティ値は未定義のはず」と考える事も可能です。)
110
-
111
-
112
-
296
+
297
+
298
+
113
- 」を認識いるか確認て認識していないようだったら一通りの型を教えてあげてください
299
+ 実際のところ、`null` は**Object が存在時に返す値**とす方が自然なのれません
114
-
300
+
115
- JavaScript は「暗黙型変換」が頻繁に発生す言語なのの型を意識しないろいろとはります。
301
+ `NodeList` 配列的性質を持つオブジェクトを除き、私の知範囲はオブジェクトが存在しない時に `null` を返しています。
116
-
117
-
118
-
119
- - Undefined 型
120
-
121
- - Null 型
122
-
123
- - Boolean 型
124
-
125
- - String 型
126
-
127
- - Symbol 型 (ES6 規定)
128
-
129
- - Number 型
130
-
131
- - Object 型
132
302
 
133
303
 
134
304
 
@@ -138,6 +308,8 @@
138
308
 
139
309
  - 2016/05/03 17:02 コード事例を追記
140
310
 
311
+ - 2016/05/04 22:17 typeof 演算子、「存在しない」を表す値、Undefined 型と Null 型の説明追加
312
+
141
313
 
142
314
 
143
315
  Re: a_yanyan さん

3

誤字修正

2016/05/04 13:17

投稿

think49
think49

スコア18164

test CHANGED
@@ -64,7 +64,7 @@
64
64
 
65
65
  */
66
66
 
67
- console.log(document.onclick); // null (onclick の定値は null である)
67
+ console.log(document.onclick); // null (onclick の定値は null である)
68
68
 
69
69
  document.onclick = function (event) { console.log(event.type); }; // onclickイベントハンドラを定義する
70
70
 

2

typo修正

2016/05/04 01:01

投稿

think49
think49

スコア18164

test CHANGED
@@ -66,7 +66,7 @@
66
66
 
67
67
  console.log(document.onclick); // null (onclick の規定値は null である)
68
68
 
69
- document.onclick = function (event) { console.log(event.type); }; // onclickイベントは野良を定義する
69
+ document.onclick = function (event) { console.log(event.type); }; // onclickイベントハンドラを定義する
70
70
 
71
71
  document.onclick = null; // onclickイベントハンドラを削除するには null を代入する
72
72
 

1

コード事例を追記

2016/05/04 00:55

投稿

think49
think49

スコア18164

test CHANGED
@@ -38,6 +38,74 @@
38
38
 
39
39
 
40
40
 
41
+ ```JavaScript
42
+
43
+ /**
44
+
45
+ * 0 の事例
46
+
47
+ */
48
+
49
+ console.log(Number('')); // 0 (空文字を Number 型に変換すると 0 を返す)
50
+
51
+ console.log('JavaScript'.lastIndexOf('J', 0)); // 0 (String#lastInddexOf は Number 型の値を返す。String#lastIndexOf の第二引数は Number 型を指定しなければならない。厳密には Number 型を指定しなくても良いが、内部的に Number 型に変換される。)
52
+
53
+ console.log([].length); // 0 (空の配列の length 値は 0 である)
54
+
55
+ console.log(Object.keys({}).length); // 0 (直属のプロパティを持たないオブジェクトを Object.keys で処理させると length 値が 0 の空の配列を返す)
56
+
57
+ console.log(document.querySelectorAll('.hogehoge').length); // 0 (querySelectorAll はマッチしなかった場合空の NodeList (擬似配列) を返す。この NodeList の length 値は 0 である。)
58
+
59
+
60
+
61
+ /**
62
+
63
+ * null の事例
64
+
65
+ */
66
+
67
+ console.log(document.onclick); // null (onclick の規定値は null である)
68
+
69
+ document.onclick = function (event) { console.log(event.type); }; // onclickイベントは野良を定義する
70
+
71
+ document.onclick = null; // onclickイベントハンドラを削除するには null を代入する
72
+
73
+ console.log(document.getElementById('hogehoge')); // null (getElementById は存在しない要素を検索した場合、null を返す)
74
+
75
+ console.log(/hoge/.exec('test')); // null (RegExp#exec はマッチしなかった場合に null を返す)
76
+
77
+ console.log('test'.match(/hoge/)); // null (String#match はマッチしなかった場合に null を返す)
78
+
79
+
80
+
81
+ (function () {
82
+
83
+ function getThisArg () { return this; }
84
+
85
+ console.log(getThisArg.call(null)); // グローバルオブジェクト (非 Strict Mode で Function#call で第1引数に null が指定された場合の this 値はグローバルオブジェクトである)
86
+
87
+ }());
88
+
89
+
90
+
91
+ (function () {
92
+
93
+ 'use strict';
94
+
95
+ function getThisArg () { return this; }
96
+
97
+ console.log(getThisArg.call(null)); // null (Strict Mode で Function#call で第1引数に null が指定された場合の this 値は null である)
98
+
99
+ }());
100
+
101
+ ```
102
+
103
+
104
+
105
+ 実際のところ、これらのコードを見せても理解するのは難しいと思いますので、やはり自分からコードを書いてもらって疑問が生まれた時に質問に答えてあげるのが良いと思います。
106
+
107
+
108
+
41
109
  ### 型の違い
42
110
 
43
111
 
@@ -64,4 +132,12 @@
64
132
 
65
133
 
66
134
 
135
+ ### 更新履歴
136
+
137
+
138
+
139
+ - 2016/05/03 17:02 コード事例を追記
140
+
141
+
142
+
67
143
  Re: a_yanyan さん