Translate

2011年3月13日日曜日

javascriptパターンのメモ

■グローバルオブジェクトへのアクセス
グローバルオブジェクトへのアクセスは、windowオブジェクトのプロパティにアクセスするとよいのね。
var str = "Global!";

function abc(){
  var str = "Local!";
  console.log(str);        //Local!
  console.log(window.str); //Global!
}

abc();

ブラウザ環境では、windowオブジェクトからグローバルオブジェクトにアクセスできるがそれ以外の環境ではwindowオブジェクトが使えない可能性もあるため、ハードコードせずにアクセスするには下記のようにするとよいのね。
var global = (function() {
  return this;
}());

上のコードを書き直すとこーなる。
var str = "Global!";

function abc(){
  var str = "Local!";
  console.log(str);

  var global = (function(){return this}());
  console.log(global.str);
}

abc();



■varのまとめた宣言
function func(){
  var a = 1,
  b = 2,
  sum = a + b;
}

■for ループ
・配列の長さをキャッシュして無駄を減らす
var marray = [1, 2, 3, 4, 5];
  for (var i = 0, max = myarray.length; i < max; i++){
  console.log(marray[i]);
}
・変数を減らし、0と比較することで無駄をさらに減らす
var marray = [1, 2, 3, 4, 5];
  for (var i = marray.length; i--;){
  console.log(marray[i]);
}
1番目の項目でiに配列の要素数を入力して初期化 2番目の項目でtrue or false(0以上かどうか)を判断してから--で要素数を減らす 3番目の項目不要なので特になし。 見慣れた形で表現するのであれば・・・ 下のように書くのと同等。
var myarray = [1, 2, 3, 4, 5];
for (var i = myarray.length; -1 < i; i--){
  console.log(i);
}
上記方法の別解として
var myarray = [1, 2, 3, 4, 5],
i = myarray.length;
while(i--){
  console.log(myarray[i]);
}

■for inループ プロトタイプにぶらさがっているプロパティを除外するために hasOwnProperty()を利用
var hoge = {
  a: "aa",
  b: "bb",
  c: "cc"
};
for(var i in hoge){
  if(hoge.hasOwnProperty(i)){
    console.log(i, ":", hoge[i]);
  }
}

ただし、うっかりhasownPropertyを上書きなんかするとプロトタイプにぶらさがっているか調べることができなくなる。
(そんなことする人いないと思うが・・・)

var hoge = {
  a: "aa",
  b: "bb",
  c: "cc",
  hasOwnProperty: function(temp){ alert(temp); return false } //うっかり上書き
};

for(var keu in hoge){
  if(hoge.hasOwnProperty(key)){ //自分でfalseを返す関数として作ってしまったので期待どおりの動きをしない。
    console.log(key, ":", hoge[key]);
  }
}

このような事態をエスケープするのなら、下記のやり方がある。
var hoge = {
  a: "aa",
  b: "bb",
  c: "cc",
  hasOwnProperty: function(i){ false }
};

for(var key in hoge){
  if(hoge.hasOwnProperty(key)){
    console.log("[hasOwnProperty]", key, ":", hoge[key]);
  }else if(Object.prototype.hasOwnProperty.call(hoge, key)){
    console.log("[call]", key, ":", hoge[key]);
  }
}
結果は、
[call]a:aa
[call]b:bb
[call]c:cc
[call]hasOwnProperty:function()


上記をキャッシュして速度あっぷ
var key,
    hasOwn = Object.prototype.hasOwnProperty;
for(key in hoge){
  if(hasOwn.call(hoge, key)){
    console.log(key, ":", hoge[key]);
  }
}

■暗黙の型変換を避ける
var zero = 0;
if (zero === false){
  //実行されない
}

if(zero == false){
  //実行される
}

■parseInt()による型変換の注意点
ES3では0で始まる文字列はparseIntの基数を省略すると8進数処理をしてしまう。
この仕様は、ES5では変更となったが予期せぬ不具合に合わないように必ずparseInt()
の基数は指定する。
var month = "06",
    year  = "09";
month = parseInt(month, 10);
year  = parseInt(year,  10);

//parseInt()は、構造解析を行うのでNumberの方が速くなることがある。
Number("09") //9

■ドキュメントを書く
JSDoc Toolkit
YUIDoc

■コードを圧縮する
YUICompressor / Closure Compiler

------------------------------------------------------------------------------------

■オブジェクトリテラル記法
{}で書く。
var bar = {};
var foo = {name: "hoge", func: function(){ return this.name }};

■カスタムコンストラクタ
コンストラクタは、return を省略してもオブジェクトを返す。
var AC = function(animal){
  this.animal = animal;
  this.greet  = function(){
    switch (this.animal){
      case "dog":
        return "こんばんワン";
      case "rabit":
        return "ありがとウサギ";
      default:
        return "ぽぽぽぽーん";
    }
  };
  //return this; と書く必要がない。
};
var CM = new AC("dog");
alert(CM.greet());//犬に挨拶

■コンストラクタの返すものは、オブジェクトならなんでもよい。

var Obj = function(){
this.bar = "foo";

var that = {};
that.bar = "hogehoge";
return that; //ほっておくとthisをかえされちゃうので、明示的に違うオブジェクトを返す
}

var test = new Obj;
console.log(test.bar); //hogehoge を返す

■new忘れ防止
newを呼び忘れると本来オブジェクトを指すはずのthisがグローバルにアクセスしてしまうということで
コンストラクタ自体でnewで呼ばれたか精査して呼ばれてないと改めて自分自身をnewで呼んでしまうパターン。

function HogeHoge(){
  if(!(this instanceof HogeHoge)){
    return new HogeHoge();
  }
  this.bar = "foo";
}

//HogeHogeを利用するともれなくsayが参照できる。
HogeHoge.prototype.say = function(){ alert("Hellow HogeHoge!") };

var newObj    = new HogeHoge(),
    notNewObj = HogeHoge();

console.log(newObj.bar);
console.log(notNewObj.bar);

newObj.say();
notNewObj.say();

--------------------------------------------------------------------

■配列リテラル
var a = [];
console.log(typeof a);//object
console.log(a.constructor === Array);//true

■配列の検査
×instanceof Array → IEのいくつかのバージョンである条件かで正しく動かない
ECMAScript 5ではArray.isArray()があるが使えない場合以下のようにするとよいらしい

if(typeof Array.isArray === "undefined") {
  Array.isArray = function (arg) {
    return Object.prototype.toString.call(arg) === "[object Array]";
  }
}

■JSON
最近のブラウザならJSONくらい実装されているので、自前でevalするようなことせず
JSON.stringify(オブジェクト); //JSONデータへ変換
JSON.parse(JSONデータ); //JSONデータをオブジェクトへ変換

非対応ブラウザなら、JSON.orgライブラリの
http://www.json.org/json2.jsを使うとよい。

■正規表現リテラル
var re = /正規表現/gmi;

var test = "str";
if (test.match(re)){
  console.log("match");
}else{
  console.log("no match");
}

■Error
alert("普通の処理");
try{
  throw{
    name: "ErrorType",
    message: "手抜きによるエラーです",
    errFunc: test
  };
}catch (e) {
  alert(e.message);
  e.errFunc(e);
}

alert("end");

function test(e){
  alert(e.name);
}


--------------------------------------------------------------------

■関数をすぐ実行する
(function(){alert("test")}());

■関数の巻き上げ
関数宣言は、巻き上げされるが、関数式は巻き上げされない。
(function(){
  functionDeclaration(); //関数宣言は、実行より後で宣言しても巻き上げられ実行される
  functionExpression();  //関数式には、巻き上げて実行されることはない。
  
  function functionDeclaration(){alert("関数宣言")};
  var functionExpression = function(){alert("関数式")};
}());

■関数を返す
var InitCounter = function() {
  var Count = 0;
  return function() { return Count++ };
}

var counter = InitCounter();

for(var i=0; i<5; i++){
  console.log(counter());
}
■自己定義関数 ■グローバルオブジェクトへのアクセス グローバルオブジェクトへのアクセスは、windowオブジェクトのプロパティにアクセスするとよいのね。
var str = "Global!";

function abc(){
  var str = "Local!";
  console.log(str);        //Local!
  console.log(window.str); //Global!
}

abc();
ブラウザ環境では、windowオブジェクトからグローバルオブジェクトにアクセスできるがそれ以外の環境ではwindowオブジェクトが使えない可能性もあるため、ハードコードせずにアクセスするには下記のようにするとよいのね。
var global = (function() {
  return this;
}());
上のコードを書き直すとこーなる。
var str = "Global!";

function abc(){
  var str = "Local!";
  console.log(str);

  var global = (function(){return this}());
  console.log(global.str);
}

abc();
■varのまとめた宣言
function func(){
  var a = 1,
  b = 2,
  sum = a + b;
}
■for ループ ・配列の長さをキャッシュして無駄を減らす
var marray = [1, 2, 3, 4, 5];
  for (var i = 0, max = myarray.length; i < max; i++){
  console.log(marray[i]);
}
・変数を減らし、0と比較することで無駄をさらに減らす
var marray = [1, 2, 3, 4, 5];
  for (var i = marray.length; i--;){
  console.log(marray[i]);
}
1番目の項目でiに配列の要素数を入力して初期化 2番目の項目でtrue or false(0以上かどうか)を判断してから--で要素数を減らす 3番目の項目不要なので特になし。 見慣れた形で表現するのであれば・・・ 下のように書くのと同等。
var myarray = [1, 2, 3, 4, 5];
for (var i = myarray.length; -1 < i; i--){
  console.log(i);
}
上記方法の別解として
var myarray = [1, 2, 3, 4, 5],
i = myarray.length;
while(i--){
  console.log(myarray[i]);
}
■for inループ プロトタイプにぶらさがっているプロパティを除外するために hasOwnProperty()を利用
var hoge = {
  a: "aa",
  b: "bb",
  c: "cc"
};
for(var i in hoge){
  if(hoge.hasOwnProperty(i)){
    console.log(i, ":", hoge[i]);
  }
}
ただし、うっかりhasownPropertyを上書きなんかするとプロトタイプにぶらさがっているか調べることができなくなる。 (そんなことする人いないと思うが・・・)
var hoge = {
  a: "aa",
  b: "bb",
  c: "cc",
  hasOwnProperty: function(temp){ alert(temp); return false } //うっかり上書き
};

for(var keu in hoge){
  if(hoge.hasOwnProperty(key)){ //自分でfalseを返す関数として作ってしまったので期待どおりの動きをしない。
    console.log(key, ":", hoge[key]);
  }
}
このような事態をエスケープするのなら、下記のやり方がある。
var hoge = {
  a: "aa",
  b: "bb",
  c: "cc",
  hasOwnProperty: function(i){ false }
};

for(var key in hoge){
  if(hoge.hasOwnProperty(key)){
    console.log("[hasOwnProperty]", key, ":", hoge[key]);
  }else if(Object.prototype.hasOwnProperty.call(hoge, key)){
    console.log("[call]", key, ":", hoge[key]);
  }
}
結果は、 [call]a:aa [call]b:bb [call]c:cc [call]hasOwnProperty:function() 上記をキャッシュして速度あっぷ
var key,
    hasOwn = Object.prototype.hasOwnProperty;
for(key in hoge){
  if(hasOwn.call(hoge, key)){
    console.log(key, ":", hoge[key]);
  }
}
■暗黙の型変換を避ける
var zero = 0;
if (zero === false){
  //実行されない
}

if(zero == false){
  //実行される
}
■parseInt()による型変換の注意点 ES3では0で始まる文字列はparseIntの基数を省略すると8進数処理をしてしまう。 この仕様は、ES5では変更となったが予期せぬ不具合に合わないように必ずparseInt() の基数は指定する。
var month = "06",
    year  = "09";
month = parseInt(month, 10);
year  = parseInt(year,  10);

//parseInt()は、構造解析を行うのでNumberの方が速くなることがある。
Number("09") //9
■ドキュメントを書く JSDoc Toolkit YUIDoc ■コードを圧縮する YUICompressor / Closure Compiler ------------------------------------------------------------------------------------ ■オブジェクトリテラル記法 {}で書く。
var bar = {};
var foo = {name: "hoge", func: function(){ return this.name }};
■カスタムコンストラクタ コンストラクタは、return を省略してもオブジェクトを返す。
var AC = function(animal){
  this.animal = animal;
  this.greet  = function(){
    switch (this.animal){
      case "dog":
        return "こんばんワン";
      case "rabit":
        return "ありがとウサギ";
      default:
        return "ぽぽぽぽーん";
    }
  };
  //return this; と書く必要がない。
};
var CM = new AC("dog");
alert(CM.greet());//犬に挨拶
■コンストラクタの返すものは、オブジェクトならなんでもよい。
var Obj = function(){
this.bar = "foo";

var that = {};
that.bar = "hogehoge";
return that; //ほっておくとthisをかえされちゃうので、明示的に違うオブジェクトを返す
}

var test = new Obj;
console.log(test.bar); //hogehoge を返す
■new忘れ防止 newを呼び忘れると本来オブジェクトを指すはずのthisがグローバルにアクセスしてしまうということで コンストラクタ自体でnewで呼ばれたか精査して呼ばれてないと改めて自分自身をnewで呼んでしまうパターン。
function HogeHoge(){
  if(!(this instanceof HogeHoge)){
    return new HogeHoge();
  }
  this.bar = "foo";
}

//HogeHogeを利用するともれなくsayが参照できる。
HogeHoge.prototype.say = function(){ alert("Hellow HogeHoge!") };

var newObj    = new HogeHoge(),
    notNewObj = HogeHoge();

console.log(newObj.bar);
console.log(notNewObj.bar);

newObj.say();
notNewObj.say();
-------------------------------------------------------------------- ■配列リテラル var a = []; console.log(typeof a);//object console.log(a.constructor === Array);//true ■配列の検査 ×instanceof Array → IEのいくつかのバージョンである条件かで正しく動かない ECMAScript 5ではArray.isArray()があるが使えない場合以下のようにするとよいらしい
if(typeof Array.isArray === "undefined") {
  Array.isArray = function (arg) {
    return Object.prototype.toString.call(arg) === "[object Array]";
  }
}
■JSON 最近のブラウザならJSONくらい実装されているので、自前でevalするようなことせず JSON.stringify(オブジェクト); //JSONデータへ変換 JSON.parse(JSONデータ); //JSONデータをオブジェクトへ変換 非対応ブラウザなら、JSON.orgライブラリの http://www.json.org/json2.jsを使うとよい。 ■正規表現リテラル var re = /正規表現/gmi;
var test = "str";
if (test.match(re)){
  console.log("match");
}else{
  console.log("no match");
}
■Error
alert("普通の処理");
try{
  throw{
    name: "ErrorType",
    message: "手抜きによるエラーです",
    errFunc: test
  };
}catch (e) {
  alert(e.message);
  e.errFunc(e);
}

alert("end");

function test(e){
  alert(e.name);
}
-------------------------------------------------------------------- ■即時関数 (function(){alert("test")})(); (function(message){alert(message)})("hoge"); var result = (function() { return new Date() })(); //戻り値を受け取る ■関数の巻き上げ 関数宣言は、巻き上げされるが、関数式は巻き上げされない。
(function(){
  functionDeclaration(); //関数宣言は、実行より後で宣言しても巻き上げられ実行される
  functionExpression();  //関数式には、巻き上げて実行されることはない。
  
  function functionDeclaration(){alert("関数宣言")};
  var functionExpression = function(){alert("関数式")};
}());
■関数を返す
var InitCounter = function() {
  var Count = 0;
  return function() { return Count++ };
}

var counter = InitCounter();

for(var i=0; i<5; i++){
  console.log(counter());
}
■条件分岐パターン ブラウザ判定のようにスクリプト実行内で一度しか発生しない分岐に対しては 関数内で毎回分岐をするのでなく、スクリプト実行時最初に1度だけ行う。 ■関数のメモ化 不要な繰り返し処理がある場合関数自体にキャッシュさせることで処理を早くする ■カリー化
function schonfinkelize(fn){
    var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1); //argumentsを配列として扱えるように変換する

    return function(){
        var new_args = slice.call(arguments),
            args = stored_args.concat(new_args);
        return fn.apply(null, args);
    };
}

function add(a, b, c, d, e){
    return a + b + c + d + e; 
}

var newadd = schonfinkelize(add, 1, 2, 3);
alert(newadd(4,5));

複数引数をもち、そのうちのいくつかがほぼ固定で扱われる場合
何度も同じ引数を渡すのでなく、固定部分だけ先に代入して別の関数を
こしらえ、それに対して代入を行う。

0 件のコメント: