Google JavaScript スタイルガイド - 日本語訳
JavaScriptのスタイルに関するルール
最終更新:
aias-jsstyleguide2
-
view
include_js plugin Error : このプラグインで利用できない命令または文字列が入っています。
トップレベルのシンボルだけを規定します。
JavaScriptのスタイルに関するルール
- 各項目の左側にある三角ボタン
で、詳細情報の表示・非表示を切り替えられます。また下のボタンを使うと全項目を一度に変更することができます。
全て切り替える
命名規則
原則として、関数はfunctionNamesLikeThis、変数はvariableNamesLikeThis、クラスはClassNamesLikeThis、列挙型はEnumNamesLikeThis、メソッドはmethodNamesLikeThis、定数はCONSTANT_VALUES_LIKE_THIS、名前空間はfoo.namespaceNamesLikeThis.bar、ファイルはfilenameslikethis.jsのように、それぞれ命名してください。
プロパティとメソッド
- private なプロパティ、メソッドには名前の末尾にアンダースコアをつけてください。
- protected なプロパティ、メソッドには(publicと同様に)末尾にアンダースコアをつけません。
private
と
protected
に関するより詳しい情報は、「可視性」の項を参照してください。
メソッドと関数のパラメータ
省略可能な関数の引数は名前の先頭をopt_としてください。
関数の引数の個数が可変である場合、最後の引数名はvar_argsとすべきです。ただしコード内ではvar_argsを参照するのではなく、argumentsオブジェクトを使って下さい。
@paramアノテーションによっても、省略可能な引数や個数の変化する引数を定義できます。Compilerは変数名とアノテーションのどちらの記法も受け入れますが、両者を共に使うのが好ましいやり方です。
GetterとSetter
EcmaScript5では、プロパティに対するgetterとsetterは推奨されません。もし使うのなら、getterが監視中のプロパティの状態を変更しないようにしなければなりません。
var foo = { get next() { return this.nextId++; } };
アクセサ関数
プロパティに対するgetterとsetterは必須ではありません。ただしそれを使う場合、getterは必ずgetFoo()、setterは必ずsetFoo(value)のように命名してください。(論理値を返すgetterであれば、isFoo()としてもかまいません。たいていはその方が自然です。)
名前空間
JavaScript自体は、パッケージや名前空間の仕組みをサポートしていません。
グローバル名の衝突はデバックが難しく、また2つのプロジェクトを統合するようなケースでは、手に負えない大きな問題となる可能性もあります。JavaSriptコードの共有を可能にするには、衝突を防ぐための約束事に従う必要があります。
グローバルコードには名前空間を適用する
グローバルスコープに存在するコードは
必ず
、プロジェクトやライブラリと関連付けられた一意のプリフィックスをもつ擬似的な名前空間に属しているものとします。もしプロジェクト名が "Project Sloth" であれば、擬似的な名前空間名はsloth.*とするのがよいでしょう:
var sloth = {};
sloth.sleep = function() {
...
};
Closure LibraryやDojo Toolkitを含む多くのライブラリは、名前空間を宣言するための高位の関数が提供されています。名前空間の宣言方法には一貫性を持たせてください:
goog.provide('sloth');
sloth.sleep = function() {
...
};
名前空間の所有権を尊重する
ある名前空間の下位の名前空間で何かをする場合は、必ず親の名前空間の所有者にそのことを知らせてください。もしあなたがslothの下にhatsを作ろうとしているのであれば、Slothチームはあなたが使うsloth.hatsのことを必ず知っていなければなりません。
外部コードと内部コードでは異なる名前空間を使う
「外部コード」とは、あなたのコードベースの外部にあり、独立してコンパイルされた全てのコードのことです。外部コードと内部コードで使われている名前は、厳格に分離されなければなりません。もしあなたがfoo.hats.*という名前で提供される外部ライブラリを使っているのなら、内部コードはfoo.hats.*にいかなるシンボルも定義してはいけません。ライブラリの提供元が新しいシンボルを定義すると、そのコードは動作しなくなるおそれがあるからです:
foo.require('foo.hats');
/**
* 間違い -- こうしてはいけません。
* @constructor
* @extends {foo.hats.RoundHat}
*/
* 間違い -- こうしてはいけません。
* @constructor
* @extends {foo.hats.RoundHat}
*/
foo.hats.BowlerHat = function() {
};
};
どうしても外部コードの名前空間に新しいAPIを追加する必要がある場合は、そのAPI関数だけを明示的にエクスポートしてください。ただしそうであっても、一貫性を保ち、Compilerがより効果的に最適化を行えるようにするため、内部コードは内部の名前で内部のAPIを呼び出すようにすべきです。
foo.provide('googleyhats.BowlerHat');
foo.require('foo.hats');
/**
* @constructor
* @extends {foo.hats.RoundHat}
*/googleyhats.BowlerHat = function() {
...
};
goog.exportSymbol('foo.hats.BowlerHat', googleyhats.BowlerHat);
可読性を上げるため長い名前に別名を付ける
もしそうすることで可読性が向上するなら、完全な型名に対しローカルの別名を付けましょう。ローカル名は型の一番最後の部分にマッチするようにしてください:
/**
* @constructor
*/some.long.namespace.MyClass = function() {
};
/**
* @param {some.long.namespace.MyClass} a
*/some.long.namespace.MyClass.staticHelper = function(a) {
...
};
myapp.main = function() {
var MyClass = some.long.namespace.MyClass;
var staticHelper = some.long.namespace.MyClass.staticHelper;
staticHelper(new MyClass());
};
名前空間にローカルの別名をつけてはいけません。名前空間への別名付けにはgoog.scopeを使いましょう。
myapp.main = function() {
var namespace = some.long.namespace;
namespace.MyClass.staticHelper(new namespace.MyClass());
};
var namespace = some.long.namespace;
namespace.MyClass.staticHelper(new namespace.MyClass());
};
プロパティが列挙型である場合を除き、別名のプロパティ(やその他のメンバ)へのアクセスは避けて下さい:
/** @enum {string} */some.long.namespace.Fruit = {
APPLE: 'a',
BANANA: 'b'
};
myapp.main = function() {
var Fruit = some.long.namespace.Fruit;
switch (fruit) {
case Fruit.APPLE:
...
case Fruit.BANANA:
...
}
};
myapp.main = function() {
var MyClass = some.long.namespace.MyClass;
MyClass.staticHelper(null);
};
var MyClass = some.long.namespace.MyClass;
MyClass.staticHelper(null);
};
グローバルスコープでの別名の作成は絶対にしないでください。別名は関数ブロックの中でのみ使用可能とします。
ファイル名の命名規則
大文字小文字を区別するプラットフォームで混乱が生じることを避けるため、ファイル名には小文字のみを使って下さい。ファイル名は.jsで終わらねばならず、-と_以外の区切り文字を含んではいけません(より好ましいのは_より-の方です)。
独自のtoString()メソッド
副作用なしで常に成功すること。
独自のtoString()メソッドを定義することで、オブジェクトが自分自身を文字列化(string-ify)する方法を制御できます。それ自体はよいのですが、そのときメソッドは以下の点を保証していなければなりません。(1) 常に成功すること、(2)他の部分に影響を与えないこと。この基準を満たしていない場合、容易に深刻な問題が引き起こされることでしょう。例えばtoString()メソッドが内部でassert関数を呼び出しているとします。assertは処理が失敗した場合にオブジェクト名を出力しようとしますが、その時に呼び出すのはもちろんtoString()メソッドです。
変数初期化の遅延
OK。
変数は、宣言された時点で常に初期化が可能とは限りません。従って初期化の遅延は当然ありうることです。
明示的なスコープ
常にそうしてください。
常に明示的なスコープを使用し、移植性と明瞭さを向上させてください。例えば関数を作るとき、スコープチェーンの中にwindowがあることを仮定しないでください。いつかwindowがコンテントウィンドウを指していない別のアプリケーションでそれを使いたくなるかもしれません。
コードのフォーマット
詳しい情報を見るには開いてください。
基本的な考え方は
C++ formatting rules
に従い、さらに以下に説明する内容が追加されます。
波括弧
セミコロンの暗黙的な挿入を考慮し、波括弧を開くときはそれまでと同じ行で始めてください。例えばこのようにします:
if (something) {
// ...
} else {
// ...
}
配列とオブジェクトの初期化
きれいに収まるようなら、配列やオブジェクトの初期化を1行で行ってもかまいません:
var arr = [1, 2, 3]; // [の後と]の前にスペースを入れないでください。var obj = {a: 1, b: 2, c: 3}; // {の後と}の前にスペースを入れないでください。
配列とオブジェクトの初期化が複数行にわたる場合は、ブロックと同様、括弧を変数と同じ行に置き、スペース2つ分インデントしてください:
// オブジェクトの初期化
var inset = {
top: 10,
right: 20,
bottom: 15,
left: 12
};
// 配列の初期化
this.rows_ = [
'"Slartibartfast" <fjordmaster@magrathea.com>',
'"Zaphod Beeblebrox" <theprez@universe.gov>',
'"Ford Prefect" <ford@theguide.com>',
'"Arthur Dent" <has.no.tea@gmail.com>',
'"Marvin the Paranoid Android" <marv@googlemail.com>',
'the.mice@magrathea.com'
];
// メソッド呼び出しの中で使う例。
goog.dom.createDom(goog.dom.TagName.DIV, {
id: 'foo',
className: 'some-css-class',
style: 'display:none'
}, 'Hello, world!');
初期化リストの中で長い識別子や値の位置を揃えるのはやっかいな問題です。むしろ常に位置を揃えない方がよいでしょう。例えばこのようにします:
CORRECT_Object.prototype = {
a: 0,
b: 1,
lengthyName: 2
};
下のようにはしません:
WRONG_Object.prototype = {
a : 0,
b : 1,
lengthyName: 2
};
a : 0,
b : 1,
lengthyName: 2
};
関数の引数
可能であれば、関数の引数リストは全て同じ行に書かれるべきです。もしそうすることで80文字の制限を超えてしまう場合、引数は読みやすい形式で改行されなくてはなりません。可能な限り80文字に近づけてスペースを節約するか、または引数ごとに改行を行って可読性を高めます。インデントはスペース4つ分、もしくは丸括弧で位置を揃えます。以下に示す例は引数を改行する際の最も一般的なパターンです:
// スペース4つ分インデントし、80文字で折り返します。関数名がとても長い場合に使います。
// 関数名が変わったときにインデントを修正する必要がなく、場所もあまりとりません。
goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(
veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {
// ...
};
// スペース4つ分インデントし、各行に1つずつ引数を書きます。関数名が長い場合に使います。
// 関数名の変更の影響を受けず、また個々の引数を目立たせることができます。goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(
veryDescriptiveArgumentNumberOne,
veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy,
artichokeDescriptorAdapterIterator) {
// ...
};
// 丸括弧に揃えてインデントし、80文字で折り返します。視覚的に引数をグループ化でき、
// かつスペースも少なくてすみます。function foo(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {
// ...
}
// 丸括弧に揃えてインデントし、各行に1つずつ引数を書きます。
// 個々の引数を目立たせることができます。function bar(veryDescriptiveArgumentNumberOne,
veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy,
artichokeDescriptorAdapterIterator) {
// ...
}
関数の呼び出し自体がインデントされている場合、元の文もしくは関数呼び出しの開始位置を基準にスペース4つ分インデントしてかまいません。次の例は全て許容されるスタイルです:
if (veryLongFunctionNameA(
veryLongArgumentName) ||
veryLongFunctionNameB(
veryLongArgumentName)) {
veryLongFunctionNameC(veryLongFunctionNameD(
veryLongFunctioNameE(
veryLongFunctionNameF)));
}
無名関数の引渡し
関数呼び出しの引数リスト内で無名関数を宣言する場合、無名関数の内容は、文全体の左端からスペース2つ分、もしくは無名関数の宣言の左端からスペース2つ分インデントします。これは無名関数の内容を読みやすくする(関数全体が画面の右側に圧し潰された状態にならないようにする)ためです。
prefix.something.reallyLongFunctionName('whatever', function(a1, a2) {
if (a1.equals(a2)) {
someOtherLongFunctionName(a1);
} else {
andNowForSomethingCompletelyDifferent(a2.parrot);
}
});
var names = prefix.something.myExcellentMapFunction(
verboselyNamedCollectionOfItems,
function(item) {
return item.name;
});
goog.scopeによる別名
Closure Libraryが提供するgoog.scopeを使うと、名前空間を含むシンボルの長い名前を短く置き換えることができます。
goog.scopeの呼び出しは、1ファイルにつき1度だけ、グローバルスコープ内で行います。
呼び出しの開始部分goog.scope(function() {は、goog.provide、goog.require、トップレベルコメントの後に、正確に1行空けてから記述しなければなりません。呼び出しはファイルの最終行で閉じられなければなりません。またこのとき// goog.scopeというコメントを、ステートメント末尾のセミコロンからスペース2つ空けて追記してください。
C++の名前空間と同様に、goog.scopeの配下のコードはインデントしません。各行の先頭から記述を続けます。
作成した別名を、別のオブジェクト(例えば、ほとんどのコンストラクタ、列挙型、名前空間)へ再割り当てしないでください。次のようにしてはいけません(コンストラクタへの別名のつけ方は、下を参照してください):
goog.scope(function() {
var Button = goog.ui.Button;
var Button = goog.ui.Button;
Button = function() { ... };
...
...
別名は、シンボルの完全名の最後の部分と同じでなければなりません。
goog.provide('my.module.SomeType');
goog.require('goog.dom');
goog.require('goog.ui.Button');
goog.scope(function() {
var Button = goog.ui.Button;
var dom = goog.dom;
// コンストラクタ宣言より後に別名化します。
my.module.SomeType = function() { ... };
var SomeType = my.module.SomeType;
// いつもどおり、プロトタイプメソッドを宣言します:
SomeType.prototype.findButton = function() {
// 上で別名化したButton
this.button = new Button(dom.getElement('my-button'));
};
...
}); // goog.scope
行折り返しのインデント
配列リテラルとオブジェクトリテラル、無名関数を除き、行の折り返しは全て'兄弟'の式に対し左揃えでインデントするか、'親'の式に対しスペース4つ(2つではなく)分深くインデントします(ここでいう'兄弟'や'親'は、丸括弧のネストの深さを指します)。
someWonderfulHtml = '' +
getEvenMoreHtml(someReallyInterestingValues, moreValues,
evenMoreParams, 'a duck', true, 72,
slightlyMoreMonkeys(0xfff)) +
'';
thisIsAVeryLongVariableName =
hereIsAnEvenLongerOtherFunctionNameThatWillNotFitOnPrevLine();
thisIsAVeryLongVariableName = siblingOne + siblingTwo + siblingThree +
siblingFour + siblingFive + siblingSix + siblingSeven +
moreSiblingExpressions + allAtTheSameIndentationLevel;
thisIsAVeryLongVariableName = operandOne + operandTwo + operandThree +
operandFour + operandFive * (
aNestedChildExpression + shouldBeIndentedMore);
someValue = this.foo(
shortArg,
'Some really long string arg - this is a pretty common case, actually.',
shorty2,
this.bar());
if (searchableCollection(allYourStuff).contains(theStuffYouWant) &&
!ambientNotification.isActive() && (client.isAmbientSupported() ||
client.alwaysTryAmbientAnyways())) {
ambientNotification.activate();
}
空行
論理的に関連性のあるコードをまとめるため、空行を使ってください。例えば次のようにします:
doSomethingTo(x);
doSomethingElseTo(x);
andThen(x);
nowDoSomethingWith(y);
andNowWith(z);
二項または三項の演算子
演算子は必ず先行する行の側に置きます。それを除くと、改行やインデントは他のGoogleスタイルガイドと同じルールに従います。当初この演算子の配置はセミコロンの自動挿入への配慮から決定されましたが、実際にはセミコロンが二項演算子の前に挿入されることはありません。しかし一貫性を保つため、新しいコードもこのスタイルを守ってください。
var x = a ? b : c; // 収まるなら1行にまとめます。
// 4文字分のインデントはOKです。
var y = a ?
longButSimpleOperandB : longButSimpleOperandC;
// 最初のオペランドの位置までインデントするのもOKです。
var z = a ?
moreComplicatedB :
moreComplicatedC;
ドット演算子も同じです。
var x = foo.bar().
doSomething().
doSomethingElse();
丸括弧
必要なときだけ使います。
丸括弧は控えめに、基本的には文法やセマンティクスが求める場合にだけ使います。
delete、typeof、voidのような単項演算子やreturn、throwのようなキーワードの後ろで丸括弧を使ってはいけません(case、in、newで使わないのと同様です)。
文字列
"より'を使います。
一貫性の点で、ダブルクォート(")よりもシングルクォート(')の方が好ましいといえます。HTMLを含む文字列を作成する際には、シングルクォートの方が便利です:
var msg = 'This is some HTML';
可視性(privateまたはprotectedなフィールド)
JSDocアノテーションの@privateと@protectedの利用を推奨します。
JSDocアノテーションの@privateと@protectedを利用し、クラス、関数、プロパティの可視性を明示することを推奨します。
Compilerのフラグ--jscomp_warning=visibilityを有効にすると、可視性の違反について警告が出力されます。
Closure Compiler Warnings
を参照してください。
グローバルな変数や関数に付けられた@privateは、同じファイル内のコードだけがそれらにアクセス可能であることを表します。
@privateが付けられたコンストラクタは、それをインスタンス化できるのが同じファイル内のコードとそれ自身の静的またはインスタンスメンバだけであることを表します。@privateコンストラクタは、同じファイルからであればパブリックで静的なプロパティとしてアクセスできます。またinstanceof演算子もそれらにアクセスできます。
グローバルな変数、関数、コンストラクタに対して@protectedアノテーションは絶対に付けられません。
// ファイル 1// AA_PrivateClass_ と AA_init_ はグローバルでありかつ同一ファイル内に存在するため、
// 互いにアクセス可能です。
/**
* @private
* @constructor
*/AA_PrivateClass_ = function() {
};
/** @private */function AA_init_() {
return new AA_PrivateClass_();
}
AA_init_();
@privateなプロパティにアクセス可能なのは、同じファイルに存在する全てのコードと、もしそのプロパティがクラスに属しているのであれば、そのプロパティを"所有"するクラスの全ての静的メソッドとインスタンスメソッドです。それらは別のファイルに存在するサブクラスからはアクセスできず、オーバーライドされることもありません。
@protectedなプロパティにアクセス可能なのは、同じファイルに存在する全てのコードと、そのプロパティを"所有"するクラスとそのサブクラスの全ての静的メソッドとインスタンスメソッドです。
これらのセマンティクスはC++やJavaのそれとは違いがある点に注意してください。例えばアクセス権限は、同じクラスやクラス階層に属するものだけでなく、同じファイル内に存在する全てのコードに与えられています。またC++とは異なり、プライベートなプロパティはサブクラスからオーバーライドできません。
// ファイル 1
/** @constructor */AA_PublicClass = function() {/** @private */this.privateProp_ = 2;
/** @protected */this.protectedProp = 4;
};
/** @private */AA_PublicClass.staticPrivateProp_ = 1;
/** @protected */AA_PublicClass.staticProtectedProp = 31;
/** @private */AA_PublicClass.prototype.privateMethod_ = function() {};
/** @protected */AA_PublicClass.prototype.protectedMethod = function() {};
// ファイル 2
/**
* @return {number} 1列に並べたカモの数。
*/AA_PublicClass.prototype.method = function() {
// これら2つのプロパティへのアクセスは正当です。
return this.privateProp_ + AA_PublicClass.staticPrivateProp_;
};
// ファイル 3
/**
* @constructor
* @extends {AA_PublicClass}
*/AA_SubClass = function() {
// protectedな静的プロパティへの正当なアクセス。
AA_PublicClass.staticProtectedProp = this.method();
};
goog.inherits(AA_SubClass, AA_PublicClass);
/**
* @return {number} 1列に並べたカモの数。
*/AA_SubClass.prototype.method = function() {
// protectedなインスタンスプロパティへの正当なアクセス。
return this.protectedProp;
};
JavaScriptでは、ある型(AA_PrivateClass_のような)とその型のコンストラクタが区別されないことに注意してください。publicな型とprivateなコンストラクタを両立させる方法はありません。(可視性のチェックを破ってコンストラクタに別名をつけるのは簡単だからです)
JavaScriptのデータ型
Compilerによる型の強制を推奨します。
詳細はこちらのページを参照してください。
コメント
JSDocを使ってください。
詳細はこちらのページを参照してください。
goog.provideによる依存関係の規定
あるクラスに含まれるメンバ(列挙型や内部クラスなど)は全て、同じファイル内で定義しましょう。こうすると、1つのファイル内でgoog.provideが規定するのは最上位のクラスだけになるはずです。
こうします:
goog.provide('namespace.MyClass');
こうはしません:
goog.provide('namespace.MyClass');
goog.provide('namespace.MyClass.Enum');
goog.provide('namespace.MyClass.InnerClass');
goog.provide('namespace.MyClass.TypeDef');
goog.provide('namespace.MyClass.CONSTANT');
goog.provide('namespace.MyClass.staticMethod');
goog.provide('namespace.MyClass.Enum');
goog.provide('namespace.MyClass.InnerClass');
goog.provide('namespace.MyClass.TypeDef');
goog.provide('namespace.MyClass.CONSTANT');
goog.provide('namespace.MyClass.staticMethod');
名前空間のメンバは個々に規定します:
goog.provide('foo.bar');
goog.provide('foo.bar.method');
goog.provide('foo.bar.CONSTANT');
コンパイル
必須です。
クライアントに配信される全てのコードは、Closure CompilerのようなJSコンパイラによってコンパイルされていなければなりません。
ヒントとテクニック
JavaScriptについてもう少しだけ。
論理値表現
以下の値は、論理値の表現としてはすべてfalseです:
- null
- undefined
- ''(空文字)
- 数値の0
でも気をつけてください。以下の値は全てtrueです:
- 文字列の'0'
- [](空の配列)
- {}(空のオブジェクト)
以上から、下のコードを書き換えてみましょう:
while (x != null) {
これはもっと短くできます(ただしxが 0、空文字、falseを取らないことが条件です):
while (x) {
また、文字列がnullでも空文字でもないことをチェックしたいとします。こうも書けますが:
if (y != null && y != '') {
下の方が短くてもっといいでしょう:
if (y) {
警告: 論理値表現の結果は直感と異なることがよくあります。いくつか例を示します:
- Boolean('0') == true
'0' != true - 0 != null
0 == []
0 == false - Boolean(null) == false
null != true
null != false - Boolean(undefined) == false
undefined != true
undefined != false - Boolean([]) == true
[] != true
[] == false - Boolean({}) == true
{} != true
{} != false
条件(三項)演算子 (?:)
こう書く代わりに:
if (val) {
return foo();
} else {
return bar();
}
こう書くことができます:
return val ? foo() : bar();
三項演算子はHTMLを作成するときにも便利です:
var html = '<input type="checkbox"' +
(isChecked ? ' checked' : '') +
(isEnabled ? '' : ' disabled') +
' name="foo">';
&& と ||
これらの二項演算子は短絡演算子です。最後に評価された項目の結果を式全体の評価とします。
||は「デフォルト」演算子とも呼ばれます。その理由は:
/** @param {*=} opt_win */
function foo(opt_win) {
var win;
if (opt_win) {
win = opt_win;
} else {
win = window;
}
// ...
}
var win;
if (opt_win) {
win = opt_win;
} else {
win = window;
}
// ...
}
上のコードを、こう書き換えられるからです:
/** @param {*=} opt_win */function foo(opt_win) {
var win = opt_win || window;
// ...
}
&&もまた、コードを短くするのに役立ちます。例えば下のように書く代わりに:
if (node) {
if (node.kids) {
if (node.kids[index]) {
foo(node.kids[index]);
}
}
}
if (node.kids) {
if (node.kids[index]) {
foo(node.kids[index]);
}
}
}
こうできます:
if (node && node.kids && node.kids[index]) {
foo(node.kids[index]);
}
あるいは:
var kid = node && node.kids && node.kids[index];
if (kid) {
foo(kid);
}
とはいえ、これは少々やりすぎでしょう:
node && node.kids && node.kids[index] && foo(node.kids[index]);
ノードリストへの反復処理
多くの場合ノードリストはフィルタ付きのイテレータとして実装されています。これは、lengthのようなプロパティを参照するときのオーダーが O(n) であり、反復処理中にlengthを再計算し続けると、そのオーダーは O(n^2) となるということを意味しています。
var paragraphs = document.getElementsByTagName('p');
for (var i = 0; i < paragraphs.length; i++) {
doSomething(paragraphs[i]);
}
for (var i = 0; i < paragraphs.length; i++) {
doSomething(paragraphs[i]);
}
上のコードはこう書き換えたほうが優れています:
var paragraphs = document.getElementsByTagName('p');
for (var i = 0, paragraph; paragraph = paragraphs[i]; i++) {
doSomething(paragraph);
}
要素が論理値のfalseとして評価される値を持たなければ、上のコードは全てのコレクションや配列で正常に動作します。
あるノードの子ノードに対して反復処理を行う際には、firstChildプロパティとnextSiblingプロパティを使うこともできます:
var parentNode = document.getElementById('foo');
for (var child = parentNode.firstChild; child; child = child.nextSibling) {
doSomething(child);
}
* 間違い -- こうしてはいけません。
*/