タイトル画像
サイトタイトル画像

ななめ読み JavaScriptの難解な関数表記・正規表現の理解

JavaScript独特な書き方で理解が困難かつWebを探してもまとまったサイトが無かったのでまとめました。

オブジェクト

オブジェクトリテラル

オブジェクトは複数のプロパティを含むコンテナであり、プロパティは名前と値で構成されている。

var flight = {
   airline: "Oceanic",
   departure: {
      IATA: "SYD",
      "city-name": "Sydney" // JavaScriptの名前規則に従わないはクォート「""」が必要
   },
   arrival: {
      city_name: "Los Angeless" // JavaScriptの名前規則に従う場合はクォート「""」が不要
   }
};

flight.departure.IATA // "SYD"が取得できる

関数

JavaScriptにおける関数はオブジェクトです。そのためそれ以外の値と同様に扱うことができます。
関数を変数やオブジェクト、配列に格納することもできます。

var add = function (a, b) {
   return a + b;
};

関数呼び出しパターン

メソッド内で別の変数を定義し、その中にthisの値を代入すれば、内部関数ではその変数を通じてthisの値にアクセスできるようになります。変数の名前は「that」とするのが通例です。

myObject.dobule = function () {
  var that = this; // 値の待機
  var helper = function () {
     that.value = add(that.value, that.value);
  };
  helper(); // helperを関数として呼び出す
}


myObject.dobule();
document.writeln(myObject.value);

変数型の拡張

標準で用意されている変数型を拡張することができる。

例1)数値の符号によって小数点以下の切り捨て、切り上げを決定する

Number.method('integer', function () {
   return Math[this < 0 ? 'ceil' : 'floor'](this);
});
document.writeln((10/3).integer()); // -3

例2)文字列の後ろにある空白を取り除く

String.method('trim', function () {
   return this.replace(/^\s+|\s+$g, '');
});
document.writeln('"' + " neat ".trim() + '"');

引数を複数利用

パラメータを限定しない関数を書くためには次のように書くことができる。

var sum = function() {
   var i, sum = 0;
   for (i = 0; i < arguments.length; i += 1) {
      sum += arguments[i];
   }
   return sum;
}

document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108

JavaScriptの設計時のミスにより、argumentsは本物の配列ではなく「配列に似た」オブジェクトです。このためlengthプロパティは存在するが、配列で利用できるメソッドはいずれも使えません。

コールバック

ネットワークやサーバの速度が遅かった場合、レスポンスの速度低下は受け入れがたいものになるでしょう。
よいアプローチは非同期のリクエストを行うことです。

カリー化

カリー化は関数に引数を固定して新しい関数を生成する

メモ化

メモ化は前回の操作の結果をオブジェクトに記憶しておくことができる

正規表現

例)URLにマッチする正規表現

var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

var url = "http://www.hoge.com:80/hage?q#fragment";
var result = parse_url.exec(url);

var names = ['url', 'scheme', 'slash', 'host', 'port', 'path', 'query', 'hash'];
var blanks = ' ';
var i;
for (i = 0; i < names.length; i += 1) {
   document.writeln(names[i] + ':' + blanks.substring(names[i].length) + result[i]);
}

結果

url:       http://www.hoge.com:80/hage?q#fragment
scheme:    http
slash:     //
host:      www.hoge.com
port:      80
path:      hage
query:     q
hash:      fragment
正規表現 説明
^ 文字の先頭。
文字列の先頭にURLではない文字がある場合に読み飛ばさないようにするため
(?:([A-Za-z]+):)?「:(コロン)」で終わるスキーム名にマッチする。
「(?:..)」は非キャプチャグループを意味する。
最後の「?」はそのグループが省略可能を意味する
(\/{0,3}) キャプチャグループ。
「\/」は「/(スラッシュ)」という文字に一致。
「{0,3}」は「/」が0回、1回、2回、3回のいずれかの回数連続していることを意味する
([0-9.\-A-Za-z]+)キャプチャグループ。
ホスト名にマッチする。
ホスト名は「-」を含む。
「-」は範囲を表すハイフンと区別するため「\-」のようにエスケープされている。
(?::(\d+))? ポート名。
省略可能。
「\d」は数字を表す。
(?:\/([^?#]*))? 省略可能。
文字クラス「^?#」の文字クラスは「?」と「#」を除く全ての文字を表している。
「*」はその文字クラスが0回(存在しない)か1回以上繰り返すことを意味する。
(?:\?([^#]*))? 省略可能。
「?」で始まるグループを意味する。
「^#」は「#」を除く0個以上の文字列をキャプチャする
(?:#(.*))? 省略可能。
「.」は改行文字以外の全ての文字にマッチする。
$ 「$(ドル)」は文字列の最後を意味する。

非キャプチャグールプにすることで「処理速度の向上」「どの部分をキャプチャするかの明確化」などの利点があるので場合によってい使い分けましょう。

正規表現のフラグ

フラグ 意味
g Global(複数回マッチする。この正確な意味はメソッドにより異なる)
i Insensitive(大文字・小文字を区別しない)
m Multiline(^と$が待つ記号にマッチする)

正規表現のキャプチャグループ

キャプチャグループ マッチした文字列はキャプチャされる。キャプチャグループには番号が振られる
非キャプチャグループ 「?:」で始まる。単なるマッチのみを行い、マッチした文字列をキャプチャしない
肯定先読み 「?=」で始まる。非キャプチャグループと似ているが、マッチした後テキストの中の走査位置がグループがマッチした場所まで巻き戻される
否定先読み 「?!」で始まる。肯定先読みグループと似ているが、走査が続行されるのはグループのマッチが失敗した場合のみである

最短一致

JavaScriptでは なるべくたくさんマッチしようとする [ 最長一致 ] となります。

表現 意味
{a} 直前の文字の a回にマッチ
{a,} 直前の文字の a回以上にマッチ
{a,b} 直前の文字の a回以上 b回以下にマッチ
{a,}? ?マークが付く事で最短を表現できる
{a,b}? ?マークが付く事で最短を表現できる
 s.str = s.str.replace(//gmi, ""); 

あい

たとえば、HTMLのタグを消す場合、このように書いたとします。(Perlの置換文法)

s/<.+>//g

これでタグは消えるのですが、タグだけではなく、タグに挟まれた間の文字まで消してしまいます。(下線部分)

<P>abc</P>

理由は最長一致モードで動作しているからです。
動作をみると、途中に「>」が出てきているのですが、そこでは止まらず、最後の「>」までマッチしています。
つまり最長一致とは、できるだけ長くマッチングさせるという意味になります。

これを防ぐためには、以下のように、「>」の前に「?」(最短一致記号)をつけます。

s/<.+?>//g

ひとつの文字や数字

.
c
[a-z0-9]
[^a-z0-9]
\w
\W
\d
\D
\s
\S
\n
\r
\t
\d
\o
\x811
\cx

数量子

? 直前の文字が0個または1個
* 直前の文字が0個以上
+ 直前の文字が1個以上
{a} 直前の文字がa個
{a,} 直前の文字がa個以上
{a,b} 直前の文字がa個以上b個以下
?? 直前の文字が0個または1個
*? 直前の文字が0個以上
+? 直前の文字が1個以上
{a}? 直前の文字がa個
{a,}? 直前の文字がa個以上
{a,b}? 直前の文字がa個以上b個以下

位置指定

^ 行頭
$ 行末
\b 単語の区切り
\B 単語の区切り以外

その他

\ 次のメタ文字をクォートする
() グループ化

特殊変数

\1、$1 はじめにマッチした項目
\2、$2 2番目にマッチした項目

メソッド

String

繰り返し同じ文字列を連結する

ある文字列のn回繰り返しを作る関数 (PHPでいうところのarray_repeat(), Perlで言うところの「"..." x n」、RubyやPythonで言うところの「"..." * n」)

Perlの場合は(5回「-」を連続する)

$toc .= '-' x 5;
concat_op

function concat_op(s, n) {
  var r = ;
  for (var i = n; --i >= 0;) {
    r += s;
  }
  return r;
}

一番ナイーブな実装。
join

function join(s, n) {
  var r = []; for (var i = n; --i >= 0;) {
    r.push(s);
  }
  return r.join();
}

これもまあ、ナイーブな感じの実装。

string.replace( searchValue, replaceValue )

replaceメソッドは文字列の検索と置換を行い、新しい文字を生成する。
引数searchValueには文字列か正規表現オブジェクトを指定できる。

もしreplaceValueが文字列だった場合、「$」という文字は特別な意味を持つ。

ドル文字シーケンス 置換されるもの
$$ $
$& マッチしたテキスト
$+数字 キャプチャグループのテキスト
$` マッチした部分の前にあるテキスト
$' マッチした部分の後ろにあるテキスト

コールバック関数を利用したreplaceの使い方

replace関数の第一引数には正規表現を指定できる。

また第二引数にはマッチした文字列を引数とするコールバック関数を指定できる。コールバック関数の戻り値でマッチした文字列が置換される。

var re = /javascript/gmi;
elementContent.replace(re, function($1) {
  return '' + $1 + ';
});
var str = "hoge123foo456-";
str = str.replace(/([a-z]+)([0-9]+)/g,function (total, alpha, num) {
    console.log("-------------1");
    console.log(total);
    console.log("-------------2");
    console.log(alpha);
    console.log("-------------3");
    console.log(num);
});

結果


---------1 hoge123
---------2 hoge
---------3 123
---------1 foo456
---------2 foo
---------3 456

グルーピングした文字列はコールバック関数での引数としてしか意味を持たない。置換対象ではないので注意。

JavaScriptでの「参照渡し」と「値渡し」

オブジェクト指向・クラス化

エラー情報

TypeError: undefined is not a function

var html = new Wiki2html("a");

var Wiki2html = function(str) {
    this.hoge = str;
}

順番の問題

var Wiki2html = function(str) {
    this.hoge = str;
}

var html = new Wiki2html("a");

JavaScript で関数を定義するのには,下記の3通りの手法があります。

  • 「function 文」による「関数定義」
  • 「function 演算子」による「関数式」
  • Function() コンストラクタ

Function() コンストラクタについては今回は触れていません。XXX() は「関数定義」,YYY は「関数式」になります。「関数定義」の場合,前述のように関数定義(実装)が前方参照可能になるという大きな違いがあります。

window.onload = hoge;
function hoge(){
  // ...
}

は成功するけど、

window.onload = hoge;
var hoge = function(){
  // ...
}

は undefined になっちゃうよ、という話。

原典というのは、もちろん、言わずと知れたECMAScript Language Specification (HTML 版、日本語版)。

読み進めたら、14. Program に次のような記述が。

最初に関数定義が処理されたあと、文が処理される模様。

FATAL ERROR: JS Allocation failed - process out of memory

一行コマンド

タグの中身だけを大文字から小文字に変換する

replace(/<\/?\s*\w+/g, function(m){return m.toLowerCase()})

桁あわせ(頭に0を付ける)

「n」から数値の桁数の足りない分の"0"を配列にして、「join()」で文字列にして数値と結合。

Number.prototype.fillZero = function(n) {
  return Array((n+1) - this.toString().split('').length).join('0') + this;
}
function formatNum(keta, num) {
  var src = new String(num);
  var cnt = keta - src.length;
  if (cnt <= 0) return src;
  while (cnt-- > 0) src = "0" + src; return src;
}

■[JavaScript][regex]Javascriptの正規表現置換で後方参照を使いたいAdd Stark_ikiC_LEhren

Javascriptの正規表現置換(replaceメソッド)で後方参照(RegExp.$1とか)を使いたいと思った時、

str = "aaaaaa123aaaaa456aaaa7890"
str1 = str.replace(/(\d+)/g, " (" + RegExp.$1 + ") ");

なんて書いてもうまくいきません。なぜなら、RegExp.$1は前回の検索時に最後にマッチした後方参照ですから。実際に試してみるとわかります。

str = "aaaaaa123aaaaa456aaaa7890"
str1 = str.replace(/(\d+)/g, " (" + RegExp.$1 + ") ");
str2 = str.replace(/(\d+)/g, " (" + RegExp.$1 + ") ");

こんな時は、無名関数を使って、こう書きます。

str = "aaaaaa123aaaaa456aaaa7890"
str1 = str.replace(/(\d+)/g, function(){return " (" + RegExp.$1 + ") "});

Permalink | コメント(6) | トラックバック(0) | 12:10 Javascriptの正規表現置換で後方参照を使いたいを含むブックマーク
コメントを書く

gotingotin 2009/02/20 09:14 "aaaaaa123aaaaa456aaaa7890".replace(/(\d+)/g, " ($&) ");

だともうちょっと楽なような。

seuzoseuzo 2009/02/20 09:54 あ、たしかにperl風に変数展開した方がわかりやすいかもしれません。
ありがとうございます。

IE特有のJavaScriptの挙動…名前空間の,(カンマ)

■名前空間について
名前空間とは

名前空間はソースコード上で冗長な命名規則を用いなくても名前の衝突が起こらないようにし、しかもそれを容易に記述できるようにするためだけの概念
簡単に書くと

例えばaction();という関数を使うとして、もうひとつ全く別のaction();という関数を作ると動作しなくなる。別の名前にすれば良いのだがソースが数千行にもなり、他人が書いたとしたら関数名を決めるだけで毎回全てのソースコードを調べなければいけない。名前空間があれば衝突を回避できる。
■JavaScriptの名前空間とは

C#やPerlなどは言語仕様として名前空間があるが、JavaScriptには無いのでJSON記法を利用し、

var nameSpace = {

   name : "JustOnePlanet",
   display : function(){
       alert(nameSpace.name);
   }

}

というように記載する
■IE特有のエラー

   display : function(){
       alert(nameSpace.name);
   }

   display : function(){
       alert(nameSpace.name);
   },

と誤ってカンマを書いてしまうとIEのみでエラーがでます。結局ミスタイプしただけですが、Firefoxだと問題なく動作します。(但し、カンマが無い方が正しい記述である)

WebFormの背景色の変更

javascript で普通に書けば、例えば、document.bgColor = "#FF0000"; のように書けると思うのですが、どうも効きません。

CSSでBODYのスタイルで background-color を設定しており、この指定をはずすと、背景色を変えることができるようです。

function chg_color()
{
document.body.style.backgroundColor = "#0000ff";
}

スポンサードリンク

オススメ書籍

HTML5、CSS3、JavaScript等の学習に役立つ書籍を紹介します。

人気コンテンツ

    Copyright ©2024 .(since )