はじめに
久々にjavascriptを弄ることになり、おさらいで各種書籍を読み直しています。
この際自分の中での暗黙知を明文化しておきたいと思い、しっくり来ているところだけピックアップしてみました。
(ほぼ自己メモです。)
for-inループ
- プロトタイプ連鎖からきたプロパティを除外するためhasOwnPropertyを使う
pattern00.js
// for-inループ
var man = {
hands: 2,
legs: 2,
heads: 1
};
for (var i in man) {
if (man.hasOwnProperty(i)) { // フィルタ
console.log(i, ":", man[i]);
}
}
forループ
- myarray.lengthに対してのキャッシュ(ループ毎に問い合わせるよりも相当速い)
- 単独varパターン(var宣言は1つに、そして関数先頭で)
- for末尾のカウンタインクリメントにi++は使わず、i += 1にする。(++,--の「過剰なトリック」を避けるため)
pattern01.js
function looper() {
var i = 0,
max,
myarray = [];
// ...
for (i=0, max = myarray.length; i < max; i += 1){
// myarray[i]に対する処理
}
}
switch
- switchとcaseを揃える(波括弧のインデントルールの例外)
- case内のコードはインデントする
- caseの最後はbreak; で終わらせる
- 意図的にbreakを省略して次のcaseに続けるのは避ける
- switchの最後はdefault: で終わらせる
pattern02.js
var inspect_me = 0,
result = '';
switch (inspect_me) {
case 0:
result = "zero";
break;
case 1:
result = "one;"
break;
default:
result = "unknown";
}
インデント
- 空白4個に揃える(JSLintのデフォルトなので)
- インデントが空白かタブか、インデント幅は?等の話題は宗教戦争。正解は無い。(が必ず統一すること)
- ifやforの中に文が1つしかなくても、波括弧は省略しない(文1カッコ無しだと、後々うっかり追加でインデント狂う副作用があるため)
pattern03.js
function outer(a, b) {
var c = 1,
d = 2,
inner;
if (a > b) {
inner = function() {
return {
r : c - d
};
};
} else {
inner = function() {
return {
r : c + d
};
};
}
}
命名規則
-
コンストラクタの頭文字は大文字
var adam = new Person();
function MyConstructor() {...}
-
関数名はキャメルケース
myFunction()
-
変数名は小文字アンダースコア区切り(関数名と見分ける事ができるため)
var favorite_bands = "hoge";
-
定数は大文字、単語区切りはアンダースコア
var MAX_WIDTH = 800;
コメント
- 習慣にするのが難しいけれど最も重要なのは、コメントを最新に保つこと
(古くなったコメントは誤解を生み、コメントが無い方がよっぽどましな場合もある) - 以下はJSDocの例
pattern04.js
// JSDoc記法
/**
* 文字列を反転させる
*
* @param {String} 反転させたい文字列
* @return {String} 反転された文字列
*/
var reverse = function(input) {
// ...
return output;
}
- YUIDocのデモはこちら
以下にも転記
pattern05.js
/**
* My JavaScript application
*
* @module myapp
*/
var MYAPP = {};
/**
* A math utility
* @namespace MYAPP
* @class math_stuff
*/
MYAPP.math_stuff = {
/**
* Sums two numbers
*
* @method sum
* @param {Number} a First number
* @param {Number} b The second number
* @return {Number} The sum of the two inputs
*/
sum : function(a, b) {
return a + b;
},
/**
* Multiplies two numbers
*
* @method multi
* @param {Number} a First number
* @param {Number} b The second number
* @return {Number} The two inputs multiplied
*/
multi : function(a, b) {
return a * b;
}
};
/**
* Constructs Person objects
* @class Person
* @constructor
* @namespace MYAPP
* @param {String} first First name
* @param {String} last Last name
*/
MYAPP.Person = function(first, last) {
/**
* Name of the person
* @property first_name
* @type String
*/
this.first_name = first;
/**
* Last (family) name of the person
* @property last_name
* @type String
*/
this.last_name = last;
};
/**
* Returns the name of the person object
*
* @method getName
* @return {String} The name of the person
*/
MYAPP.Person.prototype.getName = function() {
return this.first_name + ' ' + this.last_name;
};
コンストラクタ
- コンストラクタは単なる関数ですが、newを使って呼び出すことができる。
- newを付け忘れて呼ぶと構文エラーにも実行時エラーにもならないが、thisがグローバルオブジェクトを指してしまう。
コンストラクタ関数をnewを使って呼び出す時、関数内部では以下のようになります
- 空のオブジェクトが作成され、変数thisで参照される。thisはこの関数のプロトタイプを継承する。
- thisが参照するオブジェクトにプロパティとメソッドが追加される。
- thisが参照する新しく作られたオブジェクトは、関数の最後で暗黙に返される。
pattern06.js
var Person = function(name) {
this.name = name;
this.say = function() {
return "I am " + this.name;
};
};
上記例ではthisにメソッド say()を追加しているが、メモリが無駄なのでプロトタイプに追加すべき
pattern07.js
Person.prototype.say = function () {
return "I am " + this.name;
};