よくある失敗なのですが、Javascriptの変数の使い勝手が自由すぎるため、連想配列のkey名を間違えて配置し、想定通り値がとれないということがあります。
今までフロントでそこまで大規模なものを作らなかったので、挙動がおかしかった時はなんとなく失敗ポイントが連想しやすかったのですが、規模が大きくなるとそうもいかないと思います。
解決策として一つは以下のような方法を想定しています。
js
1var obj = {}; 2obj.hoge = 'test1'; 3console.log(obj.hoge);
という書き方をやめ、
js
1var HOGE = 'hoge'; 2 3var obj = {}; 4obj[HOGE] = 'test2'; 5console.log(obj[HOGE]);
とすることで、keyで定数的な変数を指定することで、打ち間違えなどにより誤った定数を指定するとstrictモードでエラーにする方法です。
ただkey名をすべて定数として一度指定することが非常に面倒でもあります。
皆様は普段どのようにJSの連想配列のkeyを管理されているのでしょうか。
今回webストレージで少し規模の大きなjsonを扱うことになり、かなり上記のようなことに神経質になっています。
Javaならclassでフィールド名を指定しているので、打ち間違えた時点でeclipse先生が教えてくれるのですが。。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
自分もたまにそのミスをします!
JetBrainのWeb StormというIDEを使っているのですが、
オブジェクトのキーのタイプミスは指摘してくれないので、
ちょっと悩ましく感じているところです。
自分は最近、TypeScriptというjavascriptの代替言語を使い始めたのですが、
TypeScriptであれば存在しないキーに値を代入しようとするとエラーを出してくれるので助かります。
TypeScriptを使わない解決策としては、javascriptのsetterメソッドを使って、
キーに値が代入される時にチェックするように実装することですかね。
ただ、setterメソッドはサポートされていないブラウザーのバージョンもあるので、
その点は注意が必要ですね。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
投稿2016/03/08 13:10
退会済みユーザー
総合スコア0
0
ベストアンサー
起こりうる想定状況
誤字による不正なプロパティ参照であれば、期待しない値が返ってくるので検証中に発覚すると思われます。
それでも発覚しないとしたら「不正な値が返ってくるが、期待する値と似通った値が返ってくる場合」でしょうか。
実際のところ、その辺りの状況を限定してもらわないと回答しづらいと思います。
rontec さんが想定する誤字による失敗例とはどのような状況でしょうか?
テキストエディタの補完機能を使う
私は Sublime Text 3 を使用していますが、一度使用した変数は補完機能の対象となるので、途中まで入力すれば既存候補から選択するだけで入力できます。
この機能は Atom TextEditor にもあったと思うのでいろいろとテキストエディタを試してみるといいかもしれません。
存在しないプロパティを参照してしまう場合
存在しないプロパティを参照すれば undefined
が返る仕様です。その為、書き方によっては TypeError
になります。
JavaScript
1var obj = {hoge: 'test1'}; 2 3if (obj.hogo.indexOf('test') !== -1) { // TypeError: Cannot read property 'indexOf' of undefined 4 console.log('hit!'); 5}
しかし、エラーにならないパターンも当然あるわけです。
JavaScript
1var obj = {hoge: 'test1'}; 2 3document.getElementById('sample').textContent = obj.hogo;
「空文字 (''
) が出力されるのだから気が付いてくださいよ」と突っ込みたいところですが、「いやいや例外を発生させなきゃ気がすまない」という意固地なあなたには専用関数を用意する事で対応します。
JavaScript
1function setTextContent (element, string) { 2 if (!(element instanceof Element)) { 3 throw new TypeError(element + ' is not a Element'); 4 } 5 6 if (typeof string !== 'string') { 7 throw new TypeError(string + ' is not a String'); 8 } 9 10 element.textContent = string; 11} 12 13var obj = {hoge: 'test1'}; 14setTextContent(document.getElementById('sampleeeee'), obj.hogo); // TypeError: undefined is not a Element (idのtypo) 15setTextContent(document.getElementById('sample'), obj.hogo); // TypeError: undefined is not a String (プロパティのtypo)
存在する別のプロパティを参照してしまう場合
先述でundefined
に String.prototype.indexOf
が存在しない事例をあげましたが、「実は Array.prototype.indexOf
かもしれないじゃないか」と指摘する人がいるかもしれません。
JavaScript
1var obj = {hoge: 'test1', hogo: ['test', 'hello', 'world']}; 2 3if (obj.hogo.indexOf('test') !== -1) { // Array.prototype.indexOf が存在するので TypeError にならない 4 console.log('hit!'); // 実行されてしまう 5}
その場合は getter method を用意します。
JavaScript
1function getStringValue (object, property) { 2 var string; 3 4 if (Object(object) !== object) { 5 throw new TypeError(object + ' is not a Object'); 6 } 7 8 if (typeof property !== 'string' && typeof property !== 'symbol') { 9 throw new TypeError(property + ' is not a neither String nor Symbol'); 10 } 11 12 string = object[property]; 13 14 if (typeof string !== 'string') { 15 throw new TypeError(string + ' is not a String'); 16 } 17 18 return string; 19} 20 21var obj = {hoge: 'test1', hogo: ['test', 'hello', 'world']}; 22 23if (obj.hogo.indexOf('test') !== -1) { 24 console.log('hit!'); 25}
「いやいや、obj.hogo
も String
型だったらどうするんだ?」という指摘上手なあなたは必要な場所からプロパティをコピペして下さい。
[[Prototype]] 上のプロパティを参照してしまう場合
オブジェクト初期化子は Object
を [[Prototype]]
に持つのでプロトタイプ上のプロパティを参照することはありえます。
JavaScript
1var obj = {hasOwnProperties: function hasOwnProperties () {}}; 2 3if (obj.hasOwnProperty('hasOwnProperties')) { // hasOwnProperties ではなく、Object.prototype.hasOwnProperty を実行してしまう 4 console.log('hasOwnProperties を実行したよ'); // 実行されてしまう 5}
Object.create(null)
で [[Prototype]]
を存在しないオブジェクトを生成すれば解決できます。
JavaScript
1var obj = Object.create(null, {hasOwnProperties: function hasOwnProperties () {}}); 2 3if (obj.hasOwnProperty('hasOwnProperties')) { // TypeError: obj.hasOwnProperty is not a function 4 console.log('hasOwnProperties を実行したよ'); 5}
もしくは、ES6 の new Map
を使っても良いでしょう。
JavaScript
1var map = new Map([['hasOwnProperties', function hasOwnProperties () {}]]); 2 3if (map.has('hasOwnProperty')) { 4 map.get('hasOwnProperties')(); 5 console.log('hasOwnProperties を実行したよ'); 6}
統一的なインターフェースを自作する
ES6 の Map()
を模倣して期待しない型が返ってきた場合に TypeError
、存在しないプロパティを参照する場合に ReferenceError
を発生させるインターフェースを作る事もできます。
JavaScript
1var map = new StrictMap([['hasOwnProperties', function hasOwnProperties () {}]]); 2 3map.get('hasOwnProperty'); // ReferenceError: hasOwnProperty is not defined 4map.get('hasOwnProperties', 'string'); // TypeError: hasOwnProperties is not a String
是非、頑張って作ってみてください!
Re: rontec さん
投稿2016/03/08 15:14
編集2016/03/08 15:24総合スコア18156
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
次のような assert() 関数を定義して、keyの検証が必要なところで使用する方法があります。
JavaScript
1function assert(condition, errorMessage) { 2 if (!condition) 3 console.error("ASSERTION FAILURE: " + (errorMessage? errorMessage: "")); 4} 5 6var something = { name: "Jack" }; 7var key = "name"; 8assert(something[key] || something.hasOwnProperty(key), 9 "Object doesn't have property " + key);
ほとんどの場合、配列表記の検証(obj[keyName]) だけで検証可能と思いますが、値に undefined や null、ゼロ等を意図的にセットしている場合にアサーションエラーになってしまいます。
これを回避するために、hasOwnProperty() で二重に検証を行っています。
それなら hasOwnProperty() だけでいいじゃないかという話ではありますが、hasOwnProperty() はCPUコストが非常に大きくパフォーマンス面でのリスク要因になりがちです。このため、最初に配列表記(CPUコストが非常に小さい)で検証を行い、これをパスできなかった場合だけ hasOwnProperty() で二度目の検証を行う、というやり方です。
ご参考になれば。
投稿2016/03/08 13:55
総合スコア2425
0
規模が大きくなるということであれば、連想配列の key 値以外の間違いも入りやすい&発見しにくいでしょうから、単体テストを日常的・継続的に実行する開発環境にするとよいのではないでしょうか。
投稿2016/03/08 13:15
総合スコア2468
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/03/08 13:25
2016/03/08 14:26
2016/03/08 14:32
2016/03/08 14:43
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/03/08 13:32
退会済みユーザー
2016/03/25 07:16
2016/04/02 03:40