javascript - 定義 - ダグラス クロックフォード
JavaScriptのコード構成に関する一般的に認められているベストプラクティス (19)
jQueryのようなJavaScriptフレームワークはクライアントサイドのWebアプリケーションをより豊かで機能的にするため、私は1つの問題に気付き始めました...
あなたはどのようにこれを組織化し続けていますか?
- すべてのハンドラーを1つの場所に置き、すべてのイベントの機能を記述しますか?
- あなたのすべての機能を包む関数/クラスを作成しますか?
- クレイジーのように書くと、それが最高のために動作することを願っていますか?
- あきらめて新しいキャリアを手に入れようか?
私はjQueryについて言及していますが、実際にはJavaScriptコードです。 私は、ライン上の行が積み重なり始めると、スクリプトファイルを管理したり、探しているものを見つけることが難しくなることを発見しています。 おそらく私が見つけた最大の問題は同じことをする方法がたくさんあることです。どれが現在受け入れられているベストプラクティスであるかを知るのは難しいです。
.jsファイルを他のアプリケーションと同じように素敵なものに保つ最良の方法については、一般的な推奨事項はありますか? それともIDEの問題なのでしょうか? そこには良い選択肢がありますか?
EDIT
この質問は、コード編成ではなく、ファイル編成に関するものです。 ファイルをマージしたり、コンテンツを分割したりするという、本当に良い例がいくつかあります。
私の質問は:あなたの実際のコードを整理するために現在一般に受け入れられているベストプラクティスの方法は何ですか? あなたの方法は何ですか、あるいはページ要素と対話し、お互いに矛盾しない再利用可能なコードを作成するための推奨方法ですか?
いくつかの人々は良いアイデアである名前空間をリストしています 。 他の方法は何ですか、具体的にはページの要素を扱い、コードを整理してきれいに保ちますか?
Dojoには最初のモジュールシステムがありました。 実際、それはDojoの基礎であるとみなされています。Dojoはすべてを一緒に保持します。
モジュールの使用Dojoは、以下の目的を達成します。
- Dojoコードおよびカスタム・コード(
dojo.declare()
)のdojo.declare()
スペース - グローバル・スペースを汚染したり、他のライブラリーと共存したり、ユーザーのDojo非対応コードをdojo.declare()
たりしないでください。 - モジュールを名前で同期または非同期にロードする(
dojo.require()
)。 - カスタムは、モジュールの依存関係を分析して、Webアプリケーションに必要なものだけを含めるための単一ファイルまたは相互依存ファイルのグループ(いわゆるレイヤー)を作成することによって構築されます。 カスタムビルドには、Dojoモジュールと顧客提供のモジュールも含めることができます。
- Dojoおよびユーザーのコードに対する透過的なCDNベースのアクセス。 AOLとGoogleの両方がこのようにDojoを運んでいますが、カスタムWebアプリケーションでもDojoを使用する顧客もいます。
JavaScriptを使用している組織では、
- あなたのすべてのjavascript用のフォルダ
- ページレベルのjavascriptは、ページの同じ名前の独自のファイルを取得します。 ProductDetail.aspxはProductDetail.jsになります
- ライブラリファイルのjavascriptフォルダの中にlibフォルダがあります
- 関連するライブラリ関数を、アプリケーション全体で使用するlibフォルダに置きます。
- Ajaxは私がjavascriptフォルダの外に移動し、それ自身のフォルダを取得する唯一のjavascriptです。 次に、クライアントとサーバーの2つのサブフォルダを追加します
- クライアントフォルダはすべての.jsファイルを取得し、サーバフォルダはすべてのサーバサイドファイルを取得します。
OO + MVCの優れた原理は、複雑なjavascriptアプリケーションを管理するための長い道のりに間違いありません。
基本的に私は自分のアプリとJavaScriptを以下の使い慣れたデザインに整理しています(私のデスクトッププログラミングの日からWeb 2.0までずっと存在しています)
イメージ上の数値の説明:
- 私のアプリケーションのビューを表すウィジェット。 これは、私のウィジェットをスパゲッティコードに変換するのではなく、MVCが達成しようとしているように、拡張性と分離性がきれいに分かれている必要があります(HTMLの大きなブロックをJavascriptに直接埋め込むというWebアプリケーションと同じです)。 各ウィジェットは、他のウィジェットによって生成されたイベントをリスニングすることにより、他のウィジェットを介して通信するため、管理不能なコードにつながる可能性のあるウィジェット間の強力な結合を減らします(スクリプトタグのグローバル関数を指し示すどこでもonclickを追加する日を覚えていますか?
- ウィジェットにデータを入れて前後に渡したいデータを表すオブジェクトモデル。 データをそのモデルにカプセル化することにより、アプリケーションはデータフォーマットに煩わされることになります。 たとえば、Javascriptでは、これらのオブジェクトモデルは主にシリアル化され、JSONにデシリアライズされます。サーバーがXMLを使用して通信する場合は、シリアル化/デシリアライゼーション層を変更するだけで、すべてのウィジェットクラスを変更する必要はありません。
- サーバーへのビジネスロジックと通信を管理するコントローラークラス+時折キャッシュレイヤー。 この層は、サーバへの通信プロトコルを制御し、必要なデータをオブジェクトモデルに入れる
- クラスは対応する名前空間にきれいにラップされます。 Javascriptでどのように厄介なグローバルな名前空間があるのか私たちは皆知っていると確信しています。
これまでは、ファイルを独自のjsファイルに分け、JavascriptでOO原則を作成するための一般的な方法を使用しました。 JS OOを書くための複数の方法があり、すべてのチームメンバーが同じアプローチを取っているとは必ずしも言えません。 チームがもっと大きくなった(私の場合は15人以上)、Object Oriented Javascriptの標準的なアプローチがないので、これは複雑になります。 同時に私は自分自身の枠組みを書いて、私が解決したよりも賢明な人であると確信している仕事のいくつかを繰り返すことは望まない。
jQueryはJavascriptフレームワークとして信じられないほど素敵ですが、プロジェクトが大きくなるにつれて、特にOOの練習の標準化を促進するために、私のWebアプリケーションのための追加構造が必要です。 私自身、いくつかの実験の後、YUI3 Base and Widget( http://yuilibrary.com/yui/docs/widget/およびhttp://yuilibrary.com/yui/docs/base/index.html )インフラストラクチャがまさに私が必要なものです。 私がそれらを使う理由はほとんどありません。
- これは、名前空間のサポートを提供します。 OOの本当の必要性とコードのきちんとした構成
- クラスとオブジェクトの概念をサポートしています
- クラスにインスタンス変数を追加するための標準化手段を提供します
- クラス拡張をきれいにサポートします
- コンストラクタとデストラクタを提供します
- レンダリングおよびイベントバインディングを提供します。
- 基本ウィジェットフレームワークを持っています
- 各ウィジェットは、標準イベントベースのモデルを使用してお互いに通信できるようになりました
- 最も重要なのは、すべてのエンジニアにJavascript開発用のOO標準を提供することです
多くのビューとは異なり、必ずしもjQueryとYUI3のどちらかを選択する必要はありません。 これら2つは平和的に共存することができます。 YUI3は複雑なWebアプリケーションに必要なオブジェクト指向のテンプレートを提供しますが、jQueryは私のチームに使いやすいJS抽象を提供しています。
YUI3を使用して、ModelとしてBaseを拡張するクラス、ViewとしてWidgetを拡張するクラス、必要なロジックとサーバー側の呼び出しを行うコントローラクラスを持つクラスを分離してMVCパターンを作成しました。
ウィジェットは、イベントベースのモデルを使用してイベントを聴いたり、定義済みのインターフェースに基づいて必要なタスクを実行したりして、互いに通信できます。 簡単に言えば、OO + MVC構造をJSにすることは私の喜びです。
ただ免責条項、私はYahoo!のために働いていない 元の質問によって提起された同じ問題に対処しようとしている建築家だけです。 私は誰も同等のOOフレームワークを見つけたら、これもうまくいくと思います。 主に、この質問は他の技術にも当てはまります。 私たちのプログラミングの日々をより管理しやすくするために、OO Principles + MVCを思いついたすべての人々のために神に感謝します。
javascriptに名前空間が組み込まれていればもっとうまくいくはずですが、Dustin Diazのような整理ではhere多くのことができます。
var DED = (function() {
var private_var;
function private_method()
{
// do stuff here
}
return {
method_1 : function()
{
// do stuff here
},
method_2 : function()
{
// do stuff here
}
};
})();
私は別々のファイルに異なる "名前空間"と時には個々のクラスを入れます。 通常、私は1つのファイルから始まり、クラスまたは名前空間がそれを保証するのに十分な大きさになるので、それを別のファイルに分けます。 ツールを使用してすべてのファイルを組み合わせてプロダクション用に使用することも、優れたアイデアです。
JavasciptMVCをチェックしてください。
あなたはできる :
コードをモデルレイヤ、ビューレイヤ、コントローラレイヤに分割します。
すべてのコードを1つの実動ファイルに圧縮する
自動生成コード
単体テストの作成と実行
もっとたくさんの...
とりわけ、jQueryを使用するので、他のjQueryプラグインを利用することもできます。
おそらくDDD(Domain-Driven Design)と関連していると思います。 私が取り組んでいるアプリケーションは、正式なAPIがないにもかかわらず、サーバーサイドのコード(クラス/ファイル名など)のようなヒントを与えてくれます。 私は、問題のドメイン全体のコンテナとしてトップレベルのオブジェクトを作成しました。 次に、必要な場所に名前空間を追加しました。
var App;
(function()
{
App = new Domain( 'test' );
function Domain( id )
{
this.id = id;
this.echo = function echo( s )
{
alert( s );
}
return this;
}
})();
// separate file
(function(Domain)
{
Domain.Console = new Console();
function Console()
{
this.Log = function Log( s )
{
console.log( s );
}
return this;
}
})(App);
// implementation
App.Console.Log('foo');
コードの構成には、規約や文書化の標準を採用する必要があります。
1.物理ファイルの名前空間コード。
Exc = {};
2.これらの名前空間のクラスをjavascriptにする。
3.実際のオブジェクトを表現するためのプロトタイプまたは関連する関数またはクラスを設定する。
Exc = {};
Exc.ui = {};
Exc.ui.maskedInput = function (mask) {
this.mask = mask;
...
};
Exc.ui.domTips = function (dom, tips) {
this.dom = gift;
this.tips = tips;
...
};
4.コードを改善するための規則を設定します。 たとえば、内部関数またはメソッドのすべてをオブジェクト型のclass属性にグループ化します。
Exc.ui.domTips = function (dom, tips) {
this.dom = gift;
this.tips = tips;
this.internal = {
widthEstimates: function (tips) {
...
}
formatTips: function () {
...
}
};
...
};
5.名前空間、クラス、メソッド、および変数のドキュメントを作成します。 必要に応じて、いくつかのコード(いくつかのFIとForsについては、通常、コードの重要なロジックを実装しています)について説明します。
/**
* Namespace <i> Example </i> created to group other namespaces of the "Example".
*/
Exc = {};
/**
* Namespace <i> ui </i> created with the aim of grouping namespaces user interface.
*/
Exc.ui = {};
/**
* Class <i> maskdInput </i> used to add an input HTML formatting capabilities and validation of data and information.
* @ Param {String} mask - mask validation of input data.
*/
Exc.ui.maskedInput = function (mask) {
this.mask = mask;
...
};
/**
* Class <i> domTips </i> used to add an HTML element the ability to present tips and information about its function or rule input etc..
* @ Param {String} id - id of the HTML element.
* @ Param {String} tips - tips on the element that will appear when the mouse is over the element whose identifier is id <i> </i>.
*/
Exc.ui.domTips = function (id, tips) {
this.domID = id;
this.tips = tips;
...
};
これらはちょっとしたヒントですが、これはコードの整理に大きく貢献しています。 成功するためには規律が必要であることを忘れないでください!
以前の仕事でJavascriptモジュールパターンをExt JSアプリケーションに適用することができました。 うまくカプセル化されたコードを作成する簡単な方法を提供しました。
以前の記事に触発されて、私はRakefileのコピーとWysiHat (changelogで言及されているRTE)と一緒に配布されているベンダのディレクトリを作成し、 JSLintによるコードチェックとYUI CompressorでのミニJSLint化を含むいくつかの変更を加えました。
アイデアは、 Sprockets (WysiHat)を使用して複数のJavaScriptを1つのファイルにマージし、マージされたファイルの構文をJSLintで確認し、配布前にYUI Compressorで圧縮します。
前提条件
- Javaランタイム
- ルビーとレーキの宝石
- Classpath JARを配置する方法を知っている必要があります
さあ
- Rhinoをダウンロードし、クラスパスにJAR( "js.jar")を配置します。
- YUI Compressorをダウンロードし、クラスパスにJAR(build / yuicompressor-xyz.jar)を入れてください。
- WysiHatをダウンロードし、 "vendor"ディレクトリをJavaScriptプロジェクトのルートにコピーします。
- JSLint for Rhinoをダウンロードして、 "vendor"ディレクトリに入れてください
JavaScriptプロジェクトのルートディレクトリに "Rakefile"という名前のファイルを作成し、次の内容を追加します。
require 'rake'
ROOT = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED = "final.js"
OUTPUT_MINIFIED = "final.min.js"
task :default => :check
desc "Merges the JavaScript sources."
task :merge do
require File.join(ROOT, "vendor", "sprockets")
environment = Sprockets::Environment.new(".")
preprocessor = Sprockets::Preprocessor.new(environment)
%w(main.js).each do |filename|
pathname = environment.find(filename)
preprocessor.require(pathname.source_file)
end
output = preprocessor.output_file
File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end
desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
jslint_path = File.join(ROOT, "vendor", "jslint.js")
sh 'java', 'org.mozilla.javascript.tools.shell.Main',
jslint_path, OUTPUT_MERGED
end
desc "Minifies the JavaScript source."
task :minify => [:merge] do
sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end
すべてを正しく行った場合は、コンソールで次のコマンドを使用できるはずです。
-
rake merge
- 異なるJavaScriptファイルを1つにマージする -
rake check
- コードの構文をチェックします(これはデフォルトのタスクなので、単にrake
ことができます) -
rake minify
- JSコードの縮小バージョンを準備する
ソースマージで
JavaScriptプリプロセッサであるSprocketsを使用すると、他のJavaScriptファイルをインクルード(またはrequire
)することができます。 最初のファイル( "main.js"という名前が付いていますが、Rakefileで変更することができます)の他のスクリプトを含めるには、次の構文を使用します。
(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"
// some code that depends on included files
// note that all included files can be in the same private scope
})();
その後...
WysiHatで提供されているRakefileを見て、自動ユニットテストを設定してください。 いい従業員 :)
そして今、答えを求めて
これは元の質問にはうまく答えません。 私は知っていると私はそれについて残念ですが、私はそれが誰かに彼らの混乱を整理するために有用かもしれないことを願っているので、ここに投稿しました。
問題への私のアプローチは、できるだけ多くのオブジェクト指向のモデリングを行い、実装を別のファイルに分けることです。 ハンドラは可能な限り短くする必要があります。 List
シングルトンの例も素晴らしいものです。
名前空間は...深いオブジェクト構造によって模倣することができます。
if (typeof org === 'undefined') {
var org = {};
}
if (!org.hasOwnProperty('example')) {
org.example = {};
}
org.example.AnotherObject = function () {
// constructor body
};
私は模倣のファンではありませんが、グローバルスコープから移動したいオブジェクトがたくさんある場合は、これが役立ちます。
優れたオブジェクト指向設計に続いて、プリンシパルとデザインパターンは、コードの保守と理解を容易にするための長い道のりです。 しかし、私が最近発見した最高のものの1つは、信号/スロット(別名パブリッシュ/サブスクライブ)です。 簡単なjQueryの実装については、 http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.htmlをhttp://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html 。
このアイデアはGUI開発のために他の言語でもよく使われています。 あなたのコードのどこかで何か重要なことが起こると、他のオブジェクトの他のメソッドが購読できるグローバルな合成イベントを公開します。 これにより、オブジェクトの優れた分離が得られます。
私はDojo(とPrototype?)にこのテクニックのバージョンが組み込まれていると思います。
信号とスロットとは何かを参照してください。
数日前、37Signalsの人たちWysiHat 。 彼らは、一種のプリプロセッサコマンドを使ってjavascriptファイルを束ねるライブラリを作った。
私はJSファイルを分離してから使用していましたが、最後にJSファイルを1つにマージしました。 そうすれば、懸念事項を分けて、最終的にパイプを通過するファイルが1つしかない(gzipped、less than)ことができます。
あなたのテンプレートでは、あなたが開発モードであるかどうかをチェックし、別々のファイルを含めてください。もしプロダクションであれば、最終的なものを含めてください(自分自身をビルドする必要があります)。
私の最後のプロジェクト-Viajeros.com-では、いくつかのテクニックの組み合わせを使用しました。 私はどのようにWebアプリケーションを整理するのか分からないでしょう - Viajerosは、明確なセクションを持つ旅行者のためのソーシャルネットワーキングサイトなので、各エリアのコードを分けるのは簡単です。
私は、サイト・セクションに従ってモジュールのネームスペース・シミュレーションと遅延ロードを使用します。 各ページのロード時に、私は "vjr"オブジェクトを宣言し、常に共通の関数のセットを(vjr.base.js)ロードします。 次に、各HTMLページは、必要なモジュールをシンプルに決定します。
vjr.Required = ["vjr.gallery", "vjr.comments", "vjr.favorites"];
Vjr.base.jsは、サーバからそれぞれgzipされたものを取得し、それらを実行します。
vjr.include(vjr.Required);
vjr.include = function(moduleList) {
if (!moduleList) return false;
for (var i = 0; i < moduleList.length; i++) {
if (moduleList[i]) {
$.ajax({
type: "GET", url: vjr.module2fileName(moduleList[i]), dataType: "script"
});
}
}
};
すべての "モジュール"はこの構造を持っています:
vjr.comments = {}
vjr.comments.submitComment = function() { // do stuff }
vjr.comments.validateComment = function() { // do stuff }
// Handlers
vjr.comments.setUpUI = function() {
// Assign handlers to screen elements
}
vjr.comments.init = function () {
// initialize stuff
vjr.comments.setUpUI();
}
$(document).ready(vjr.comments.init);
限られたJavascriptの知識を考えれば、これを管理するためのより良い方法が必要であることは分かっていますが、今までは私たちのためにうまくいっています。
私は、HTMLで任意のJavaScriptを含むことを避けようとしています。 すべてのコードはクラスにカプセル化され、各クラスは独自のファイルに格納されます。 開発のために、私はそれぞれのjsファイルを含むように別々の<script>タグを持っていますが、HTTP要求のオーバーヘッドを減らすために、プロダクション用に単一の大きなパッケージにマージされます。
通常は、アプリケーションごとに1つの「メイン」jsファイルがあります。 だから、私が "survey"アプリケーションを書いていたら、 "survey.js"というjsファイルがあります。 これには、jQueryコードへのエントリポイントが含まれます。 インスタンス化中にjQuery参照を作成し、それをパラメータとしてオブジェクトに渡します。 これは、javascriptクラスが「純粋」で、CSS IDやクラス名への参照を一切含まないことを意味します。
// file: survey.js
$(document).ready(function() {
var jS = $('#surveycontainer');
var jB = $('#dimscreencontainer');
var d = new DimScreen({container: jB});
var s = new Survey({container: jS, DimScreen: d});
s.show();
});
また、読みやすいように命名規則が重要であることがわかりました。 たとえば、すべてのjQueryインスタンスに「j」を追加します。
上の例では、DimScreenというクラスがあります。 (これは画面を暗くし、アラートボックスをポップアップすると仮定します)。画面を覆うように拡大できるdiv要素が必要です。その後、アラートボックスを追加するので、jQueryオブジェクトを渡します。 jQueryはプラグインのコンセプトを持っていますが、実際の不利な点はありません(インスタンスが永続的でなくアクセスできないなど)。 したがって、DimScreenクラスはjQueryを使用する標準のjavascriptクラスになります。
// file: dimscreen.js
function DimScreen(opts) {
this.jB = opts.container;
// ...
}; // need the semi-colon for minimizing!
DimScreen.prototype.draw = function(msg) {
var me = this;
me.jB.addClass('fullscreen').append('<div>'+msg+'</div>');
//...
};
私は、このアプローチを使用してかなり複雑なアプリケーションを構築しました。
私は誰もMVCのフレームワークについて言及していないことに驚いています。 私はBackbone.jsを使用してコードをモジュール化し、デカップリングしてきました。
そこにはかなりの種類のフレームワークがあり、そのほとんどはかなり小さいです。 私の個人的な意見では、派手なUIのためのjQueryを数行以上作成したり、豊富なAjaxアプリケーションを作成したい場合、MVCフレームワークはあなたの人生をはるかに簡単にします。
あなたの質問は、昨年末に私を悩ませたものです。違いは、プライベートとパブリックの方法について聞いたことがない新しい開発者にコードを渡すことです。私は単純なものを作る必要があった。
最終的な結果は、オブジェクトリテラルをjQueryに変換する小さな(約1KB)フレームワークでした。シンタックスは視覚的にスキャンしやすく、jsが非常に大きくなった場合は、使用されたセレクタ、ロードされたファイル、依存関数などを見つけるために再利用可能なクエリを書くことができます。
ここに小さなフレームワークを投稿することは実用的ではないので、私は例を挙げてブログ記事を書いた(私の最初、それは冒険だった!)。あなたは一見を歓迎します。
ここで何か他の人には、それをチェックするために、私は非常にフィードバックを感謝したい!
FireFoxは、オブジェクトクエリの例でtoSource()をサポートしているので推奨されています。
乾杯!
アダム
あなたは使用することができますjqueryのMXあなたはモデル、ビュー、およびコントローラを使用することを可能にするスクリプトのセットです(javascriptMVCで使用します)。私はプロジェクトでそれを使用し、圧縮のため最小のスクリプトサイズで構造化されたjavascriptを作成するのを助けました。これはコントローラの例です:
$.Controller.extend('Todos',{
".todo mouseover" : function( el, ev ) {
el.css("backgroundColor","red")
},
".todo mouseout" : function( el, ev ) {
el.css("backgroundColor","")
},
".create click" : function() {
this.find("ol").append("<li class='todo'>New Todo</li>");
}
})
new Todos($('#todos'));
レイジー必要に応じて必要なコードを読み込みます。Googleはgoogle.loaderこれを実行しgoogle.loader
私はベン・ノーランの行動に触発されたカスタム・スクリプトを使用しています(私にはこれ以上の悲しいことにリンクがありません)、ほとんどのイベント・ハンドラーを保管しています。これらのイベントハンドラは、classNameやIdなどの要素によってトリガされます。例:
Behaviour.register({
'a.delete-post': function(element) {
element.observe('click', function(event) { ... });
},
'a.anotherlink': function(element) {
element.observe('click', function(event) { ... });
}
});
私は、グローバルな振る舞いを含むものを除いて、大部分のJavaScriptライブラリーをオンザフライで含むことが好きです。私はZend FrameworkのheadScript()プレースホルダーヘルパーを使っていますが、javascriptを使ってAjileで他のスクリプトをオンザフライで読み込むこともできます。