質問するログイン新規登録

回答編集履歴

6

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

2016/05/04 14:16

投稿

think49
think49

スコア18194

answer CHANGED
@@ -168,29 +168,28 @@
168
168
 
169
169
  ### Undefined 型と Null 型
170
170
 
171
- この節は私の個人的な考え(イメージ)なので戯言とでも思って読んでください(前述の通り、私は初心者にイメージで語るべきではないと考えている為、この節は非初心者向けです)。
172
- Undefined 型は未定義ですが、Null 処理が終わった後に定義するとう意味で「定義済(処理済)」と考える事も出来ます。
171
+ ECMAScript 6 `undefined`, `null` 値を次のように定義しています。
173
172
 
174
- ```JavaScript
175
- (function () {
176
- console.log(a); // undefined (未定義)
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
- var a = null; // 存在しないを表す値として null を定義する
182
+ > 任意のオブジェクト値が意図的に存在しないことを表すプリミティブ
179
183
 
184
+ `getElementById` は存在しない id を指定した場合、対象のオブジェクト(要素ノード)が存在しない事を表す `null` を返します。
180
- console.log(a); // null (定義済)
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 型の判定方法の複雑化について説明&コード追記

2016/05/04 14:16

投稿

think49
think49

スコア18194

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
- ところが、このコードは ES6 Symbol 型が増えた途端に破綻しました。
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` 演算子 "symbol" を返した場合 Object 型と判定する動作が正しい為、**Symbol 型に対してObject 型と判定してしまう**の
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 型の説明追加

2016/05/04 13:39

投稿

think49
think49

スコア18194

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
- * 0 の事例
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
- console.log([].length); // 0 (空の配列の length 値は 0 である)
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
- * null の事例
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
- function getThisArg () { return this; }
139
+ console.log(a); // undefined (未定義)
43
- console.log(getThisArg.call(null)); // グローバルオブジェクト (非 Strict Mode で Function#call で第1引数に null が指定された場合の this 値はグローバルオブジェクトである)
44
- }());
45
140
 
46
- (function () {
47
- 'use strict';
141
+ var a = null; // 存在しないを表す値として null を定義する
142
+
48
- function getThisArg () { return this; }
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

誤字修正

2016/05/04 13:17

投稿

think49
think49

スコア18194

answer CHANGED
@@ -31,7 +31,7 @@
31
31
  /**
32
32
  * null の事例
33
33
  */
34
- console.log(document.onclick); // null (onclick の定値は null である)
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修正

2016/05/04 01:01

投稿

think49
think49

スコア18194

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

コード事例を追記

2016/05/04 00:55

投稿

think49
think49

スコア18194

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 さん