正規表現の文字クラス

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
{
	
}

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

javascriptのクラスごとのメンバーについて

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

概要

javascriptのクラスについて、クラスごとのメンバーを確認します。
javascriptでは関数を値として考えることができ、メソッドとフィールドに区別はないです。

プロパテの値が関数の場合は、メソッドとしての振る舞いになり、
関数ではない場合は、プロパティ(値)となります。

クラスに関係するオブジェクトは次の3種類になります。

コンストラクタオブジェクト
プロトタイプオブジェクト
インスタンスオブジェクト

各オブジェクトと実際のコードの書き方、動作の確認等、次回の投稿で掘り下げてみます。

コンストラクタ

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

概要

javascriptでクラスを定義する際にも、他の言語と同様にコンストラクタを定義する書き方があります。

コンストラクタはクラスをインスタンス化するタイミングで実行される(初期化のタイミングで実行される)関数と考えることができます。

コンストラクタはnewキーワードを使ってインスタンスを生成するタイミングで自動的に呼び出されます。

javascript特有の動きとしては、コンストラクタの呼び出し時にprototypeプロパティが新しいオブジェクトのプロトタイプとして使われます。

次にコンストラクタを使ったコードを書いてみます。

//クラスとなるメソッドを定義
function Testclass()
{
	//生成したオブジェクトに対して、プロパティを定義
	this.test1 = 2;
	this.test2 = 60;
}

//上記のクラスに新たにメソッドを定義(付与)する
Testclass.prototype = {
	
	//1つめのメソッドを追加
	addfunc1: function(a)
	{
		//引数aに元のクラスのプロパティtest1の値を加算して返す
		return a + this.test1;
	},
	
	//2つめのメソッドを追加
	addfunc2: function(b)
	{
		//引数aに元のクラスのプロパティtest2の値を加算して返す
		return b + this.test2;
	}
};

//Testclassメソッドをもとにインスタンス「ti」を生成する
let tic = new Testclass();

//インスタンス
console.log(tic.addfunc1(2));
console.log(tic.addfunc2(3));

上記の出力結果は

4
63

という値になります。

前回に投稿したブログ記事と構造は似ていますが、クラス「Testclass」の定義の方法と
呼び出し方(インスタンスの生成方法)が異なっています。

newキーワードを使ってTestclassを呼び出しているので、下記のように呼び出すタイミングで

let tic = new Testclass();

コンストラクタの実行としてfunction Testclass() 内の命令が実行され、this.test1 と this.test2 にそれぞれ値が入っているのことが把握できます。

また、クラス名(コンストラクタ)の先頭は大文字にしています。
小文字でも動作は同様になるのですが、一般的な慣習としてクラスの先頭は大文字にして書くと良いかと思います。

次に「Testclass.prototype」の定義の仕方ですが、これはコンストラクタ呼び出しの際に、オブジェクトのプロトタイプとして自動的に.prototypeが使われる為、このような書き方になります。

クラスとプロトタイプ

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

概要

javascriptのクラスはプロトタイプオブジェクトからプロパティを継承するオブジェクトと言えます。

プロトタイプオブジェクトはクラスの原型という位置づけで考えられ、
このプロトタイプオブジェクトを生成することはクラスを定義することと同じような意味になります。

一般的には新しいオブジェクトを生成する場合、インスタンスを初期化します。

次にシンプルなクラスを定義して、実際にコードを書いてみます。

//クラスとなるメソッドを定義
function testclass()
{
	//プロトタイプオブジェクト(addmethod)を継承するオブジェクトを生成する
	let tc = testclass.addmethod;
	
	//生成したオブジェクトに対して、プロパティを定義
	tc.test1 = 1;
	tc.test2 = 50;
	
	//オブジェクトを返す
	return tc;
}

//上記のメソッドに新たにメソッドを定義(付与)する
testclass.addmethod = {
	
	//1つめのメソッドを追加
	addfunc1: function(a)
	{
		//引数aに元のクラスのプロパティtest1の値を加算して返す
		return a + this.test1;
	},
	
	//2つめのメソッドを追加
	addfunc2: function(b)
	{
		//引数aに元のクラスのプロパティtest2の値を加算して返す
		return b + this.test2;
	}
};

//testclassメソッドをもとにインスタンス「ti」を生成する
let ti = testclass();

//インスタンス
console.log(ti.addfunc1(2));

console.log(ti.addfunc2(3));

上記の出力結果は

3
53

になります。

上記のコードの動きについてですが、まずクラス「testclass」を定義し、その定義したクラスに対してプロトタイプオブジェクトを継承し、
継承したプロトタイプオブジェクト内のメソッドaddfunc1やaddfunc2を呼び出しています。

このように書いて検証してみることで、プロトタイプオブジェクトの動きが見てわかるようになります。

クラス

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

概要

javascriptのクラスについて、特徴を簡単にまとめてみます。

クラスの考え方として一般的にプロパティとメソッドを持ちます。
プロパティはクラス内で保持する変数(のようなもの)の概念があり、メソッドはクラスの振る舞いを定義する関数のようなものです。

javascriptでクラスを定義した場合、上記のプロパティとメソッドを用い、処理を行うことができます。

プロトタイプ

javascriptはプロトタイプという概念があるので、クラスを定義する際にもその考え方が適用されます。
2つのオブジェクトが同じプロトタイプからプロパティを継承している場合、この2つのオブジェクトは同じクラスのインスタンスとして振舞います。

コンストラクタ

オブジェクト指向の言語で必ず出てくるキーワードとしてコンストラクタがあります。
コンストラクタはクラスを基にオブジェクトが生成されたタイミングで実行されるメソッドと考えられます。

次回からの投稿でクラスを詳しく試して把握していこうと思います。

メモ化

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

概要

javascriptの関数の書き方としてメモ化(Memoization)というものがあります。
これはjavascriptのみならず、他の言語でも同じ概念で書くことができます。

ざっと調べると「関数のサブルーチンの呼び出し結果を後で再利用する為に保持し、そのサブルーチンの呼び出し毎の再計算を防ぐ手法」ということが言えます。

この保持する部分を「オブジェクトに記憶」や「キャッシュしておく」という言い方に変えればイメージがつきやすくなるかもしれません。

サンプル

簡単な例を書いてみました。

//関数定義
let testfunc = function(n) {

	let memo;

	//キャッシュのキーに値があるかどうか
	if (testfunc.cache[n]) {
		//キャッシュ内にある場合は、既にメモ化された結果を返す
		return testfunc.cache[n];
	} else {
		//引数を基にキャッシュから読み込み
		memo = {};
		//仮の演算を行う(時間がかかる処理にする)
		for (i = 0; i < 100000; i++) {
			memo[i] = i * n * 2;
		}
		//演算結果をメモ化したキャッシュに格納
		testfunc.cache[n] = memo;
		return testfunc.cache[n];
	}
};

//メモ化した関数のキャッシュ領域を用意
testfunc.cache = {};

上記の関数定義と、その後キャッシュ領域の用意(初期化)を行うことにより、
初回の関数呼び出しのみ、時間のかかる処理が呼び出され、その後の呼び出し時には、一度演算したことがある引数についてはキャッシュから値を読み込んで値を返す動作になります。

高階関数

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

概要

高階関数についてまとめます。
高階関数を簡単に言うと、「引数で関数を取り扱う処理方法」ということができます。

関数を引数にとり、関数内で引数の関数に対して処理を行い、演算結果を返す関数です。
引数に1つ以上の関数を受け取り、新たな関数を返します。

簡単なサンプル

文章で書いても理解しにくいので、簡単なサンプルを書いて実行してみます。

//高階関数の定義
function test(fs, data) {
    fs(data + 1);
}

//ユーザ定義の関数
function test2(val_a) {
    console.log(val_a);
}

//テスト用の変数に数値を定義
let t_val = 10;

//高階関数を呼び出す
//このとき、引数の1つめはユーザ定義関数、2つめはテスト用の変数
test(test2, t_val);

実行結果は「11」になります。

上記の例では単純な関数を使っていますが、書き方次第では、
ユーザ定義関数をより複雑な処理にすることもできます。

関数型プログラミングについて

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

概要

javascriptは関数をオブジェクトのように扱えるので、関数型プログラミングの書き方ができます。

サンプル(reduce)

関数型プログラムの例として、配列をreduce()メソッドを用いて処理してみます。

まず、reduce()のサンプル

let arr = [1, 2, 3, 5];

let calc_arr = arr.reduce(function (x, y) {
	return x + y;
});

console.log(calc_arr);

出力結果は「11」になります。

ものすごい単純な例ですが、reduceメソッドは関数型プログラミングの動作をしていると言えます。

reduceは、元となる配列arrの1つめの要素を引数と渡して処理した後で、演算後にreturnされた値を、2つめ以降の関数の引数として用いることができます。(配列の要素が無くなるまで演算されます)

こうすることで、上記の例では配列の各要素が順に加算された演算結果「11」となります。

サンプル(map)

次に関数型プログラムの例として、配列をmap()メソッドを用いて処理してみます。

上記のreduce()のサンプルを少し変えて、次のように書いてみました。


//mapサンプル
let arr2 = [1, 2, 3, 5];

let calc_arr2 = arr.map(function (x) {
	return x + 10;
});

console.log(calc_arr2);

出力結果は

Array(4) [ 11, 12, 13, 15 ]

という結果になります。

map()は、配列の各要素に対し、map()内部で宣言された無名関数の演算結果を、関数の戻り先の配列に対して、各要素の値を演算した結果を返して生成しています。
ただ、map()引数の指定の仕方によっては、元の配列要素の値を変更することが可能です。

それぞれの違い

map()は、reduce()と違い、要素数が複数になります。

map()は配列の全要素に対して演算を行い、演算結果も配列の要素数が生成されることに対して、
reduce()は配列の各要素に対して、演算した結果をさらに無名関数内部で演算をするため、処理結果が単一となります。

for等のループ文を使うより、シンプルな書き方ができるので、実用面で有効な書き方と言えそうです。

クロージャ(その2)

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

クロージャ(その2)

クロージャについて、別なサンプルを書いて動作させてみます。

前回のクロージャは、関数内にローカル変数を宣言し、そのローカル変数をクロージャを動作させるタイミングでアクセスしていました。
次に、ローカル変数を宣言せず、関数の引数として指定するサンプルを動かしてみます。

function test3(n)
{
	return {
		//ゲッター
		get f1() {
			return n++;
		},
		//セッター
		set f1(m) {
			n = m;
		}
	};
}

//関数test3を変数へ代入し、オブジェクトを生成する
var obj3 = test3(50); //このタイミングでゲッターメソッドを実行する

console.log(obj3.f1);
console.log(obj3.f1);
console.log(obj3.f1);

obj3.f1 = 100; //セッターメソッドを実行する

console.log(obj3.f1);
console.log(obj3.f1);
console.log(obj3.f1);

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

50
51
52
100
101
102

上記のように、関数内にローカル変数を持たない場合でも、
引数を渡してその値をコントロール(セッター&ゲッター)することにより、
内部の値を保持するような形で処理することができています。

クロージャ

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

概要

他の言語同様、javascriptにもクロージャの概念があります。

コードを書きながら理解してみようと思います。

基本的な考え方

クロージャを理解するには、関数とスコープについて整理が必要です。
まず、関数が定義された時、変数の定義がどこでされているかに注意します。

通常、関数を定義すると、スコープチェーンを持ちます。

関数の中に(入れ子の)関数がある状態にある場合、定義された(入れ子の)関数から、親側の関数内で定義された変数を参照できる。という考え方があります。

文章にするとわかりにくいので、まず通常の入れ子の関数を書いてみます。

let test_val = "Global_A";

function test1()
{
	let test_val = "Local_A";
	
	function test2() {
		return test_val;
	}
	
	return test2();
}
console.log(test1()); //出力:Local_A

「console.log(test1());」の行で出力された文字列は「Local_A」になります。

次に、ネストされた関数オブジェクトを返す関数にしてみます。

let test_val = "Global_A";

function test1()
{
	let test_val = "Local_A";
	
	function test2() {
		return test_val;
	}
	
	return test2; //このタイミングで関数オブジェクトを返す
}
console.log(test1()()); //出力:Loacl_A

一番外側で「test1()()」という形で(親となる)関数内にあるネストされた関数をオブジェクトを使って呼び出しています。
最初の例と合わせて、どちらも「Local_A」が出力されます。

簡単なクロージャの書き方(例)

ここで、非常に簡単なクロージャの例を書いて動かしてみます。

var test1 = (function() {
	var counter = 0;
	
	return function() {
		return counter++;
	};
}());

console.log(test1());
console.log(test1());
console.log(test1());
console.log(test1());

上記のコードを動作させると、出力結果は

0
1
2
3

となります。

test1()を呼び出す度に、関数内部で宣言されているローカル変数counterがカウントアップされた結果が返ってきます。
test1に返ってくる値は全体が括弧()で囲まれているので、内部の無名関数の戻り値が代入されます。

上記の例では、無名関数内に一つの入れ子型の関数がある形になりますが、
無名関数が2つある場合は次のような書き方ができます。

function test2()
{
	var n = 1;
	
	return {
		doubleup: function() {
			n = n * 2;
			return n;
		},
		reset: function() {
			n = 0;
		}
	};
}

//関数test2を変数へ代入し、オブジェクトを2つ生成する
var cnt1 = test2();
var cnt2 = test2();

//出力確認(1回目)
console.log("----");

console.log(cnt1.doubleup());
console.log(cnt1.doubleup());
console.log(cnt1.doubleup());
console.log(cnt1.doubleup());

console.log(cnt2.doubleup());
console.log(cnt2.doubleup());
console.log(cnt2.doubleup());
console.log(cnt2.doubleup());

console.log("----");

//cnt1の内部カウンタをリセットする
cnt1.reset();

//出力確認(2回目)
console.log(cnt1.doubleup());
console.log(cnt1.doubleup());
console.log(cnt1.doubleup());
console.log(cnt1.doubleup());

console.log(cnt2.doubleup());
console.log(cnt2.doubleup());
console.log(cnt2.doubleup());
console.log(cnt2.doubleup());

上記のコードの出力結果は次のようになります。

----
2
4
8
16
2
4
8
16
----
0
0
0
0
32
34
128
256

最初の8行では、生成したオブジェクト「cnt1」と「cnt2」に対して、カウントアップ(2ずつ加算)する処理がされて出力されます。
その後、cnt1に対してreset()メソッドを呼んでいるので、
後半の8行では、cnt1についてはまた最初の値からカウントアップされています。

生成したオブジェクトが別な場合は、内部の変数が同じでも、共に影響しない形でアクセスできていることがわかります。

クロージャのシンプルな例として、上記のような関数内への変数の取り扱い方があるので、基本として抑えておくのが良いかと思います。

クロージャにはまだ別な書き方があるので、次回のブログとして取り上げようと思います。

匿名関数の名前空間での利用

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

概要

javascritpでのプログラミングには、常にグローバル変数や関数定義、ローカル変数などの定義場所、等で、変数名、関数名が他のライブラリやモジュールと重複するリスクがあります。

このようなリスクを回避する方法として、匿名関数を利用する方法があります。

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

(function() {
    //特定の処理(変数の定義と利用)
}()};

上記のコードにより、「特定の処理」の箇所に書いたコードは
他のライブラリやモジュール等に影響を受けずに処理を書くことができます。

関数プロパティ

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

概要

javascriptの関数の特徴として、関数プロパティと呼ばれるものがあります。

この関数プロパティについて、試してみます。

関数プロパティについて

関数プロパティは、関数に対してプロパティを持つことをいい、Functionオブジェクトに対してプロパティを付与することで実現します。

こうすることで関数の実行後にも値が保持できる静的変数を持つことができます。
また、この静的変数については関数を呼び出し時にも使うことができます。

具体的にコードを書いて試します

次のコードを書きます。


function test1()
{
	//関数プロパティの値に対して作用するコードを書く
	//(例:文字列の連結)
	return test1.teststring + "def";
}

//通常の関数呼び出し
console.log(test1());

//関数プロパティとして「teststring」を定義し、文字列を代入する
test1.teststring = "abc";

//関数プロパティに文字列を代入した後の、出力
console.log(test1());

少し荒い書き方ですが、関数test1に対して、通常の呼び出しと、関数プロパティの定義と、それに対しての処理(文字列連結)をした後の、関数呼び出しを書いてみました。

実行結果は次のようになります。

undefineddef
abcdef

最初の1行目の「undefineddef」は、通常の関数呼び出しを行うタイミングでは、関数プロパティの定義すらしていないので、ごく当たり前の出力結果といえます。

未定義のプロパティ「undefined」に対して、文字列「def」が連結されています。

次に出力された「abcdef」については、関数プロパティに定義された値が呼び出されているので、単純に文字列が連結された結果が出力されています。

グローバル変数との違い

この関数プロパティの動きを見てみると、グローバル変数ができることと同じ動きになります。
実際には、グローバル変数の使いどころを見極める必要があり、グローバル変数名の重複や、ロジックが複雑になる場合があるので、シンプルなコードを書く場面で関数プロパティを使うと良いと考えることができます。

関数を変数や、オブジェクトのプロパティに格納して利用する

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

概要

javascriptの言語の特徴の一つとして、関数を変数やオブジェクトのプロパティへ代入して値として使用する方法があります。

詳しい言語仕様については仕様書を確認するとして、ここでは実際にコードを書いて動作を見てみます。

まず、次のコードです。

//関数を定義
function test1(val1)
{
	return val1 * 2;
}

//通常の関数呼び出し
console.log(test1(10)); //20

出力結果

20

出力結果は「20」になりました。

これはオーソドックスな関数の定義と呼び出し方法です。

では次に、上記のtest1という関数を、オブジェクトのプロパティとして利用してみます。
具体的には次のようなコードを書きます。

//関数を定義
function test1(val1)
{
	return val1 * 2;
}

//関数を一度、変数に代入してみる
let tmp_func1 = test1;
console.log(tmp_func1(30)); //60
console.log(tmp_func1(40)); //80

出力結果

60
80

この場合は、関数を一度変数に代入し、その変数に対して引数を指定し、関数を実行しています。
見た感じでは、通常の関数を実行しているように見えます。

では、次に関数をオブジェクトのプロパティとして指定する方法を試してみます。

具体的には、次のようなコードを書きます。

//関数をオブジェクトのプロパティとして代入する
let test_obj = {
	test2: function(val2) { return val2 * 3; }
};
console.log(test_obj.test2(5)); //15

出力結果

15

出力結果は「15」になりました。

この場合は、test_obj内に定義されたメソッドtest2に対して、関数を割り当てています。

次に、変数の引数の値として、関数を指定する方法についてです。

//配列の要素に、関数を格納する
let test_arr = [function(val3) { return val3 * 8; }];

//40
console.log(test_arr[0](5));

//引数の2つ目は何もないので「TypeError: test_arr[1] is not a function」
console.log(test_arr[1](5));

出力結果

40
TypeError: test_arr[1] is not a function

二つ目の出力結果は、「TypeError: test_arr[1] is not a function」となります。
これは配列の二つ目の要素には何も指定していない為です。
関数を定義しているのは配列の1つ目であることに注意します。

javascriptの関数の引数の型の扱いについて

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

概要

javascriptの関数は、関数を呼び出す際の引数について、型を宣言することはできない。
また、関数宣言側の引数も型のチェックが行われず処理されます。

関数の呼び出し時、関数の受け取り時に引数の型は厳密ではないですが、
言語の特性として型は動的に変化します。

とはいえ、プログラミングを進めるうえで、引数の型についてまったく考慮しなくても良いということはありません。
できれば型を厳密にチェックして、関数内の動作を保障するようにしないと、プログラムの規模が大きくなるほど大変になってきます。

関数の引数の型をチェックする

では、具体的にどのように関数の型をチェックすればよいでしょうか。
試しに簡単なコードを書いて調べてみます。
次の例は引数の値が配列かどうかをチェックして、処理を分岐しています。

function test1(str)
{
	//ここで「str」の引数のチェックを行う
	//原則、文字列であればOKで、数値や配列等はNGとする

	//配列かどうかのチェック
	if (str instanceof Array) {
		//配列の場合はここで処理
	}
}

test1("abc");

上記の場合は、呼び出し元の関数の引数は文字列を渡しているので、呼び出し先の関数内では、配列かどうかの判定をしている為、分岐の中にははいりません。

配列のチェックは「isArray()」メソッドを使ってもOKですが、
空白の配列の判定が甘い為、上記のようにinstanceofを使っています。

また、上記は配列かどうかを判定していますが、その他、nullやNaNかどうかのチェック等のメソッドも使って、引数を判定することが有効です。

関数呼び出し時の引数にオブジェクトのプロパティを使う方法

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

概要

これまでの関数は、呼び出し元の引数と、呼び出される関数側の引数についてみてきました。

注意深くプログラミングすれば特に問題なく動作しますが、
引数の順序や引数の数には気を配る必要は常にあります。

そこで、少し工夫をして、関数側の引数としてオブジェクトのプロパティを使う方法があります。
(呼び出す側の引数は、必然的にオブジェクト定義された値になります)

オブジェクトのプロパティを渡すことで、引数は一つになり、
「プロパティ名.値」という形で呼び出して利用することが可能です。

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

function test1(args) {

	//引数のオブジェクト内の1つ目の配列を呼び出して出力
	console.log(args.testobj1[0]);
	console.log(args.testobj1[1]);
	console.log(args.testobj1[2]);

	//引数のオブジェクト内の2つ目の配列を呼び出して出力
	console.log(args.testobj2[0]);
	console.log(args.testobj2[1]);
	console.log(args.testobj2[2]);
	console.log(args.testobj2[3]);
	console.log(args.testobj2[4]);
}

//テスト用の配列の定義
let args1 = [10, 20, 30];
let args2 = [60, 70, 80, 90];

//オブジェクトの定義
let obj_args = {
	testobj1: args1,
	testobj2: args2
};

//ここで関数を呼び出す
//引数はオブジェクトをそのまま渡す
test1(obj_args);

出力結果

10
20
30
60
70
80
90
undefined

出力結果の一番最後は、わざと2つ目のテスト用配列の存在しない要素にアクセスしています。

この場合はエラーにはならずに、引数に渡したオブジェクトに格納されている2つめの配列の要素にアクセスしようとしますが、undefinedとして出力されます。

可変長の引数リスト

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

概要

前回のブログ記事は関数の引数を省略した場合の挙動を試しましたが、
今回の記事は、関数の呼び出し元が引数の数を多く書いた場合の挙動を試してみます。

まずは実際のコードを書いて動きを試してみます。

function test3(a, b) {

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

	if (a == undefined) {
		console.log("a1");
	} else {
		console.log("a2");
	}
	
	if (b == undefined) {
		console.log("b1");
	} else {
		console.log("b2");
	}
	
}

test3(10, 20, 30);

出力結果

10
20
a2
b2

上記のような出力結果になりました。

関数test3の第三引数の「30」は引数として解釈不可能なので、
エラーにもならず、関数内では何もなかったかのような挙動になります。

関数の挙動について

それでは、上記のような関数内で、第三引数が存在する呼び出し方をした場合に処理するにはどうすればよいかというと、
javascriptのArgumentsオブジェクトを利用する方法があります。

Argmentsオブジェクトは配列のように振る舞い、引数の値はarguments[]配列に格納され、それを関数内で使うことができます。

具体的には次のようなコードを書いて試してみます。

function test4(a, b) {

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

	console.log(arguments[0]);
	console.log(arguments[1]);
	console.log(arguments[2]);
}

test4(10, 20, 30);

出力結果

10
20
10
20
30

出力結果を見ればわかりますが、test4の関数の引数は2つしか定義していませんが、arguments[2]という書き方をすることにより、Argmentsオブジェクトを使い、引数の3つめの値が取得できています。

現場のプログラミングでは、関数の引数の数は、設計や仕様に基づいて正確に書くことが大半ですが、
引数の数を限定しない仕様で関数を定義すると便利な場合があります。

例えばmax関数で引数があればあっただけ、その中から最大値を求める場合の関数はその例です。

また、任意の引数を受け取ることができる関数を、可変長引数関数と呼びます。
別名としてvarargs関数と呼んだりもします。

calleeプロパティ、callerプロパティ

Argmentsオブジェクトについては、配列のように使う方法に限らず、その他の動きをするプロパティもあります。

use strictの状態ではエラーになりますが、非strictモードの場合はcalleeプロパティ、callerプロパティというプロパティが使用できます。

calleeプロパティは関数を呼び出した関数を参照できます。

callerプロパティは匿名関数にて、自分自身を再帰的に呼び出せます。