質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

332閲覧

JavaScriptのコーディング方法について幾つか教えてください。

fukumi822

総合スコア228

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

2クリップ

投稿2019/04/29 15:00

趣味でプログラミングをしているのですが、以下の点が分からないので教えてください。

  • JavaScriptの書き始め方
  • 大量の要素管理方法

JavaScriptの書き始め方

JavaScriptでは基本的にDOM操作をする為、ウィンドウのイベントリスナーを設定します。

window.addEventListener('load', ()=>{ //!script });

ただ、ここ(!script)にやたい事をズラズラ書いてしまうと後で読み返す時に苦労するので、私はクラスを使って、主な処理をクラス内で記述しています。

クラス内であれば、インスタンス時にDOMが取れたら順序関係なくプロパティ・メソッドの定義が出来る為です。
これはあくまでも私の考えで一般的にはどの様な方法で書き始めているのかが知りたいです。

大量の要素の管理方法

大量の要素を取得する際、の管理方法が知りたいです。
クラスコンストラクタ内で以下の様にしていました。

class MyClass{ constructor(){ this.element = document.getElementById("postText"); //以下十個以上似た記述 } }

個人的な感想これだと、何がなんだよく分からなくなって来ます。
そこで、全てを一纏めにしようと感がえ次の様に構造化しました。

class MyClass{ constructor(){ this._publicElements = { editorInputInterface : { textArea : document.getElementById("postText"), input : { postTitle : document.getElementById("postTitle"), postLabel : document.querySelector("input[name='post_label']"), postSeo : document.querySelector("input[name='post_seo_about']") } }, editorControlInterface : { ControlButton : { addImage : document.querySelector("#Toolbar .left button[value='image']"), addLink : document.querySelector("#Toolbar .left button[value='link']"), addTable : document.querySelector("#Toolbar .left button[value='table']"), addTableWidget : { widgetBody : document.querySelector("div.tableInWidget"), input : document.querySelectorAll("div.tableInWidget input"), button : document.querySelector("div.tableInWidget button") }, addQuote : document.querySelector("#Toolbar .left button[value='quote']") }, RequestButton : { public : document.querySelector("#Toolbar div.right button.article"), preview : document.querySelector("#Toolbar div.right button.preview"), save : document.querySelector("#Toolbar div.right button.save") } } }; } }

確かに構造化されて分かりやすい??? な状況で、デメリットも見えて来ました。

  • 参照する時軽く数十文字行くthis._publicElements.editorControlInterface.ControlButton.addLink
  • 確かに読めば分かるけど、視認性が悪すぎる

この様な問題に直面しました。皆さんは大小の規模に限らずこの様な場合どの様に管理をしますか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

basicemkk

2019/04/29 15:25

ピュアなjavascriptで書く必要があるのでしょうか? vueやreactなどを使った方が管理が楽かなと感じます。
fukumi822

2019/04/29 15:27

vueやreactを使った事がありません。 どういった物なんですか?
basicemkk

2019/04/29 15:30

ざっくり今回の要件に合う表現をするならdom毎にロジックを与えられたりでき、コンポネント管理がしやすいものです。 一度調べてみてもいいかもしれません。
退会済みユーザー

退会済みユーザー

2019/04/29 16:45

MyClass の中で決め打ちしているのだから、それを再利用できないクラスになっていますね。 querySelector で指定する要素群を constructor への引数にしてはどうですか? 細分化して構造を構築する、その構造を再利用できるようにするのがクラスの便利なところではないですか? あと短く一度に指定するなら let [a,b,c] = document.querySelectorAll ("..., ...., ...");
miyabi_takatsuk

2019/04/29 16:47

hai_haiさん>そこですよね。 せっかくクラス構文使ってオブジェクト指向で書いてるのに、 意味がなくなってる気がします 汗
退会済みユーザー

退会済みユーザー

2019/04/29 17:29 編集

おそらくね、必要な要素を取り込む必要はないのですよ。イベントハンドラの引数に渡される event から必要なものを探せばいい。event.target からクリックされた要素、そこから id なり className なり、親要素を辿っても十数段上を探すだけで必要な要素を見つけられるはずです。document.addEvenentListener で始めると dom 構造を読み終わるのを待たなくていい。document はすでに存在するのだから。 ここで見ていると document にイベントを張り付けている人は少ないよね。最善だと思うのに。
guest

回答2

0

まず、MyClassのままなのが相当ダメです。イケてません。
貴方は玩具を整理しようとおもちゃ箱を買ってきて、
それにMyClassというラベルを貼って管理しようと思いますか?

普通の人間は入れるものを抽象化してラベル名を決定します。
例えばゲーム機でまとめるならゲーム機というラベルを貼りますよね。

やってないでしょ?すぐやりましょ。
もしこれが例題であってもやってください。
コードを書くというのはやることに名前を付けるという行為なので、練習であっても、いやだからこそ命名の練習が必須です。

私はクラスを使って、主な処理をクラス内で記述しています。

オブジェクト指向でよくあるクラスを使うとコード量が増えますが
この増えたコード量をペイ出来るか否かがオブジェクト指向のとてもむずかしい所になります。

そこで、全てを一纏めにしようと感がえ次の様に構造化しました。

オブジェクト指向初心者が陥るやつですね。
ポリモーフィズムとDIを覚えない限り、オブジェクト指向の良さは1ミリも引き出せませんので、
この両方は確実に学習してください。

私の書いたオブジェクト指向の記事が多少は参考になるでしょう。
分かりそうで分からない少し分かるオブジェクト指向プログラミング - Qiita

軽く解説しておくと、インスタンスは情報とメソッドをカラクリ人形です。


今回の例だとこのクラスはインスタンス化して使うことを想定していませんので、
プレーンなオブジェクトを生成したほうが良いです。

そしてオブジェクトとは微塵も関係ない所で解決できそうですね。
ちょっと作ってみましょうか。

JavaScript

1var nodeList = { 2 posts: { 3 title: "#postText", 4 text: "#postText", 5 label: "input[name='post_label']", 6 seo: "input[name='post_seo_about']" 7 }, 8 controller: { 9 image: "#Toolbar .left button[value='image']", 10 link: "#Toolbar .left button[value='link']", 11 table: "#Toolbar .left button[value='table']", 12 widget: { 13 body: "div.tableInWidget", 14 input: "div.tableInWidget input", 15 button: "div.tableInWidget button" 16 }, 17 quote: "#Toolbar .left button[value='quote']" 18 }, 19 requests: { 20 public: "#Toolbar div.right button.article", 21 preview: "#Toolbar div.right button.preview", 22 save: "#Toolbar div.right button.save" 23 } 24}; 25var node = function (path) { 26 var name = path.split(".").reduce((obj, n) => obj != null ? obj[n] : "", nodeList); 27 return name; 28 // 本当はquerySelectorで包んで返したいのでこっちに変更してね 29 // return document.querySelector(name); 30} 31 32console.log(node("controller.image")); 33// "#Toolbar .left button[value='image']" 34console.log(node("requests.public")); 35// "#Toolbar div.right button.article" 36console.log(node("controller.widget.input")); 37// "div.tableInWidget input" 38console.log(node("hoge.piko")); 39// "" 40 41window.addEventListener('load', ()=>{ 42 // あとはやりたいことをこの中で記述する 43 // 必要になったらこんな風に実行する 44 // var input = node("controller.widget.input"); 45});

ちょっとnode関数の中身でイディオム使ってますが、
JavaScriptはnullやundefined値はプロパティを所持してないのでエラー回避の為に三項演算子使ってます。
また、文字列の.を元に再帰的にプロパティを読みに行く仕組みを作る為にreduce使ってます。

投稿2019/05/02 01:33

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

まず、windowオブジェクトを使用しているので、
ブラウザ上で動くスクリプトという認識でお答えしていきます。
また、アロー関数やclass構文は、Internet Explorerでは動きません。
javascriptは、プロトベースのオブジェクト指向言語のため、
javascriptのclass構文は、プロトベース構文の糖衣構文になります。
とまぁ、class構文、アロー関数を使う事を前提とされているようなので、IEは対象ブラウザとして考えないでいい状況との認識も併せてお答えしていきます。

JavaScriptの書き始め方

これに関して。
DOMを読み込んでから実行するなら、window.addEventListener('load', ()=>{});
は発動が遅いですよ。
これは、HTML上のすべての要素(画像やらJSやら)全ファイルを読み込み完了してから実行するものです。つまり、初動が遅くなります。
DOMを読み込んでからなら、

javascript

1document.addEventListener('DOMContentLoaded', ()=> { 2 // 処理を記述 3});

で充分ですし、適当かと思います。
そして、考えとおっしゃっていますが、
class構文を使用したとしても、
インスタンスを生成する際には、DOMが読み込まれている状態でないと、
当然エラー
が起きます。
クラスの記述は、ロード系イベントの外側でけっこうですが、
インスタンスを生成するnew構文は、ロード系イベントの中で書かないと、DOMが取得できずエラーが出ます。
(body要素終了タグ直前に書くなら、あるいはエラーにならないかもしれない)
つまりは、下記のようにしなくてはならないということです。

javascript

1class myClass { 2 constructor(){ 3 this.hoge = document.getElementById('hoge'); 4 } 5} 6// ここでDOM読み込み前にインスタンスを生成するとエラーになる。 7const myInstans = new myClass(); 8 9document.addEventListener('DOMContentLoaded', ()=> { 10 // このイベント内であれば、DOMが読み込まれているので、絶対にエラーは起きない 11 window.myInstans002 = new myClass(); 12});

つまり一般的とか、考えとかではなく、
そもそも、DOMの取得なりのコントロールは、DOMを読み込んでからでないと動かないんです。

また、主な処理を、クラスの中に~とおっしゃっておりますが、
もしかして、
クラスのメソッドの中でいちいち、
window.addEventListnerなどを実行しているということでしょうか?
だとしたら、かなりよろしくなく、メモリーリークを誘発させる書き方だと思います。
addEventListnerは、その名の通り、イベントを追加するメソッドです。
つまり、そんなクラスを作った日には、インスタンスを生成するないし、該当メソッドを実行する度に、イベントを追加されてしまいます。
一、二個ならまだしも、一万個とか生成した時は、
管理不能、メモリ解放不能な変数や関数を、大量にメモリ上に残すことになります。
addEventListnerは、同スクリプト内で大量に使うなら、必要ないものは削除できるようにしておくか、
そもそも、同じオブジェクトにはあまり何個も重ねて処理を追加しないように書く工夫が必要かと思います。

大量の要素の管理方法

constructorに直接オブジェクト処理実行してる時点で、メモリの無駄遣いというかすでに分かりやすさや、柔軟性が失われているような気がしますね。
せっかくclass構文を使ってオブジェクト指向で書いているのにもったいないような気がします。

私なら、構造だけを定義したクラスを先に用意し、
実際に使用するクラスの中でインスタンスを生成するようにして、
オブジェクトを管理しますね。
そちらの方が、何のクラスで何をしているかがわかりやすくなり、
また、各構造別にメソッドを定義もできますので、非常に管理しやすくなるかと。
そうすれば、プロパティ名を短くしても、どこで何をやってるかのわかりずらさも軽減されるかと思います。

javascript

1class inputInterface { 2 constructor(titleID, labelSelector, seoSelector){ 3 this.title = document.getElementById(titleID); 4 this.label = document.querySelector(labelSelector); 5 this.seo = document.querySelector(seoSelector); 6 } 7} 8 9class editorInputInterface { 10 constructor(textAreaName, titleID, labelSelector, seoSelector){ 11 this.textArea = document.getElementById(textAreaName); 12 this.input = new inputInterface(titleID, labelSelector, seoSelector); 13 } 14} 15 16class MyClass { 17 constructor(){ 18 this._publicElements = { 19 editorInput: new editorInputInterface("postText", "postTitle", "input[name='post_label']", "input[name='post_seo_about']"), 20 // . 21 // . 22 // . 23 // 以下にも同様に、先に構造体を定義して、こちらで使用していく 24 }; 25 26 27 } 28}

つまり、質問者さんの書き方だと、せっかくクラス構文で書いているのに、
結局ハードコーディングになってしまっていて、
クラス構文を生かし切れていない形になってます。
ハードコーディングは、パッと見でわかりやすい反面、
柔軟性もなければ、メンテナンス性もかなり悪くなります。

先に構造体を作って、インスタンスを生成して・・・という作り方は、
中~大規模のオブジェクト指向での開発において、とても重要になります。

以上、長文失礼しました。
また、後半に関しては、正直私の考え方なので、違うやり方の方がいい場合もあるかもしれません。

投稿2019/04/29 16:39

編集2019/04/29 17:12
miyabi_takatsuk

総合スコア9528

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fukumi822

2019/04/29 17:36

回答ありがとうございます。 Internet Explorerに関して、対応の意思はありませんので問題はありません。 クラスの考え方についてもありがとうございます。 確かにクラスを使っているので、JavaのMainクラスみたいな書き方(自分だけかも)になっていて無駄が多いですね。この辺りもう少し設計を練り直したいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問