windowオブジェクトについて

Posted コメントするカテゴリー: javascript

windowオブジェクトについて

クライアントサイドでのjavascriptでは、windowオブジェクトが一番最初のスコープチェーンと考えることができます。
windowオブジェクトのプロパティやメソッドは、グローバル変数、グローバル関数としてみることができます。

例えば次のような非常にシンプルなHTMLを作成し、main.js内にwindowオブジェクトのlocationプロパティに値を設定する。
というファイルを用意して、ブラウザからhtmlファイルにアクセスすると、locationに設定したURLを読み込む動きになります。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>

<script src="./main.js"></script>

</head>
<body>

test123

</body>
</html>
//指定URLへ遷移する
window.location = "https://google.com";

上記の例は、windowオブジェクトに対してのプロパティを操作しましたが、
メソッドも用意されています。

例えば、次のように書きます。

window.setTimeout(function() {
	console.log("set!");
}, 5000);

この場合は5秒後にconsoleログ出力が一度だけされます。

setTimeout命令(メソッド)により、引数に定義した無名関数が、第二引数の5000ミリ秒後に実行される。という仕組みです。

この時、「window.」と明示的に指定していますが、setTimeoutのみを記述しても動作します。
windowオブジェクトはグローバルオブジェクトなので、windowオブジェクトが持つメソッドもグローバルメソッド(グローバル関数)として使うことができます。

また、ブラウザ上で実行するjavascriptでは、下記のプログラム要素があります。

  • windowオブジェクト(上記以外の使い方)
  • style(css)の制御
  • DOMの制御
  • HTML5APIの使用
  • グラフィックの描画(canvas)
  • サウンドの使用(再生等)

上記は一部の要素ですが、このブログ内で一つ一つ動かしながら確かめていく予定です。

クライアントサイドjavascript

Posted コメントするカテゴリー: javascript

概要

これまではjavascriptの基本的な文法を中心に、サンプルコードを書いて試してきましたが、
ここからは少し実践的なjavascriptの用途について試していこうと思います。

ご存じの通り、javascriptの言語仕様の策定やブラウザのjavascriptエンジンのバージョンアップ、各種javascriptフレームワーク、そして書き方のスタイルの変化、等が早いスピードで進化しています。

実際にそれら全ての情報を収集し、常に最新な状態を理解することは非常に難しいですが、ポイントを押さえつつある程度の幅を持たせて実践的な書き方を試そうと思います。

try catchの複数の条件判定について

Posted コメントするカテゴリー: javascript

try catch の複数の条件判定について

これまでの投稿でtry catch 文については、深く掘り下げていませんたが、
try catchの基本的な書き方は以下のようになります。

try {
  //処理
} catch(e) {
  //エラー時の処理
} finally {
  //最終的に必ず実行する処理
}

まず、tryで通常の処理を行い、この中で何らかのエラーが発生した場合や、throw文によって例外が投げられると、catchブロックに処理が移ります。

この時、catch文を複数書くことができます。

try {
  //処理
} catch(e) {
  //エラー時の処理1
} catch(e) {
  //エラー時の処理2
} catch(e) {
  //エラー時の処理3
} finally {
  //最終的に必ず実行する処理
}

このように複数書く場合は、catch(e)の引数eについて、条件判定する書き方があります。

具体的には次のように書きます。

//書き方の例1
catch(e if e instanceof TypeError)

//書き方の例2
catch(e if e instanceof RangeError)

//書き方の例3
catch(e if e instanceof EvalError)

上記は3通りのcatch文の引数の判定方法を書きましたが、現状のjavascriptプログラミングでは複数のcatch文は推奨はされていませんし、ブラウザによっては正常に動作しない場合があるので、注意が必要です。

また、上記の

TypeError
RangeError
EvalError

等は、javascriptの標準ビルトインオブジェクトであり、その中の基本オブジェクトとしてあらかじめ定義されているものです。
(詳しくは、MDN web docsに記載されています)

上記以外には、上記の3通りのオブジェクトも含め、下記のようなものが定義されています。

EvalError
InternalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError

また、例のコードには書きませんでしたが、下記のような反映も書くことができます。

//tryブロックの中で、throw new Error("err1"); のように例外が投げられた場合
catch(e if e === "err1")

//throwされた例外が数値だった場合
catch(e if typeof e === "number")

いずれもcatchについての書き方になりますが、あまり複数のcatchを前提で開発を進めないほうが良いかもしれません。
またtry catch句について調べたら当記事を更新しようと思います。

ジェネレータを試してみます

Posted コメントするカテゴリー: javascript

概要

前回に投稿したジェネレータについて、実際にコードを書いて試してみます。
非常に簡単な例を書いてみます。

function* g_test(s)
{
	console.log(s);
	yield;
	
	console.log(s+1);
	yield;
	
	console.log(s+2);
}

//ジェネレータ関数に引数を渡して呼び出す
let gene = g_test(100);

//1回目の実行
gene.next();

//2回目の実行
gene.next();

//3回目の実行
gene.next();

上記を実行すると、次のようになります。

100
101
102

geneを生成した後、gene.next();とすることで、g_test関数を実行しています。
ここで、注意が必要な点は、functionの後に「*」をつけていることです。

function* g_test(s)

と記述することで、g_test()はジェネレータとしての振る舞いをする関数として定義されています。

また、ジェネレータから値を取り出す際には、関数内部で値を返すように書く方法があります。

次のようなサンプルを書いて、試してみます。

function* g_test(s)
{
	yield s;
	yield s+10;
	yield s+20;
}

//ジェネレータ関数に引数を渡して呼び出す
let gene = g_test(100);

//1回目の実行
console.log(gene.next());

//2回目の実行
console.log(gene.next());

//3回目の実行
console.log(gene.next());

上記の場合は、firefoxの開発ツールで確認したところ、下記のような出力がされます。

Object { value: 100, done: false }
Object { value: 110, done: false }
Object { value: 120, done: false }

注意する点は、ジェネレータの2回目の呼び出しと、3回目の呼び出し時に、
関数内で「yield 」の値を返しているという点です。

関数内でyieldを返すことにより、呼び出し元でObjectとして処理結果を受け取り、その内容を使用することが可能になります。

また、ジェネレータに対して、値を渡すことも可能なので、それはまた改めて試して投稿する予定です。

ジェネレータ

Posted コメントするカテゴリー: javascript

概要

javascript1.7(時期としては2006年以降)からジェネレータが実装されました。
javascriptの改定の歴史からすると、古い時期に実装されている機能ですので、お浚いを兼ねて試してみます。
ちなみに他の言語でもジェネレータという考え方があり、同様にyieldキーワードを利用する場面があります。

特徴を下記にまとめます
・ジェネレータはyieldキーワードを関数中で使用します。
・yieldは、関数から値を返します。
・呼び出し元に値を返した後も内部状態を保持します。
・ジェネレータ使用時は、yidldキーワードの実行有無に関わらずジェネレータ関数として振る舞います。
・functionキーワードを使って宣言します。

具体的なサンプルは次の投稿で試してみようと思います。

イテレータ(続き)

Posted コメントするカテゴリー: javascript

概要

前回投稿した反復処理のイテレータについて引き続き試してみます。
イテレータはコレクションに対してループ処理をする際、次の値が存在しない場合はStopIterationがスローされます。

前回試したイテレータのサンプルを少し改造して、次のようなコードを書きました。

function test2(start, end) {
	let sv = start;
	return {
		next: function() {
			if (sv > end) {
				throw StopIteration;
			} else {
				return sv++;
			}
			
		}
	};
}

//イテレータに対し引数を2つ設定し定義
let testval2 = test2(100, 105);

//定義したイテレータ内のnextを呼び出す
try {
	console.log(testval2.next());
} catch(e) {
	console.log(e);
}

try {
	console.log(testval2.next());
} catch(e) {
	console.log(e);
}

try {
	console.log(testval2.next());
} catch(e) {
	console.log(e);
}

try {
	console.log(testval2.next());
} catch(e) {
	console.log(e);
}


イテレータ内のnext()で、test2の第一引数と第二引数で開始値と終了値を指定し、
イテレータが呼ばれる回数が終了値(=endの値)を超えた時に例外「StopIteration」をスローしています。

ループ処理でtry catchを呼ぶ形のほうがコードの量が少なく綺麗になりますが、
※・・・上記のサンプルは挙動を確かめる(エラー発生行数)為にループはしていません。

注意点

イテレータ側で配列を扱う場合は、終わりの値が正しく設定されている場合は問題はないですが、
無限サイズの配列の場合は、際限なく処理されてしまいます。
実行時のメモリ消費に影響する為、startとendを決める仕様のほうが安全と言えます。

また、言語仕様として存在はしますが、使用については非推奨となっているので、注意が必要です。

反復制御について

Posted コメントするカテゴリー: javascript

概要

javascriptで反復制御をする場合の書き方をおさらいします。

for in 文はループ中にキーを出力します。

//for in ループ
let tmp = {a: 10, b: 20, c: 30}

//for in の場合
for(let v in tmp) console.log(v);

出力結果は

a
b
c

になります。

イテレータは、for in を拡張したものになります。
反復制御の際には、ある値のコレクションを対象とし、nextメソッドを使い、要素を巡回してアクセスします。
簡単な例は次のようになります。

//イテレータ
function test(strparam) {
	let V = strparam;
	return {
		next: function() {
			return V++;
		}
	};
}

let testval = test(100);
console.log(testval.next());
console.log(testval.next());
console.log(testval.next());

出力結果は

100
101
102

となります。

その他の反復制御については、別途掘り下げて投稿します。

分割代入

Posted コメントするカテゴリー: javascript

概要

分割代入について試してみます。
分割代入は、配列やオブジェクトを、1つまたは複数の変数に一度に代入します。
具体的には次のように書きます。

let [a, b, c] = [10, 20, 30];

console.log(a);
console.log(b);
console.log(c);

出力結果は

10
20
30

となります。

分割代入は、配列形式等の値を一度に代入が可能なので、等号記号「=」の左辺に配列を指定すると効果的です。

上記の例に少し手を加えてみます。

let [a, b, c] = [10, 20, 30];

//配列を分割代入を利用して、順序を変更して代入する
[a, b, c] = [b, a, c];

console.log(a);
console.log(b);
console.log(c);

この場合、出力結果は

20
10
30

になります。
見ての通りシンプルな動きになります。

次に、結果を配列で返す形にして、関数の戻り値を分割代入するサンプルを書いてみます。

function test1(x, y) {

	//引数を加算して配列で返す
	x = x + 100;
	y = y + 200;

	return [x, y];
}

let [d, e] = test1(10, 20)

console.log(d);
console.log(e);

出力結果は

110
220

になります。
配列で分割代入されていることがわかります。

代入する値の数が異なる場合

上記のサンプルでは、等号記号の左辺と右辺は同じ数でした。
では、代入する要素数が異なる場合はどのような挙動になるのか、試してみます。

//右辺が左辺よりも少ない場合
let [g, h, i] = [10, 20];

console.log(g);
console.log(h);
console.log(i);

//右辺が左辺よりも多い場合
let [j, k, l] = [30, 40, 50, 60];

console.log(j);
console.log(k);
console.log(l);

この場合の出力は、次のようになります。

10
20
undefined
30
40
50

左辺の変数iには、それに対する右辺の値が存在しない為、undefinedになります。

配列をネストしている場合

分割代入する配列がネストされている場合について試してみます。

let [m, n, o, [p, q], r] = [100, 101, 102, [201, 202], 103];

console.log(m);
console.log(n);
console.log(o);
console.log(p);
console.log(q);
console.log(r);

出力結果は

100
101
102
201
202
103

となります。

let キーワードについて

Posted コメントするカテゴリー: javascript

概要

これまでにも何度も出てきたletキーワードについておさらいをしてみます。
letは次のような画面で使用します。
・変数宣言
・ループ内でのカウンタとして
・ブロック文としてスコープを明示する
・式に付与したスコープを定義する

変数宣言はES6以降、varでの宣言は、letやconstで宣言するようになりました。
letは再定義不可の変数で、constは再代入不可の変数になります。

また、ループ内でのカウンタの利用としては、

for (var i = 0; i < x; i++) {

といったループの箇所で

for (let i = 0; i < x; i++) {

という記述になるような書き方になります。
また、letで宣言するより前の箇所で変数を使用するとundefinedになり、これはvarで宣言した時も同様になります。

letキーワードはES6から策定され、現在の開発ではvarよりもletを使うほうが主流の書き方になっています。
varでも動作することはしますが、今後の開発ではletで統一します。

RegExpオブジェクト

Posted コメントするカテゴリー: javascript

概要

javascriptで正規表現を取り扱うRegExpオブジェクトについて、調べてみます。

RegExpオブジェクトを使うとき、次の使い方があります。
・RegExp()コンストラクタ
・RegExpプロパティ
・RegExpメソッド

それぞれどういった使い方をするものか、順番に見ていきます。

RegExpコンストラクタについて

1つ(または2つ)の引数をもとに、RegExpオブジェクトを生成します。
正規表現を1番目の引数に指定し、2番目の引数は正規表現のフラグを書きます(省略可能)。

例として、RegExpコンストラクタの生成をしてみます。

let regcon = new RegExp("\\d", "g");
console.log(regcon);

上記のRegExpコンストラクタは数値にマッチする場合(フラグはg)を意味します。
「\」を2重に書く意図はエスケープ文字として動作する為、「\d」を正しく処理する為に2重に書きます。
また、ここで定義した「regcon」は正規表現オブジェクトとも言えます。
この正規表現オブジェクトを元に正規表現の処理を行います。

上記の例以外では、次のように書くことも可能です。

let regcon2 = new RegExp("BC", "g");
console.log(regcon2);

これは単純に文字列「AB」に対してマッチする場合の書き方です。
実際にマッチさせる場合のプログラムはこの投稿の後半にサンプルを書いてみます。(↑の記述だけでは、コンストラクタを生成しただけなので、何も処理を行いません)

RegExpプロパティについて

RegExpプロパティについて調べてみます。
javascriptのバージョンが年々アップされており、使用を推奨するプロパティと非推奨のプロパティが変化してきています。
詳しい解説は、こちらに解説されているので、詳細は割愛します。

RegExpプロトタイプオブジェクトのプロパティは次のようなものがあります。

RegExp.prototype.constructor
RegExp.prototype.flags
RegExp.prototype.dotAll
RegExp.prototype.global
RegExp.prototype.ignoreCase
RegExp.prototype.multiline
RegExp.prototype.source
RegExp.prototype.sticky
RegExp.prototype.unicode

また、非推奨のプロパティはこちらにまとめられていますので、詳細は省略します。

RegExpメソッドについて

RegExpメソッドについて、調べてみます。
メソッドというだけあって、正規表現のパターンマッチを実行する為に書きます。

RegExpのプロトタイプオブジェクトの仕様を調べてみると、次のようなものがあります。

RegExp.prototype.compile()
RegExp.prototype.exec()
RegExp.prototype.test()
RegExp.prototype[@@match]()
RegExp.prototype[@@matchAll]()
RegExp.prototype[@@replace]()
RegExp.prototype[@@search]()
RegExp.prototype[@@split]()
RegExp.prototype.toSource()
RegExp.prototype.toString()

それぞれの詳細な解説はこちらを参照するとわかりますが、
この中で頻繁に使われるものは「exec()」と「test()」です。
exec()メソッドは引数に文字列を指定してパターンマッチングを行います。
パターンマッチした場合は、match()メソッドと同様に配列を返し、
マッチしなかった場合はnullになります。

また、test()メソッドは引数に指定された文字列が、指定された正規表現とマッチするかどうかを返します。
マッチする場合はtrueを返し、しない場合はfalseを返します。
(詳しい仕様上の説明はここでは割愛します )

実際にサンプルを書いて動かしてみます

ここまでのRegExpの説明を元に簡単なサンプルを書いて動作させてみます。

次のサンプルコードを実行します。

let str3 =  "abc5def";
let regcon3 = new RegExp("\\d", "g");
console.log(regcon3.test(str3));

let str4 =  "abcdef";
let regcon4 = new RegExp("\\d", "g");
console.log(regcon4.test(str4));

結果は

true
false

という結果になります。
ここで出てくるtestはRegExpのメソッドとして機能します。
正規表現オブジェクトは文字列のパターンマッチングを行っています。
動作としてはmatch()となり、文字列の中に数値が含まれている場合にtrueが返ります。

では、上記の例で書いているtestメソッドをexecメソッドに変えて試してみます。

let str5 =  "abc5def";
let regcon5 = new RegExp("\\d", "g");
console.log(regcon5.exec(str5));

let str6 =  "abcdef";
let regcon6 = new RegExp("\\d", "g");
console.log(regcon6.exec(str6));

出力結果は次のようになります。

Array [ "5" ]
null

メソッド名の他は全て同じコードでしたが、メソッドがexecに変わった後の出力結果は異なります。

testメソッドでは文字列の検索をして存在の有無を判定結果を返していることに対し、
execメソッドでは検索でヒットした文字列を返しています。

この例と同様に、searchメソッド、replaceメソッド、splitメソッド等、RegExpオブジェクトとともに使用できるメソッドがあり、状況に応じて書きます。

パターンマッチング用文字列メソッド

Posted コメントするカテゴリー: javascript

概要

正規表現を使って、文字列を任意の条件でマッチさせて、検索や置換等を行う方法を試してみます。

パターンマッチによる検索

javascriptで文字列操作をする場合、非常に多岐に渡る方法があり、ここで一部を試して見ます。

下記のサンプルコードを書きます。

//文字列検索(対象文字列内のeの出現位置を返す)
let pattern_str = "abcdef";
console.log(pattern_str.search(/e/i));

//出現しない場合は-1を返す
let pattern_str2 = "abcd";
console.log(pattern_str2.search(/e/i));

出力結果は、それぞれ

4
-1

になります。
前者は「対象文字列内のeの出現位置を返す」ので、文字列eは対象文字列中の4つめを示します。
後者はマッチするものが無かった場合に-1になります。

パターンマッチによる置換

次に置換を試してみます。

//置換(文字列eが一致する場合、その文字を任意の文字列に置換する)
let pattern_str3 = "abcdef";
console.log(pattern_str3.replace(/e/i, "y"));

出力結果は

abcdyf

になります。
これは単純に文字列中の「e」がヒットし、それを「y」に置き換えています。

パターンマッチで検索したものを取得し、配列化する

次に文字列中にある条件のものを全て配列として返す場合です。

//正規表現で一致した結果を配列で返す
let pattern_str4 = "abc5def7";
console.log(pattern_str4.match(/\d/g));

出力結果は

Array [ "5", "7" ]

になります。(firefoxのログで確認した場合)

「\d」は数値を表し、パターンマッチ時にgオプションをつけることで、対象文字列中の全てのヒットした文字列に一致した文字列を返します。

簡単なサンプルのみになりますが、その他の文字列操作の正規表現は代表的なもので次のものがあります。

charAt
slice
split
substr
substring
toLowerCase
toUpperCase

これらはStringオブジェクトのメソッドなので、正規表現での文字列操作と合わせて使うと有効です。

正規表現のフラグ

Posted コメントするカテゴリー: javascript

概要

正規表現でパターンマッチする際に、フラグを指定することができます。

以下の3つのフラグがあります。

i
g
m

それぞれ、
iは大文字、小文字の区別をしない
gは一致する文字列をすべてにマッチ
mはマルチラインでマッチ

という意味になります。

実際にコードを書いて試してみます。

let test = /abcdef/;
let pattern_str = "ABCdef";
console.log(pattern_str.match(test));

let test2 = /abcdef/i;
let pattern_str2 = "ABCdef";
console.log(pattern_str2.match(test2));

let test3 = /abcdef/i;
let pattern_str3 = "ABCdefabcdef";
console.log(pattern_str3.match(test3));

let test4 = /abcdef/ig;
let pattern_str4 = "ABCdefabcdef";
console.log(pattern_str4.match(test4));

出力結果はそれぞれ以下のようになります。(firefoxの場合)

null
Array [ "ABCdef" ]
Array [ "ABCdef" ]
Array [ "ABCdef", "abcdef" ]

フラグのiとgをつけた場合、大文字小文字の区別をしないケースと、
対象の文字列中に複数回マッチするケースにそれぞれマッチすることがわかります。

正規表現のマッチ位置指定

Posted コメントするカテゴリー: javascript

概要

文字列のパターンマッチの方法として、前回の投稿は任意の文字列にマッチする方法を試しました。
正規表現を駆使すると文字列以外のマッチも多く使われます。

具体的には次の条件指定があります。

\s     //空白の文字列
\b     //単語の境界
\w     //任意の単語(単語文字列)
\W     //任意の単語文字以外の文字(境界)
^      //先頭
$      //末尾

次のサンプルコードを書いてみます。

//先頭と末尾を指定
let test = /^abcdef$/;

let pattern_str = "abcdef";

console.log(pattern_str.match(test));         //マッチする



//先頭(単語境界の区切り文字)を指定
let test2 = /\babcdef/;

let pattern_str2 = "abcdef";

console.log(pattern_str2.match(test2));       //マッチする



//単語境界以外(\B)を指定
let test3 = /\Babcdef/;

let pattern_str3 = "abcdef";

console.log(pattern_str3.match(test3));       //マッチしない



//単語境界以外(\B)を指定
let test4 = /\B[a]bcdef/;

let pattern_str4 = "testabcdef";

console.log(pattern_str4.match(test4));       //マッチする (abcdefとしてマッチ)



//単語境界以外(\B)を指定し、任意の文字aまたはkまたはwから始まるbcdefの文字列を指定
let test6 = /\B[akw]bcdef/;

let pattern_str6 = "testkbcdef";

console.log(pattern_str6.match(test6));       //マッチする (kbcdefとしてマッチ)




//空白の文字列を指定
let test7 = /\B[akw]bcdef/;

let pattern_str7 = "test kbcdef";

console.log(pattern_str7.match(test7));       //マッチしない




//空白の文字列(\s)を指定
let test8 = /test\sabcdef/;

let pattern_str8 = "test abcdef";

console.log(pattern_str8.match(test8));       //マッチする (test abcdefとしてマッチ)

出力結果は各console出力のコメント部分に記載した結果になります。

上記サンプルでは正規表現の一部で、詳しくは正規表現に特化した書籍やサイトで調べてプログラムを行います。

グループ化に関する記述

また、上記以外で、主にグループ化に使われる特殊文字の意味をまとめます。

|                 //左右どちらかに一致
(任意の文字)      //複数の項目をグループ化し、1つにまとめる(一致する文字を記憶する)
(?:任意の文字)    //複数の項目をグループ化し、1つにまとめる(一致する文字を記憶しない)
\N                //グループ化したもののN番目の文字列に一致する

「言明」について

さらに上記サンプルでは書いていなかった「言明」については、次のように書きます。

a(?=b)        //先読み言明          aにbが続く場合のみマッチ
a(?!b)        //否定先読み言明      aにbが続かない場合のみマッチ
(?<=b)a       //後読み言明          bにaが続く場合のみマッチ
(?<!b)a       //否定後読み言明      bにaが続かない場合のみマッチ

上記のルールを踏まえてサンプルコードを書いてみます。

//先読み言明
let test9 = /test(?=abcdef)/;

let pattern_str9 = "testabcdef";

console.log(pattern_str9.match(test9));       //マッチする(testとしてマッチ)



//否定先読み言明
let test10 = /test(?!abcdef)/;

let pattern_str10 = "testabcdef";

console.log(pattern_str10.match(test10));     //マッチしない



//後読み言明
let test11 = /(?<=abcdef)test/;

let pattern_str11 = "abcdeftest";

console.log(pattern_str11.match(test11));

//chromeではマッチする      ["test", index: 6, input: "abcdeftest", groups: undefined]
//firefoxではエラーになる   (SyntaxError: invalid regexp group)



//否定後読み言明
let test12 = /(?<!abcdef)test/;

let pattern_str12 = "abcdeftest";

console.log(pattern_str12.match(test12));


//chromeではマッチしない    ["test", index: 6, input: "abcdeftest", groups: undefined]
//firefoxではエラーになる   (SyntaxError: invalid regexp group)

出力結果は各consoleログの後に記載されているようになりますが、
注意が必要な点は、後読み言明の処理の場合、ブラウザの実行環境によりfirefoxの場合はエラーになります。

javascriptの実行処理がブラウザごとに完全に同一ではない点に注意する必要があります。

代替表現、グループ化

Posted コメントするカテゴリー: javascript

概要

正規表現の代替表現について、試してみます。
前回の投稿で下記のようなサンプルコードを書きました。

//先頭がaのものにマッチ
let test = /^a/;
let pattern_str = "abcdef";
console.log(pattern_str.match(test));

let pattern_str2 = "bcadef";
console.log(pattern_str2.match(test));

上記の場合は先頭がaの場合にマッチするという動きなので、
前者がマッチし、後者はマッチしません。

では、「先頭がaもしくはbのものにマッチする」という必要がある場合はどのように書くかというと、|を使います。
実際に書いて試してみます。

//|を使ってaとbの両方にマッチさせる
let test = /^a|^b/;

let pattern_str3 = "abcdef";
console.log(pattern_str3.match(test));

let pattern_str4 = "bcadef";
console.log(pattern_str4.match(test));

上記の出力の場合、出力結果は次のようになります。

Array [ "a" ]
Array [ "b" ]

|を使用すると一度のパターンマッチで複数の条件にマッチさせることができます。

グループ化

次に括弧を使った正規表現を試してみます。
括弧を使うとパターン文字列をグループ化してマッチさせることができます。

簡単な例を書いてみます。

let test_pattern = /a(b)/;
let str = "abcdef";
console.log(str.match(test_pattern));

出力結果は

Array [ "ab", "b" ]

になります。これはaと括弧内のbを組み合わせたabにマッチした結果と、
括弧内をグループ化してマッチした結果がそれぞれ出力されています。

次に上記の例を少し変更して次のコードを書いてみます。

let test_pattern4 = /a(b|c)/;
let str4 = "acbdef";
console.log(str4.match(test_pattern4));

let str5 = "abcdef";
console.log(str5.match(test_pattern5));

出力結果は

Array [ "ac", "c" ]
Array [ "ab", "b" ]

になります。
この場合は、/a(b|c)/と記述することで、aの後に続く文字列として、括弧内のabまたはacにマッチし、またそれぞれbとcもマッチします。

パターンの繰り返し

Posted コメントするカテゴリー: javascript

概要

正規表現の繰り返しについてです。
パターンマッチの対象文字列が任意のn桁の場合、n桁分のマッチ条件を正規表現で書くこともできますが、効率が良くないです。
その場合に繰り返し文字をマッチする為の記述にします。

具体的には次のような書き方をします。

{n, m} //n回からm回まで繰り返す
{n, }  //n回以上繰り返す
{n}    //n回のみ繰り返す
?      //0回または1回のみ
+      //1回以上繰り返す
*      //ゼロ回以上繰り返す

実際にコードを書いて動かしてみます

簡単なサンプルコードを書いてみます。

let test = /^a/;
let pattern_str = "abcdef";
console.log(pattern_str.match(test));

これを動かしてみると出力は「Array [ “a” ]」という結果になります。
これは対象の文字列の先頭がaだった為にマッチしています。

次に対象の文字列を少し変えてみます。

let test2 = /^a/;
let pattern_str2 = "bcadef";
console.log(pattern_str2.match(test2));

この場合は、出力は「null」になり、何もヒットしません。
先頭の文字列がbなので、ヒットしないというシンプルな動きになります。

では「n回からm回までを繰り返す」という例を書いてみます。
上記のサンプルを変更して次のコードを書いてみます。

let test3 = /a{4,7}/;
let pattern_str3 = "bcaaadef";
console.log(pattern_str3.match(test3));

出力は「null」になります。
これは文字列aが4回から7回まで繰り返している場合にマッチします。
対象となる文字列の中にaは3回繰り返していますが、この場合の条件には一致しないのでnullになりました。

では、さらに上記のコードを次のように書いてみます。

let test4 = /a{4,7}/;
let pattern_str4 = "bcaaaadef";
console.log(pattern_str4.match(test4));

この場合は、出力が「Array [ “aaaa” ]」になりました。
aが4回繰り返しているので、「4回から7回まで繰り返している」という条件にマッチする為です。

さらに次のコードを書いてみます。

let test5 = /a{4,7}/;
let pattern_str5 = "bcaaaaaadef";
console.log(pattern_str5.match(test5));

出力は「Array [ “aaaaaa” ]」になります。
aが5回記述されている場合としてマッチします。

同様に、aが6回記述されている場合、7回記述されている場合もマッチします。

では、aが8回記述されている場合はどうなるのか。
次のように書いてみます。

let test6 = /a{4,7}/;
let pattern_str6 = "bcaaaaaaaadef";
console.log(pattern_str6.match(test6));

出力は「Array [ “aaaaaaa” ]」になります。
対象の文字列にaが8個記述されている場合でも、「4回から7回」記述されているというパターンとしてマッチする。という動作になりました。

実際にコードを書いて動かした結果

正規表現のパターンマッチの条件一つとっても、やって試してみると動きがよくわかります。
上記の「4回から7回までの繰り返しのマッチ」ではaが8個記述されている場合でも、aが7回繰り返されているという条件でマッチすることがわかります。
文章で書くと「7回まで」という表現が紛らわしいですが、対象の文字列中に7回繰り返されているという条件が厳密に実行されることがわかります。

正規表現の文字クラス

Posted コメントするカテゴリー: javascript

概要

文字クラスについてまとめます。
文字クラスはリテラル文字を括弧[]で囲んで記述します。

プログラミング中によく見られる記述は

[a-z]
[1-9]
[\u\l]
[^def]

等で、括弧内の記述方法はそれぞれ正規表現のルールに基づきます。

また、文字クラスとしてよく使われるのものは次のようになります。

[...]     //括弧内の任意の1文字
[^...]    //括弧内の文字以外の任意の1文字
\w        //任意の単語
\W        //任意の単語文字以外の文字
\s        //任意のUnicode空白
\S        //任意のUnicode空白以外
\d        //任意の数字
\D        //任意の数字以外

正規表現の書き方として同じパターンマッチをする場合でも
複数の書き方で実現できます。
正規表現だけとりあげても奥深い世界なので、別途研究するといいかもしれません。

正規表現のリテラル文字列

Posted コメントするカテゴリー: javascript

概要

前回の投稿で正規表現クラスについて書きましたが、
正規表現でパターンマッチに使う正規表現リテラルの文字列を整理してみます。

パターンマッチに使える文字列

パターンマッチに使える文字列はバックスラッシュ「\」で始まるエスケープシーケンスの形式で下記の文字列があります。

//NUL文字
\0

//タブ
\t

//改行
\n

//垂直タブ
\v

//改ページ
\f

//復帰
\r

//ASCII文字(16進数)
\xnn

//Unicode文字
\uxxxx

//制御文字
\cX

また正規表現の中で使われる文字列は次のものになります。

^
$
.
*
+
?
=
!
:
|
\
/
()
[]
{}

例えば^は文字列の先頭を表し、$は末尾を表します。

簡単に次のように書くと「末尾がaで終わるもの」等のパターンマッチができます。

a$

また、次のように書くと「1文字のa」にマッチします。

^a$

バックスラッシュにマッチさせる場合

単純にバックスラッシュそのものにマッチさせる必要がある場合、\を\の前に記述してエスケープさせます。

/\\/

エスケープすることで、パターンマッチに使える文字そのものを正規表現の対象文字として書くことができます。

正規表現

Posted コメントするカテゴリー: javascript

概要

javascriptの正規表現は次のような特徴があります。
・RegExpクラスを用いる
・Stringクラスを用いる
・初期はperlに近い
・パターンマッチング、テキスト検索、テキスト置換等のメソッドが定義されている
・正規表現リテラルは文字列をスラッシュで囲む

RegExpオブジェクト

サンプルコードを書いて試してみます。
まずはRegExpオブジェクトの生成を行い、変数testに代入します。

let test = /^a$/;
console.log(test);

このコードをそのまま実行すると、ブラウザのログ出力には次のように出力されます。

/^a/
flags: ""
global: false
ignoreCase: false
lastIndex: 0
multiline: false
source: "^a"
sticky: false
unicode: false
<prototype>: Object { … }

正規表現のパターン定義については、ここでは詳しく触れませんが、
「^a」は「先頭の文字列がaのもの」にマッチします。

RegExpコンストラクタ

上記と同様の書き方として、RegExp()コンストラクタで書く方法もあります。
次のようになります。

let test2 = new RegExp("^a");
console.log(test2);

ログ出力結果も上記の出力と同じになります。

正規表現のパターンマッチについては一つ一つ確かめていきます。

モジュール

Posted コメントするカテゴリー: javascript

概要

javascriptのモジュールについて簡単にまとめます。

・モジュールはjavascriptのコードを1ファイルにまとめたもの
・クラス定義、関連するクラスが含まれる
・ライブラリが含まれる
・言語仕様としてはモジュールは無い(ES5では)
・CommonJS(団体名)によりサーバサイドjsの標準化がされ、モジュール仕様の概念もある
・上記の仕様にはrequireを利用する
・さまざまなコードを組み合わせて、大きなプログラムを作成できるようになる

不変クラス(不変オブジェクト)

Posted コメントするカテゴリー: javascript

概要

前回の投稿でオブジェクトのプロパティ列挙不可をとりあげましたが、
今回は別な方法でオブジェクトを不変にする方法を試してみます。

具体的にはObject.freeze()というメソッドを使います。
これはオブジェクトを凍結し、プロパティの追加や削除の可否を決めることができます。

サンプル

下記のサンプルコードを書いてみます。
(値を確認する為のログ出力を多くいれています)

let obj = {a: 10};

console.log(obj);

//試験的に他の値を代入する
obj.a = 20;

console.log(obj);

//オブジェクトをfreezeする
Object.freeze(obj);

console.log(obj);

//試験的に他の値を代入する
obj.a = 30;

console.log(obj);

出力結果は下記のようになります。

Object { a: 10 }
Object { a: 20 }
Object { a: 20 }
Object { a: 20 }

このfreezeメソッドはプロパティを凍結する為、書き込み可否はfalseになります。
また、セッターおよびゲッターのアクセサープロパティは使用可能です。

その他、類似するメソッドについて

Object.freeze()と同様の動作するメソッドとして以下のものがあります。

Object.seal()
Object.preventExtensions()

sealはオブジェクトにプロパティの追加や削除ができず、値の変更のみ可能です。
preventExtensionsはプロパティ追加ができません。

また、freeze()メソッドはオブジェクトを「不変」にするので、解凍するという命令はなく、一度メソッドを使うとそのプログラム内では状態が凍結されたままになります。

どうしてもプロパティの内容を変更したい場合は、オブジェクトをコピーし、コピーしたオブジェクトに対してプロパティを付与する方法もありますが、strictモードではオブジェクトをコピーしたタイミングでエラーになります。

プロパティ列挙不可

Posted コメントするカテゴリー: javascript

概要

javascriptでプロパティを定義する際に、列挙不可にする方法です。
次のような書き方はenumerableの属性が全てのオブジェクトに継承されます。

Object.defineProperty(Object.prototype, "TestId", {
    writable : false,
    enumerable: false,
    configurable: false
});

上記のコードは定義のみをしているので、何も出力(動作)はしません。
各パラメータの意味は
writable:値の変更可否
enumerable:列挙可否
configurable:再定義可否

になります。

簡単なサンプルコードを書いて動作テストしてみます。

Object.defineProperty(Object.prototype, "TestId", {
	value : 1,
	writable : true,
	enumerable: false,
	configurable: false
});

//初期状態を呼び出しして内容を確認
console.log(TestId); //出力結果 1

//異なる値を代入してみる
TestId = 2;

console.log(TestId); //出力結果 2

出力結果は

1
2

となります。

次にこのTestIdに対して、以下のコードを書いてみます。

for (let check_value in TestId) {
    console.log(check_value);
}

enumerable属性を無指定であれば、デフォルトはtrueなので、ログ出力の内容が確認できますが、上記の例の場合はfalseなのでログ出力はされずエラーにもなりません。

また、次のように記述しても同様の動きになります。

Object.keys(TestId);

コンストラクタチェーン

Posted コメントするカテゴリー: javascript

概要

サブクラスを定義する際に、継承元のクラスを完全に置き換えず、メソッドの変更をすることができます。

この場合は、サブクラスのコンストラクタやメソッドから、継承元のコンストラクタやメソッドを呼び出す方法があります。

次のようなサンプルコードを書いてみます。
(途中でconsoleログ出力して内容を出力しています)


//テスト用クラス
function Mainclass() {
	Set.apply(this, arguments);
}

console.log(Mainclass.prototype);

//Mainclassのプロトタイプにスーパークラスのプロトタイプを定義
Mainclass.prototype = Object.create(Set.prototype);

//Mainclassを、そのMainclassのプロトタイプのコンストラクタに定義
Mainclass.prototype.constructor = Mainclass;

Mainclass.prototype.add = function() {
	
	//引数argumentsを精査し、問題がなければスーパークラスのaddのみを追加
	for (let i = 0; i < arguments.length; i++) {
		if (arguments[i] == null) {
			throw new Error("err");
		}
	}
	
	return Set.prototype.add.apply(this, arguments);
};

console.log(Mainclass.prototype);

出力結果は次のようになります(firefoxで確認した場合)

Object { … }
Object { constructor: Mainclass(), add: add() }

最初の出力では空のオブジェクトなのに対し、
次の出力ではconstructorの定義と、addメソッドが定義されていることがわかります。

具体的にどのような動きになっているかをみてみると、
テスト用クラスMainclassに対し、Setのサブクラスを定義しています。

例として「Mainclassに対しnullのメンバーは定義できない」という制約がある場合、
addメソッドを定義する場合はnullをチェックする必要がでてきます。

そのような場合に
Mainclass.prototype.add = function() {
の行にあるメソッドで定義する引数argumentsをチェックし、チェック結果に
問題がない場合、スーパークラスのメソッドにチェーンします。

サブクラス定義

Posted コメントするカテゴリー: javascript

概要

javascriptでサブクラスの定義をします。

javascriptのオブジェクトはクラスのプロトタイプからプロパティを継承します。
継承した状態のサンプルを書いてみます。

//テスト用クラス
function Mainclass() {}

var SubClass = (function () {
	SubClass.prototype = new Mainclass();
	SubClass.prototype.constructor = SubClass;
});

//オブジェクトを生成してみる
let MainObj = new Mainclass();
let SubObj = new SubClass();

//生成したオブジェクトを調べる
console.log(MainObj);
console.log(SubObj);

//MainObjにSubObjのコンストラクタのprototypeが存在するか
console.log(MainObj instanceof Mainclass);
console.log(MainObj instanceof SubClass);
console.log(SubObj instanceof Mainclass);
console.log(SubObj instanceof SubClass);

出力結果は次のようになります。

Object {  }
Object {  }
true
false
false
false

後半の2行の出力結果は、サブクラスのプロトタイプオブジェクトをメインクラスのプロトタイプオブジェクトを継承していない為です。

サブクラス

Posted コメントするカテゴリー: javascript

概要

javascriptを他のオブジェクト指向言語と同様に、クラスを発展させた形で書くことができます。

具体的には次のような書き方ができます。
・サブクラス
・サブクラス使用時のメソッドのオーバーライド
・クラスの合成
・メソッドチェーン
・コンストラクタチェーン
・抽象クラス
・具象サブクラス

これらはjava言語などで頻繁に出てきます。
ひとつひとつの書き方をできるだけサンプルコードを書きつつ確かめてみます。

クラスをコンストラクタ名で調べる

Posted コメントするカテゴリー: javascript

概要

オブジェクトのクラスをコンストラクタ名で判別する方法です。

複数の実行環境の場合、同一名称のオブジェクトが存在しても、オブジェクト(のコンストラクタ関数)は同一とは判別されない、コンストラクタ関数の名前を使って判別します。

書き方は前回投稿した記事にも出てきますが、オブジェクト.constructorの内容を利用します。

具体的には次のように書いてみます。

//テスト用クラス1
function Testclass(a)
{
	console.log(a.constructor.name);
}
//オブジェクト1を生成
let testobj = new Testclass("10");

//テスト用クラス2
function Testclass2(a)
{
	console.log(a.constructor.name);
}
//オブジェクト2を生成
let testobj2 = new Testclass2(20);

出力結果は

String
Number

となります。

また、上記の場合は実行環境が同一なので、良い例とはいえないかもしれませんが、
constructor.nameは必ずしも名称を返せる保障はありません。

クラスが無名関数を使って定義されている場合等はコンストラクタ名が取得できないので、判別する前提条件として注意する必要があります。

クラスをconstructorで調べる

Posted コメントするカテゴリー: javascript

概要

オブジェクトのクラスを判定する方法として、constructorを使って調べてみます。

以下、サンプルコードを書きます。

//テスト用クラス1
function Testclass(a)
{
	console.log(a.constructor);
}
//オブジェクト1を生成
let testobj = new Testclass("10");

//テスト用クラス2
function Testclass2(a)
{
	console.log(a.constructor);
}
//オブジェクト2を生成
let testobj2 = new Testclass2(20);

出力結果は次のようになります。(出力形式は実行環境により異なります、ここではfirefoxを例に実行しています)

テスト用クラス1の出力と

String()
fromCharCode: function fromCharCode()
fromCodePoint: function fromCodePoint()
length: 1
name: "String"
prototype: String { "" }
raw: function raw()
<prototype>: function ()

上記の出力結果は、引数がStringクラスのオブジェクトということを表しています。

テスト用クラス2の出力

Number()
EPSILON: 2.220446049250313e-16
MAX_SAFE_INTEGER: 9007199254740991
MAX_VALUE: 1.7976931348623157e+308
MIN_SAFE_INTEGER: -9007199254740991
MIN_VALUE: 5e-324
NEGATIVE_INFINITY: -Infinity
NaN: NaN
POSITIVE_INFINITY: Infinity
isFinite: function isFinite()
isInteger: function isInteger()
isNaN: function isNaN()
isSafeInteger: function isSafeInteger()
length: 1
name: "Number"
parseFloat: function parseFloat()
parseInt: function parseInt()
prototype: Number { 0 }
<prototype>: function ()

上記の出力結果は、日k氏ううがNumberクラスのオブジェクトを表しています。

上記の動作から、オブジェクトを生成する時の、引数に指定した値に対し、constructorプロパティを参照できることがわかります。

また、constructorプロパティは、オブジェクト生成時の引数に対してコンストラクタ関数を参照することができます。

クラスをinstanceofで調べる

Posted コメントするカテゴリー: javascript

概要

javascriptでオブジェクトのクラスを、instanceof演算子で調べる方法を試してみます。

具体的には、次のように書きます。
オブジェクトA instanceof クラスB
上記はオブジェクトAがクラスBのプロトタイプチェーンにコンストラクタのprototypeが存在するかを調べてます。

サンプル

次のコードを書いて動かしてみます。

//テスト用クラス
function Testclass()
{

}

//オブジェクトを生成
var testobj = new Testclass();

//instanceof演算子で判定
console.log(testobj instanceof Testclass);

出力結果はtrueになります。

考察

instanceof演算子は、オブジェクト自身のプロトタイプチェーンにコンストラクタのprototypeが含まれているかを判定するので、
「オブジェクトが何を継承しているか」
という判定方法になります。

ということは、生成したオブジェクトに別のオブジェクトのプロトタイプを入れると、みかけ上は同じオブジェクトだとしても、結果は変わってしまいます。

試しに、次のようなコードを書いてみます。

//テスト用クラス
function Testclass()
{

}

//オブジェクトを生成
var testobj = new Testclass();

//instanceof演算子で判定
console.log(testobj instanceof Testclass);

//生成したオブジェクトにStringオブジェクトのプロトタイプを入れる
testobj.__proto__ = String.prototype;

//instanceof演算子で判定
console.log(testobj instanceof Testclass);

先ほど書いたサンプルに、Stringオブジェクトを入れる処理をして、
同様にinstanceof演算子で判定しています。

出力結果はfalseになります。

生成したオブジェクトに対し、オブジェクトのプロトタイプチェーンに含まれるかを判定する

では、生成したオブジェクトに対し、オブジェクトのプロトタイプチェーンに含まれるかを判定するには、
instanceof演算子ではなくisPrototypeOf()メソッドを使って調べます。

構文は次のようになります。

オブジェクトA.isPrototypeOf(検索対象のオブジェクト)

試しに、次のようにサンプルコードを書いてみます。

function Testclass2() {}
function Testclass3() {}

Testclass3.prototype = Object.create(Testclass2.prototype);

let testobj2 = new Testclass3();

console.log(Testclass3.prototype.isPrototypeOf(testobj2));

上記の出力結果はtrueになります。

生成したオブジェクトtestobj2は、元々Testclass3のプロトタイプチェーンに、Testclass2のプロトタイプを代入している為、少しわかりづらいですが、、

Testclass3のプロトタイプオブジェクトに対して、testobj2をisPrototypeOfで判定するとtrueと判定されます。

では、少し雑ですが、上記のコードを次のように書き換えてみます。

function Testclass4() {}
function Testclass5() {}

Testclass5.prototype = Object.create(Testclass4.prototype);

let testobj4 = new Testclass4();

console.log(Testclass5.prototype.isPrototypeOf(testobj4));

出力結果はfalseになります。

ほとんどコードは同じに見えますが、違いは「let testobj4 = new Testclass4();」の箇所で、
testobj4のオブジェクトを生成する際、Testclass5から生成するのではなく、Testclass4から生成しています。

そうすると、「Testclass5.prototype.isPrototypeOf(testobj4)」の判定は、testobj4のオブジェクトには「Testclass5」のプロトタイプチェーンが含まれていない為、判定はfalseになります。

型について

Posted コメントするカテゴリー: javascript

概要

javascriptのクラスの型について。
javascriptのクラスの型を判定する場合は、typeof演算子を使います。
クラスの属性を調べる際にはclassof演算子を使います。
ただ、独自クラスの場合は正確に識別できないので、別な識別方法でクラスを調べます。

クラスの型を調べる方法

以下の方法で調べます。
1.instanceof
2.constructor
3.コンストラクタ

次回の投稿で一つ一つの判定を実際のコードを書いて確かめてみます。

コンストラクタとインスタンス

Posted コメントするカテゴリー: javascript

概要

前回の投稿でクラスの定義を行いました。
他の言語と同様、クラスを定義すると同時にコンストラクタも記述します。

コンストラクタはクラスを初期化する関数で、newを使ってクラスを生成する際にコンストラクが呼び出されます。
また、コンストラクタのprototypeプロパティが新しいオブジェクトのプロトタイプとしてして使用されます。

サンプルを書いてみます

具体的には次のようなコードを書きます(一例として)。
前回の投稿サンプルを使用しています。

class testclass
{
    constructor(val1, val2) {
        this.val1 = val1;
        this.val2 = val2;
    }
}

上記のクラス「testclass」を呼び出す場合は、次のように書きます。

let tc = new testclass();

インスタンス「tc」生成されるタイミングでコンストラクタが実行されます。

javascriptでのクラス定義

Posted コメントするカテゴリー: javascript

javascriptでのクラス定義は、次のようにします。

・コンストラク関数を定義し、オブジェクトにインスタンスプロパティを設定
・コンストラクタのprototypeオブジェクトにインスタンスメソッドを定義
・コンストラクタに、クラスやメソッドを定義

また、ES6からはクラス定義する際に、class構文を使って書くようになりました。
class構文を使った場合は、一般的にクラス宣言という呼び方をします。
具体的には次のように書きます。

class testclass
{
	
}

中身は何も記述していませんので、当然何もしないクラスです。

また、次のようなクラス式を使った書き方もあります。
これはclassの後になにも記述していないので、名前なしでの宣言になります。

let testclass = class 
{
	
}

また、クラスの後に名前を宣言したクラス式もあります。
次のように書きます。

let testclass = class Testclass2
{
	
}

クラス宣言と関数宣言は似ていますが、クラス宣言は巻き上げが起きないことが大きな違いです。