コンストラクタチェーン

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プロパティは匿名関数にて、自分自身を再帰的に呼び出せます。

引数と仮引数

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

概要

これまでの関数のサンプルコードでは、関数定義の際に、関数の引数の型は指定していませんでした。
また、関数の呼び出しの再にも型については、厳密にチェックをして呼び出していません。

javascriptは型の扱いが動的で、他の言語に比べると型の扱いに注意してプログラムする必要があります。

また、引数の型に対しての注意と、引数の数についても、javascriptは厳密にチェックしていません。

これから実際のコードを書いて、引数の細かい使い方についてまとめていこうと思います。

省略可能な引数

javascriptの関数の引数を省略した場合の挙動をみてみます。

下記のようなコードを書いて実行してみます。

function test2(a, b) {
	console.log(a);
	
	if (a == undefined) {
		console.log("a1");
	} else {
		console.log("a2");
	}
	
	if (b == undefined) {
		console.log("b1");
	} else {
		console.log("b2");
	}
	
	return true;
}

test2();
test2(10);
test2(10, 20);

出力結果

関数test2を引数の省略、明示した場合に分けて、3回呼んでいます。
出力結果はわかりやすいように、関数ごとに空行を入れています。

undefined
a1
b1

10
a2
b1

10
a2
b2

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

最初のtest2()の関数では、引数のaもbもundefinedとして解釈されます。
javascriptの関数は、引数を省略してもエラーにはならずに実行されます。

2つめと3つめの関数はそれぞれ、引数を記述していますが、第一引数や第二引数をそれぞれ省略しても、省略した引数はundefinedとして解釈されます。

関数の呼び出し方法について

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

概要

javascriptの関数の呼び出し方法についてまとめます。

関数は定義しただけでは動作しません。
関数の呼び出し方法は、次のように呼び出されます。

・1.関数定義を行い、関数を呼び出す
・2.メソッドとして呼び出す
・3.コンストラクタとして呼び出す
・4.call()、apply()メソッドを使って間接的に呼び出す

以下、関数の呼び出し方法を一つ一つみていきます。

1.関数定義を行い、関数を呼び出す

最も一般的というか、プログラムを実装していて多く見かける書き方です。

前回の投稿でも書いたスタンダードな関数を定義し、それを呼び出す方法です。
具体的には下記のような形になります。

//関数の定義(戻り値はなし)
function test1 () {
    //処理
}

//呼び出し
test1();

上記の場合、関数test1には引数がないので、呼び出す時にも引数の記述はありません。
引数は状況に応じて書きます。

上記の書き方とは別に、「関数式」で関数を定義し、呼び出す方法は次のようになります。

//関数の定義(戻り値はなし)
let test1f = function () {
    //処理
};

//呼び出し
test1f();

上記の関数の書き方は関数名がない無名関数になります。

2.メソッドとして呼び出す

上記の方法とは別に2つめの呼び出し方について調べてみます。

メソッドはオブジェクトのプロパティに保存されている関数を指します。

例えば、関数test2をオブジェクトobj1のメソッドtestmとして定義する場合、次のように書きます。

obj1.testm = test2;

呼び出す時はobj1.testm();で呼び出せます。

また、呼び出し方法は上記の他、

obj1["testm"]();

という書き方をしても同様の意味になります。

3.コンストラクタとして呼び出す

関数呼び出しの前にnewキーワードをつけると、コンストラクタが呼び出されます。

コンストラクタとして呼び出す場合、具体的には次のように書きます。

let obj1 = new Object();

newキーワードを使って、オブジェクトを生成するタイミングで、コンストラクタとして関数が呼び出されます。

コンストラクタは他の言語では、オブジェクトがnewされるタイミングで必ず実行されるメソッド。が存在し、newと同時にそのメソッドが実行される特徴があります。

コンストラクタは他の関数呼び出しやメソッド呼び出しとは違う挙動になります。

コンストラクタ呼び出しの際の、括弧を書いても省略しても、動作には問題はなく、引数を省略すると、引数式が評価されずに関数に渡らない挙動になります。

4.call()、apply()メソッドを使って間接的に呼び出す

関数呼び出しの方法の中で、あまり見かけない(と思う)呼び出し方が「callによる呼び出し」「applyによる呼び出し」になります。

簡単な例をあげると、上記で書いたサンプルのコードがあるとして

//関数の定義(戻り値はなし)
let test1f = function () {
    //処理
};

//呼び出し
test1f();

この関数test1f (無名関数)をcallメソッドを使って呼び出してみます。

//関数の定義(戻り値はなし)
let test1f = function () {
    //処理
};

//呼び出し
test1f.call();

書き方はほとんど変わらないのですが、呼び出す箇所が「.call()」になっている点が異なります。

また、「applyメソッドによる呼び出し」は次のように書きます。

//関数の定義(戻り値はなし)
let test1f = function () {
    //処理
};

//呼び出し
test1f.apply();

見てわかるように、callメソッドの呼び出しと流れは変わりません。

では、この2つの呼び出し方は何が違うのでしょうか。

簡単に言い切ってしまうと、このcallとapplyの両者の違いは、引数にあります。

applyメソッドの引数は、配列で渡す必要があります。
この挙動については、言語仕様に深く関わる点なので、別途取り上げて掘り下げようと思います。

関数の入れ子について

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

概要

関数の入れ子について、まとめてみます。
javascriptで関数の中にさらに関数を入れ子で定義する場合についてです。

この場合、変数へのアクセスの制約に注意する必要があります。

関数内で入れ子にした関数から、大元の関数の仮引数や変数にアクセスが可能です。

簡単な例を下記に書きます。

function test1(a) {
	//処理
	console.log(a);

	//入れ子の関数を定義
	function test2() {
		//処理
		a++;
		//ここで引数aに対してアクセスができる
		console.log(a);
	}

	test2();

	console.log(a);
}

test1("10");

出力結果

10
11
11

わかりやすいように処理を一切書いていませんが、上記のように大元の関数の引数へアクセスすることができます。

出力結果は、10、11、11となり、まず最初の関数test1で呼び出された直後の引数の値が「10」として出力され、
次に関数内で定義されたtest2が呼び出された際の「11」が出力されます。
最後に、test1の関数の終わりに出力した変数aの値「11」が出力されます。

3回目の出力で値が「11」になるのは、関数test1の引数で定義されているaが、関数test2の中からアクセスされてインクリメントされている為です。

実装の方法はさらに複雑になる場合が多く、変数の有効範囲について注意して実装していきます。

関数の定義について

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

関数の定義

javascriptの関数の定義についてまとめます。

関数の定義については、関数定義式、関数宣言文として、「function」キーワードを使って定義します。

関数の定義の特徴は次のようになります。

・関数宣言文では、関数の名前を指定します。この名前は関数名として利用します。

・関数名は変数名として利用することもできます。

・関数の定義する際に、関数名を省略することが可能です。

・関数名を省略した関数は無名関数や、匿名関数とも呼ばれます。

・関数の引数は()の内部に記述します。

・引数は、カンマ区切りで0個以上の識別子を記述します。

・識別子は引数名として利用可能で、関数本体内でローカル変数のように利用できます。

・{}ブロックでかこまれた文は、関数の本体部分になります。

関数の定義の方法はjavascript独特の定義があるので、実際にコードを書いて試していきます。

代表的な関数の定義の仕方を下記のコードにまとめます。

//関数の定義(戻り値はなし)
function test1 () {
	//処理
}

//関数の定義、引数「value_y」を持つ(戻り値はなし)
function test2 (value_y) {
	//処理
}

//関数の定義(戻り値は有り)
function test3 () {
	//処理
	
	//決まった文字列を返す
	return "test3";
}

//関数の定義、再帰関数(関数内の条件判定が真の場合、戻り値が返される)
function test4 (value_x) {
	//処理
	if (value_x < 100) {
		return true;
	}
	
	//決まった文字列を返す
	return test4(value_x + 10);
}

//関数式を変数に代入する場合(無名関数)
let test5 = function(x) { return x * x; }

//関数式に名前をつけて、定義する
let test6 = function test7(x) { return x * x; }

//関数式を他の関数の引数として使用する
//(この場合、test8オブジェクトのtest9メソッドの引数として、無名関数を割り当てています)
test8.test9(function(a, b) { return a - b; } ));

//関数定義と同時に処理を呼び出す場合
let test10 = (function(x) { return x + x; }(100));

上記の例にあるように、式として関数を定義することができます。
その場合、関数名を省略して無名関数という呼び方をします。

また、変数に対して関数を代入する場合は、変数に関数オブジェクトを代入するという考え方ができます。

無名関数はプログラム中で1回だけ実行される関数を定義する場合に有効で、実装方法に応じて通常の関数定義と使い分けます。

関数の名前について

関数名は識別子として有効な名前であれば、文字列に制約はありません。
命名規則はプロジェクトの規約やルールにより変動するものですが、
一般的な命名ルールは「リーダブルコード」等の書籍等を参照にすると良いと思います。

関数の呼び出しについて

関数の呼び出し方法を理解するには、javascript特有の動作があることを把握する必要があります。

関数宣言文はスクリプトや関数の先頭に巻き上げられて処理されます。
関数宣言文(function)で宣言された関数は、関数定義した箇所よりも前に定義することができます。
(他のプログラム言語では、関数の宣言位置によってエラーが起こります)

また、式に代入された関数は、変数に代入された後から利用可能になります。

関数からの返し方について

関数内で処理を行い、その処理結果を関数の呼び出し元に返すには、return文を使います。

return文が定義された行から関数が返されます。

また、return文を省略した場合の関数は、呼び出し元にundefinedが返されます。

関数

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

概要

ここからはjavascriptの関数について、一つ一つ丁寧に掘り下げていこうと思います。

他のプログラム言語でも同様に、javascriptのプログラムにおいて同様の処理がまとまって連続する場合、それをまとめて関数にして利用します。

javascriptの関数を定義するときは、仮引数と呼ばれる識別子のリストを記することが可能です。
仮引数はローカル変数として解釈され、関数を呼び出す時に関数の引数として値を渡します。

また、関数には戻り値があります。
戻り値は関数の呼び出し式の値になります。

関数の呼び出しには、仮引数の他に、呼び出しコンテキストと呼ばれる「this」で書くものがあります。

関数はオブジェクトのプロパティとして代入可能で、代入した関数はそのオブジェクトのメソッドになり、呼び出すことが可能です。
また、そのメソッドを呼び出す場合、オブジェクトがコンテキストになります。
新しく生成されるオブジェクトはコンストラクタによって初期化されます。

javascriptでは、関数を変数に代入することができます。
また、関数を別の関数に渡すこともできます。
さらに関数の中で、別な関数を定義することも可能です。
関数の中で別な関数を定義した場合、定義した関数からスコープ中の変数にアクセスが可能になります。(この考えは後ほどとりあげるクロージャの考え方につながるので、別途、深く掘り下げようと思います)

上記の特長の諸々は、別途コードを書いて動作を確かめてノウハウを蓄積していこうと思います。

文字列に対して配列メソッドを使う場合

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

概要

javascriptの文字列に配列のメソッドを使った場合について、調べてみます。

文字列を定義し、その文字列に対して配列のメソッドを使った時、どのような動きになるでしょうか。
次のコードを書いて動かしてみます。

let test1 = abcdef;
console.log(test1.charAt(0));
console.log(test1.charAt(1));
console.log(test1.charAt(2));

出力結果は「a b c」となり、変数test1の中の値を一つづつ取得しています。

charAtの詳しい説明はこちら(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/charAt)を参照すると良いと思います。

また、上記のコードを次のように書いてみます。

let test1 = abcdef;
console.log(test1[0]);
console.log(test1[1]);
console.log(test1[2]);

これも同様に「a b c」と出力されます。

文字列を配列として扱うことも可能で、配列用のメソッドを使うことも可能です。
その際は読み出し専用の配列として処理されます。