可変長の引数リスト

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」と出力されます。

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

配列ではないオブジェクトに対しての配列メソッドの扱い

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

概要

javascriptの配列は、以下の特徴があります。

・配列に要素が追加されると、lengthプロパティが自動的に更新される
・lengthプロパティに現在の長さより小さな値を設定することで、配列を縮められる
・Array.prototypeからメソッドを継承している
・Arrayというクラス属性を持っている

上記は配列の特徴ですが、必ずこの特徴を持たなければならないことはないようです。(まだ推測の段階なので、検証は必要)
javascriptの配列ではないオブジェクトに対し、配列として扱っても、配列同様に振舞うことができます。

また、文字列を配列にように扱うことも可能です。

文字列に対し、インデックス指定でアクセスし、内容を取得したりすることが可能です。

ES2015(ES6)の配列メソッド

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

概要

ES6の配列に使えるメソッドを簡単に試してみます。

forEach

forEachは前のブログ投稿にも出てきました。
配列test1に対して、全ての要素を加算し、最終的な合計値を出力しています。
次のようなコードを書きます。

let test1 = [1, 10, 20, 30, 60];
let sum = 0;
test1.forEach(function(value) { sum += value; });
console.log(sum);

出力結果

Array(5) [ 2, 11, 21, 31, 61 ]

map

このメソッドは配列の要素を1つづつ、関数に引数として渡します。
関数から返された値は最終的に配列として返されます。
次のようなコードを書いて試してみます。

let test2 = [1, 10, 20, 30, 60];
test3 = test2.map(function(x) { return x * x; });
console.log(test2);
console.log(test3);

出力結果

Array(5) [ 1, 10, 20, 30, 60 ]
Array(5) [ 1, 100, 400, 900, 3600 ]

filter

配列要素の部分集合となる配列を返します。
次のようなコードを書いて試します。

let test4 = [1, 10, 20, 30, 60];
test5 = test4.filter(function(x) { return x > 15; });
console.log(test4);
console.log(test5);

出力結果

Array(5) [ 1, 10, 20, 30, 60 ]
Array(3) [ 20, 30, 60 ]

every

配列の各要素に対して、指定した関数内の演算結果をみます。演算結果が配列の全ての要素が真だった場合にはtrueを返します。(演算結果が偽の場合はfalseを返します)
次のコードを書いて試してみます。

//every
let test6 = [1, 10, 20, 30, 60];
test7 = test6.every(function(x) { return x < 20; });
console.log(test6);
console.log(test7);

//some
let test8 = [1, 10, 20, 30, 60];
test9 = test8.some(function(x) { return x < 20; });
console.log(test8);
console.log(test9);

出力結果

Array(5) [ 1, 10, 20, 30, 60 ]
false
Array(5) [ 1, 10, 20, 30, 60 ]
true

indexOf

要素の中から指定した値を持つ要素を検索して、見つかった場合は最初のインデックスを返す。

//indexOf
let test12 = [1, 10, 20, 30, 60];
test13 = test12.indexOf(20);
console.log(test12);
console.log(test13);

出力結果

2

indexOfの例

const testArray = [1, 2, 3, 4, 5];
const elementToCheck = 3;

if (testArray.indexOf(elementToCheck) !== -1) {
    console.log('配列内に存在する');
} else {
    console.log('配列内に存在しない');
}

javascriptの配列に対してのメソッド

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

概要

配列のメソッドについて試してみます。

メソッドはprototypeオブジェクトのメソッドとしてjavascriptの標準機能として実装されています。

近年ではECMAScriptのバージョンも上がってきており、最新の言語仕様に対応する必要性があります。

現在普及している言語標準化規格は「ECMAScript2015」(ES6)と「ECMAScript2016」(ES7)が大半になりますが、言語仕様が新しくなる前から実装されている基本的なメソッドについても試してみます。

join

配列の全ての要素を文字列に変換して連結するメソッドです。
実際に次のようコードを書いて動かしてみます。

let test1 = [1, 10, 20, 30, 60];
console.log(test1.join());

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

1,10,20,30,60

少しわかりづらいですが、出力結果はひとつづきの文字列になっています(カンマも含む)

sort

配列の全ての要素をソートするメソッドです。
実際に次のようコードを書いて動かしてみます。

let test2 = [10, 30, 1, 20, 60];
console.log(test2.sort());

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

Array(5) [ 1, 10, 20, 30, 60 ]

配列内の各要素を数値として比較し、小さい→大きい順にソートされています。

では、配列内の要素が数値と文字列があった場合に、どのような挙動になるか試してみます。
具体的に次のコードを書いて動作させてみます。

let test3 = ["xyz", 30, "abc", "5", 60];
console.log(test3.sort());

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

Array(5) [ 30, "5", 60, "abc", "xyz" ]

上記の出力結果はとても「ソートした結果」とは言えず、不完全なものです。
言語仕様にもよりますが、文字列と数値が混在した配列は、sort関数ではソート不能という結果になりました。

では、配列内の要素が全て数値である(負の数、0、正の数)場合のソート結果はどうなるでしょうか。
次のようなコードを書いて実行してみます。

let test4 = [-50, 30, -100, 0, 5, 60];
console.log(test4.sort());

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

Array(6) [ -100, -50, 0, 30, 5, 60 ]

これも、よく見るとソートが不完全です。
結果の後半で「30, 5, 60」のように、桁が考慮されておらず、単純に文字列の出現順(アルファベット順)として出力されています。

上記の配列test4を厳密に数値してソートするには、次のように書きます。

let test4 = [-50, 30, -100, 0, 5, 60];
//数値順としてソートする場合
console.log(test4.sort(function(a, b) { let ans = b - a; if(isNaN(ans)) { return -1; } else { return a - b; }}));

このように書くと、次のような出力結果になります。

Array(6) [ -100, -50, 0, 5, 30, 60 ]

これで正しくソートされています。
sort関数内で、無名関数を定義し、その無名関数の中で引数同士を比較して、比較結果により、引数をソートする判定を行っています。

より詳しく書くと、2つの引数「a」と「b」を比較し、

「b – a」の演算結果が
「正」になる場合は、aをbの後ろへ
「負」になる場合は、aをbの前へ

という無名関数を作り、その演算を繰り返すことで、正しいソート結果を得ています。

concat

concatメソッドは、配列に対して、要素を追加して新しい配列要素を返します。
具体的には次のように書きます。

let test5 = ["xyz", 30, "abc", "5", 60];
console.log(test5.concat(10, 20));

出力結果は

Array(7) [ "xyz", 30, "abc", "5", 60, 10, 20 ]

のようになります。
contactの引数「10, 20」の要素を、元の配列「test5」の最後に付け加えています。

slice

sliceメソッドは配列に対して、指定番目の要素をスライスして別な配列を返します。
引数が2つあり、1つ目の引数で、スライス開始の要素(○番目)を指定し、2つ目の配列で配列の末尾を指定します。
引数が1つのみの場合は、その引数で指定した要素から配列の最後までを取得します。
引数が負の数の場合は、配列の最後から数えた要素数からスライスされます。

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

let test6 = ["xyz", 30, "abc", "5", 60];
console.log(test6.slice(0, 2));
console.log(test6.slice(4));
console.log(test6.slice(-2));

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

Array [ "xyz", 30 ]
Array [ 60 ]
Array [ "5", 60 ]

splice

配列に要素を挿入したり、削除する場合に使います。
このメソッドの特徴として、メソッドを実行する配列に対し、直接要素を操作する点があります。

例えば、次のようなコードを書いて実行してみます。

let test7 = ["xyz", 30, "abc", "5", 60, 70, "80", 90];

console.log(test7.splice(3)); //出力結果 ["5", 60, 70, "80", 90]

//一旦、元の配列の要素を出力して確かめる
console.log(test7);  //出力結果 ["xyz", 30, "abc"]

//さらにspliceメソッドを使う
console.log(test7.splice(1, 2));  //出力結果 [30, "abc"]

//元の配列の要素を出力して確かめる
console.log(test7);  //出力結果 ["xyz"]

実行した際の2つめの出力を確認すると、spliceメソッドを使った後の配列は、要素が先頭の3つのみになっています。
つまり元の配列の要素数が変更され、要素が減っている状態と言えます。

さらに2回目のspliceの実行で、要素は元の配列の1番目から2つ削除されます。

最終的には元の配列の要素は1つだけになり、「”xyz”」の文字列が出力されました。

push pop

pushメソッドは配列に対して、最後に1つの要素を追加するメソッドです。
その反対にpopメソッドは、配列の最後の要素を削除します。

配列の長さは動的に変化し、pushした場合は1つ増加し、popした場合は1つ減少します。
この2つのメソッドを使うと、配列に対してのスタック操作が可能になります。

上記を踏まえて次のようなコードを試してみます。

let test8 = [];

test8.push(3)
console.log(test8);

test8.pop();
console.log(test8);

test8.push(5, 2);
console.log(test8);

test8.pop();
console.log(test8);

test8.push(4);
console.log(test8);

test8.push(6);
console.log(test8);

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

Array [ 3 ]
Array []
Array [ 5, 2 ]
Array [ 5 ]
Array [ 5, 4 ]
Array(3) [ 5, 4, 6 ]

pushとpopのメソッドを実行した後、それぞれ配列の最後の値が増加、減少していることがわかります。

unshift shift

unshiftメソッドとshiftメソッドは、配列の先頭の要素を操作します。

unshiftは配列の先頭に要素を追加します。
shiftは配列の先頭の要素を削除します。

先ほどpushで書いたコードに似た次のようなコードを書いて試してみます。

let test9 = [];

test9.unshift(3)
console.log(test9);

test9.shift();
console.log(test9);

test9.unshift(5, 2);
console.log(test9);

test9.shift();
console.log(test9);

test9.unshift(4);
console.log(test9);

test9.unshift(6);
console.log(test9);

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

Array [ 3 ]

Array []

Array [ 5, 2 ]

Array [ 2 ]

Array [ 4, 2 ]

Array(3) [ 6, 4, 2 ]

pushやpopと違い、配列の先頭に要素を追加していることがわかります。

toString

配列に対してのtoStringメソッドを試してみます。
このメソッドは配列要素の一つ一つを文字列に変換してカンマ区切りのリストを出力します。

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

let test10 = ["xyz", 30, "abc", "5", 60, 70, "80", 90];
console.log(test10.toString());

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

xyz,30,abc,5,60,70,80,90

配列要素が全てカンマ区切りで連結され、全体でひとつの文字列として出力されています。

また、類似するメソッドとして、toLocaleStringメソッドがあります。
メソッドの動きはtoStringと同様の動きになりますが、区切り文字がロケール固有区切り文字で区切られます。
区切り文字はプログラム実行時の、ブラウザ環境に依存します。

javascriptの多次元配列

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

概要

javascriptで多次元配列を扱う場合を試してみます。

厳密に言うと、javascriptは言語仕様としての多次元配列はありません。
言語仕様としては存在しませんが、擬似的に配列と配列を組み合わせて多次元配列となるような配列を生成できます。

実際にコードを書いて多次元配列を試してみる

javascriptでコードを書いて、多次元配列のような配列を試してみます。

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

//通常の配列を定義する
let test1 = [1, 10, 20, 30, 60];

//要素の全てを回して処理
for (let i = 0; i < test1.length; i++) {
	//ループ内で、要素ごとに、さらに新規配列を定義して代入する
	test1[i] = [2, 20, 40, 60, 80];
}

//ループ後の結果を出力1(わかりやすいように、1次元目の要素を一つづつ出力する)
console.log(test1[0]);
console.log(test1[1]);
console.log(test1[2]);
console.log(test1[3]);
console.log(test1[4]);

//ループ後の結果を出力2(わかりやすいように、1次元目の要素内の2次元目の要素を出力する)
console.log(test1[0][0]);
console.log(test1[0][1]);
console.log(test1[0][2]);
console.log(test1[0][3]);
console.log(test1[0][4]);

実際に動作させた結果は、次のようになります。

Array(5) [ 2, 20, 40, 60, 80 ]
Array(5) [ 2, 20, 40, 60, 80 ]
Array(5) [ 2, 20, 40, 60, 80 ]
Array(5) [ 2, 20, 40, 60, 80 ]
Array(5) [ 2, 20, 40, 60, 80 ]
2
20
40
60
80

出力結果を確認すると、1次元目の要素には「Array(5) [ 2, 20, 40, 60, 80 ]」というように、1つの要素内にさらに配列が格納されていることがわかります。

次に、2次元目の要素を一つづつ出力しているのですが、順に、2, 20, 40, 60, 80 という形で2次元目の要素を一つづつ出力することができています。

では次に、上記のコードを少し改造して、次のようなコードを書いて実行してみます。

//通常の配列を定義する
let test2 = [1, 10, 20, 30, 60];

//要素の全てを回して処理
for (var i = 0; i < test2.length; i++) {
	//ループ内で、要素ごとに、さらに新規配列を定義して代入する
	//要素内の値に対して i を加算
	test2[i] = [2 + i, 20 + i, 40 + i, 60 + i, 80 + i];
}

//ループ後の結果を出力1(わかりやすいように、1次元目の要素の一つづつ出力する)
console.log(test2[0]);
console.log(test2[1]);
console.log(test2[2]);
console.log(test2[3]);
console.log(test2[4]);

//ループ後の結果を出力2(わかりやすいように、1次元目の要素内の2次元目の要素を出力する)
//2次元目の1つ目の要素を出力してみる
console.log(test2[0][0]);
console.log(test2[0][1]);
console.log(test2[0][2]);
console.log(test2[0][3]);
console.log(test2[0][4]);

//2次元目の2つ目の要素を出力してみる
console.log(test2[1][0]);
console.log(test2[1][1]);
console.log(test2[1][2]);
console.log(test2[1][3]);
console.log(test2[1][4]);

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

Array(5) [ 2, 20, 40, 60, 80 ]
Array(5) [ 3, 21, 41, 61, 81 ]
Array(5) [ 4, 22, 42, 62, 82 ]
Array(5) [ 5, 23, 43, 63, 83 ]
Array(5) [ 6, 24, 44, 64, 84 ]
2
20
40
60
80
3
21
41
61
81

最初の1次元目の出力結果を見てみると、forでループする際のカウンタ「i」の変数が、2次元目の各要素に対して加算されていることがわかります。

2次元目の2つ目の要素を出力してみると、加算された結果として「3, 21, 41, 61, 81」が出力されていることがわかります。

このようにしてjavascriptでも、多次元配列として配列を使うことができ、行列の計算や、座標の計算等に応用すること可能になると言えます。

2次元以上の配列も応用次第で活用することができます。

配列の全ての要素にアクセスする方法

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

概要

配列に対して、全ての要素に順にアクセスする方法を試してみます。
別な言い方をすればイテレータ(反復子とも言います)についての挙動を実際に試します。

forループによるアクセス

実際に次のようなコードを書いて、配列の全ての要素に対して出力を行ってみます。

let test1 = [1, 10, 20, 30, 60, 90];
for (var i = 0; i < test1.length; i++) {
	console.log(test1[i]);
}

上記のコードは単純に配列の全ての要素にアクセスしています。
出力結果は次のようになります。

1
10
20
30
60
90

では、この配列が、途中で値が存在しない場合の配列の場合を試してみます。
配列の3つめ5つめをnullにしてあります。

let test2 = [1, 10, , 30, , 90];
for (var i = 0; i < test2.length; i++) {
	console.log(test2[i]);
}

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

1
10
undefined
30
undefined
90

ループ処理の3番目と5番目の要素にアクセスする際、配列の要素がないので「undefined」として結果が出力されます。

上記のことから、ループ処理でイテレータの実行する時は、配列の要素が全て存在する場合か、または存在することをチェックするプログラムを書く必要があります。

例えば次のように書くことで配列の要素が存在しない場合はループをスキップすることが可能です。

let test3 = [1, 10, , 30, , 90];
for (var i = 0; i < test3.length; i++) {
	if (test3[i]) {
		console.log(test3[i]);
	}
}

上記のコードの場合は、配列の要素が存在する場合のみtrueと判定されるので、次のような出力結果になります。

1
10
30
90

if文での判定を行う際に、要件仕様によってcontinue等を使って処理をスキップする場合も有用です。

for in によるアクセス

上記のような配列に対して、for文と同じようにアクセスする構文として「for in」という構文があります。
これも同様に動きを試してみます。

次のようなコードを書きます。

let test4 = [1, 10, , 30, , 90];
for (var index in test4) {
	console.log(test4[index]);
}

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

1
10
30
90

for in構文の場合、値が空白やnull、undefinedの要素にはアクセスせず、次の要素にアクセスします。

for in構文で注意が必要なのは、動作するブラウザ環境により、処理される順番が担保されないという点です。(降順/昇順などがバラバラになる)

順番を重視する要件の場合にはfor 文を使ったほうが無難と言えます。

forEach()メソッド によるアクセス

では次にforEach()メソッドを使った場合について動作を確認してみます。

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

let test5 = [1, 10, , 30, , 90];
test5.forEach(function(x) {
	console.log(x);
});

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

1
10
30
90

このメソッドを使った場合も、存在しない要素については処理されず、値が存在する要素を処理しています。

また、for inメソッドとの大きな違いは、処理される順番がインデックス順になる。という点です。

上記のような書き方はjavascriptの関数型プログラムに発展することができ、それはまた別な投稿で取り上げたいと思います。

要素の追加と削除

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

概要

配列の要素を追加する場合と、削除する場合について、調べてみます。

要素の追加

配列の要素を追加する動きを確かめるため、次のようなサンプルコードを実行してみます。

let test1 = [];

test1[0] = "abc";
test1[1] = "def";

console.log(test1[0]);
console.log(test1[1]);

配列test1を生成し、要素の1番目と2番目に対して値を入れています。
出力結果は「abc」「def」になります。

さらに次のようなコードを実行してみます。

let test2 = [];

test2.push("abc");
test2.push("def", "ghi");

console.log(test2[0]);
console.log(test2[1]);
console.log(test2[2]);
console.log(test2.length);

test2[test2.length] = "jkl";

console.log(test2[3]);

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

abc
def
ghi
3
jkl

配列に対してのpushは要素を一つ追加して、値を代入する動きをします。
pushは引数が複数ある場合は、配列の一つづつの要素として代入する動きをします。

途中で配列のインデックス「test2.length」に対して値を代入していますが、
「test2.length」とすることで配列の最後の要素を示すことができます。
上記の場合は3つ目の要素に対して値を代入しています。

要素の削除

配列の要素を削除するには、delete演算子を使います。(その他の方法もあります)
次のようなコードを書いて実行してみます。

//要素の削除

let test3 = [];

test3[0] = "z";
test3[1] = "y";
test3[2] = "x";
test3[3] = "w";

console.log(test3[0]);
console.log(test3[1]);
console.log(test3[2]);
console.log(test3[3]);

console.log(test3.length); //出力結果は4

//要素のインデックス1を削除
delete test3[1];

console.log(test3[0]);
console.log(test3[1]); //出力結果はundefined
console.log(test3[2]);
console.log(test3[3]);

console.log(test3.length); //出力結果は4

上記のコードを実行すると、配列test3の1つめの要素をdelete演算子を使って削除しています。
この時、lengthの結果をみても削除前と削除後は結果は変わらないという特徴があります。

その他の配列要素の削除について

上記の例の他、要素を削除するには、popメソッドやshiftメソッドがあります。
delete演算子とは異なり、popやshiftの場合は要素の数が変動します。
この削除のされ方の違いは、プログラムを実装する際に非常に重要になるので、注意して実装します。

pop、shiftについては、また別途、ブログ記事でとりあげる予定です。

配列に対してのlengthプロパティについて

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

概要

配列の長さについて試してみます。
配列の長さはlengthプロパティで調べます。
ここでいう長さとは、要素数と同じことを意味します。

前回のブログ投稿で、疎な配列について書きましたが、その場合は要素数とlengthの長さの関係は、必ずしも一致はしません。

簡単なサンプル

次のような簡単なサンプルコードを書いて動かしてみます。

let test1 = new Array(10);

let test2 = [];

let test3 = [1, 10, 20, 30, 60, 90];

console.log(test1.length);
console.log(test2.length);
console.log(test3.length);

上記の出力結果は「10」「0」「6」になります。

lengthプロパティは通常、配列の要素数を出力する場面で使うことが多いですが、
反対に配列の要素に対してlengthプロパティを使って設定することもできます。

次のコードを書いて実行してみます。

let test4 = [1, 10, 20, 30, 60, 90, 100];

console.log(test4.length);

test4.length = 4;

console.log(test4.length);

最初の出力では「7」と出力されますが、次の出力では「4」と出力されます。
配列test4に対して、lengthプロパティに対して「4」を設定したので、4つ目以降の要素が削除される動きになります。

配列の要素が0から埋まっていない配列

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

概要

これまでのサンプルコードの中でも出てきましたが、
配列の要素が0から埋まっていない配列についてまとめます。
上記のような配列を疎な配列と呼びます。

特徴

この疎な配列の特徴は次のようになります。
この場合のlengthで取得した要素数は、実際の要素数よりは大きくなります。
疎な配列は実行速度は遅くなる代わりに、メモリ効率は上がります。

配列リテラルで値を省略した場合の挙動

配列を生成する再に、配列リテラルで生成し、その値が無い場合(省略した場合)は、疎な配列にはならないです。
この場合は、undefinedの値として配列が生成されます。

配列の要素へのアクセス方法について

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

概要

javascriptの配列について、要素へのアクセス方法について試してみます。

配列への代入

配列の要素にアクセスする方法は[]演算子を使います。
具体的には次のようなコードになります。

//要素が空の配列を作成
let test1 = [];

//要素の1に値を代入
//(この時、配列の0番目はemptyで空なので全体の要素数は1つ)
test1[1] = 10;

//要素の0に文字列「abc」を代入
test1[0] = "abc";

//要素へのアクセスに変数を使う(その1)
let arr_str = 4
test1[arr_str] = "efg";

//要素へのアクセスに式を使う(その2)
let arr_cal = 5;
test1[arr_cal + 10] = "hij";

便宜上、開発ツールのログ出力命令は省略していますが、出力結果は次のようになります。

Array [ <1 empty slot>, 10 ]

Array [ "abc", 10 ]

Array(5) [ "abc", 10, <2 empty slots>, "efg" ]

(16) […]
0: "abc"
1: 10
4: "efg"
15: "hij"
length: 16
<prototype>: Array []

(<>で表現している箇所は、ブログの構造上、半角文字の「<>」ですが、表示エラーになるので全角にしています)

上記の4つ目の出力結果だけは、配列を展開した形で出力を確かめています。

代入した値の取得

次に配列に代入した値を取り出してみます。

ここで注意が必要なことは、上記の例で配列で値を代入する場合、
配列のインデックスとしての「1」を使っていますが、実際は文字列の1として要素名を確定していることです。
これは[]演算子を使って配列の値を代入する場合の動作になるので注意が必要です。

値を取り出すには次のような書き方をします。

console.log(test1[0]);
console.log(test1[1]);
console.log(test1[13]);

便宜上、console.logで出力していますが、実際には「test1[0]」のように記述します。

上記の実行結果は、値が正常に取得できた場合は、配列内の値を取得、
インデックスが正しくない、存在しない場合は、undefinedが出力されます。

上記の例は次のようになります。

abc
10
undefined

当然、3番目の例は、配列のインデックスの13には要素を代入していないので、値の取得ができずにundefinedになります。
javascriptでは配列の値が取得できない場合はエラーで処理落ちすることなく、undefinedになるので注意が必要です。

配列の要素数について

配列を定義し、値を代入して利用すると、代入した数だけの要素が増えることになります。(要素を削除しない限りは)

要素数を出力するには、lengthプロパティを使います。

console.log(test1.length);

上記の出力結果は「16」となります。
ここで16となる理由を考察してみると、全ての要素に値を格納されている訳ではなく、
一番最初の値の代入の際に

let arr_cal = 5;
test1[arr_cal + 10] = "hij";

という配列の添え字に整数を使って代入した為、配列のインデックスとして解釈され、0から15までの要素が作成された為になります。
この時、途中で要素の代入をしていないインデックスは空(undefined)という形で要素が生成されます。

上記の考えにより「console.log(test1[13]);」という出力は「undefined」という形で出力されると言えます。

配列のインデックスに非整数値を使った場合

ここまでは配列のインデックスに数値を使ってきましたが、
あえて文字列を設定して代入と取得を試してみます。

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

let test2 = [];

test2["z"] = 10;

console.log(test2);

出力結果は

[]
length: 0
z: 10
<prototype>: Array []

のようになります。
配列のN番目に値が格納されているわけではなく、配列のプロパティ名「z」に対して、値「10」が格納されていることになります。

試しに、この配列に対して、0番目や1番目の要素を取得してみます。

console.log(test2[0]);
console.log(test2[1]);

出力結果は、共に「undefined」になります。

また、この状態で配列test2に対してlengthを取得してみます。

console.log(test2.length);

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

配列のインデックスに非整数値を使った場合(その2)

では、配列のインデックスにマイナスの値を使うとどうなるか試してみます。

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

let test3 = [];

test3[-50] = 100;

console.log(test3);

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

[]
"-50": 100
length: 0
<prototype>: Array []

インデックスに「-50」と指定しましたが、この場合の動きは要素の「数」としてのインデックスではなく「-50」というプロパティ名として解釈され、「-50」の要素として「100」が配列に格納されている動きになります。

オブジェクトに対してのメソッド

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

概要

javascriptのオブジェクトに対してのメソッドを調べてみます。

javascriptのオブジェクトは全てObject.prototypeからプロパティとメソッドを継承しています。
代表的なメソッドを具体的に調べてみます。

toString()

toString()メソッドはオブジェクトの値を表す文字列を返します。
オブジェクトを文字列に変換する必要がある場合、このメソッドを呼び出します。
例えば、配列を文字列に変換すれば、文字列に変換された配列要素のリストが取得できます。

toLocaleString()

このメソッドはオブジェクトを表すローカライズされた文字列を返します。

valueOf()

このメソッドは文字列以外の基本型にオブジェクトを変換する必要があるときに使います。

オブジェクトのシリアライズ

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

オブジェクトのシリアライズ

javascriptのオブジェクトのシリアライズについて、試してみます。

オブジェクトのシリアライズは、一度シリアライズしたオブジェクトを元に戻せるような文字列に変換します。
他のプログラム言語にもあるように、シリアライズ操作はプログラミングをするうえで要所で使う場面が出てきます。

ネイティブ関数の使用

まず、オブジェクトをシリアライズするケースからです。
javascriptのネイティブ関数として、JSON.stringify()という関数が用意されています。

逆に一度シリアライズした文字列をアンシリアライズ(元に戻す)場合は、
JSON.parse()という関数が用意されています。

実際にシリアライズを試してみます

まずは、検証用オブジェクトをシリアライズして、ブラウザの開発ツールでログ出力してみます。

//テスト用オブジェクトの用意
let test1 = {
	a: '10',
	b: '20',
	get access_a() {
		return this.a;
	}
}

//オブジェクト「test1」をシリアライズします
let s_obj = JSON.stringify(test1);

console.log(s_obj);

//出力結果は、以下のようになる
{"a":"10","b":"20","access_a":"10"}

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

次にアンシリアライズを試してみます

上記の例で使用した変数を、そのままアンシリアライズしてみます。

//(先ほどシリアライズした際の変数を使用)アンシリアライズ
let us_obj = JSON.parse(s_obj);

console.log(us_obj);

//出力結果
Object { a: "10", b: "20", access_a: "10" }

出力結果は上記のようになります。
ブラウザごとに出力結果の表示方法は変わるので、見え方は異なりますが、オブジェクトの内容が列挙されます。

JSON形式について

json形式はjavascrptでプログラムする際に頻繁に使用するデータ形式になります。

正式には「Javascript Object notation」という名称になり、javascriptでオブジェクトリテラルや配列リテラルににたデータ形式でデータ構造を表したり、保持したりします。

シリアライズ可能なものと不可能なもの

シリアライズはjavascriptの値を全てサポートしているわけではないです。

次にサポートしているものと、サポートしていないものを整理してみます。

サポートしているもの

オブジェクト
文字列
配列
数値
true
false
null
NaN
Infinity
Dateオブジェクト

サポートされていないもの

Function
RegExp
Error
undefined

オブジェクトの拡張可属性について

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

オブジェクトの拡張可属性

オブジェクトに対して、新しくプロパティを追加できるかどうかの指定ができます。
この指定のことを拡張可属性と呼びます。

オブジェクトが拡張可かどうかは、ECMAScriptの処理系によって変わってきます。

ES5は組み込みオブジェクトとユーザ定義オブジェクトは拡張可になります。
明示的に拡張不可が設定された場合は拡張不可になります。

拡張可か拡張不可かを調べるには、Object.isExtensible()で調べます。

オブジェクトと拡張不可にするには、Object.preventExtensions()を使います。

オブジェクトの全ての独自プロパティを再定義不可にするには、Object.seal()を使います。
この場合、オブジェクトに新しいプロパティを追加することはできず、
削除することも再定義することもできません。

オブジェクトをロックするには、Object.freeze()を使います。
オブジェクトを拡張不可にしてプロパティの再定義も不可になります。
また、オブジェクトの独自データプロパティを読み出し専用にします。

オブジェクトのクラス属性について

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

概要

クラス属性について調べてみます。

クラス属性はオブジェクトの型情報を表す文字列です。
この属性は文字列として設定されており、取得の方法は限定的です。

オブジェクトからクラス属性を取得するにはtoString()メソッドを使います。

実際に簡単なコードを書いてみて動作させてみます。

var test1 = {
	a: '10',
	b: '20',
	get access_a() {
		return this.a;
	}
}
console.log(Object.prototype.toString.call(test1));

var test2 = new Object();
console.log(Object.prototype.toString.call(test2));

var test3 = new Array();
console.log(Object.prototype.toString.call(test3));

var test4 = new Date();
console.log(Object.prototype.toString.call(test4));

上記のコードの実行結果をブラウザの開発ツール(コンソール出力)で確認すると、次のようになります。

[object Object]
[object Object]
[object Array]
[object Date]

出力のルールとしては、先頭部分に「[object]」という文字列が必ず出力されており、その後にオブジェクトのクラス属性が出力されます。

オブジェクト属性の種類について

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

概要

javascriptのオブジェクトの属性には、3種類の属性があり、
オブジェクトの属性ごとに、使用方法や取得方法、設定方法が変わってきます。

いままではオブジェクトの属性を意識せずにサンプルのコードを書いていましたが、属性ごと詳しく違いを調べてみます。

プロトタイプ属性

オブジェクトが生成されるときにプロトタイプ属性が設定され、オブジェクトの生成方法によってプロトタイプの設定のされかたが変わります。オブジェクトリテラルを使って生成する場合
object.prototype
がプロトタイプ属性使われます。

newを使って生成する場合
コンストラクタ関数のprototypeプロパティの値が使われます。

Object.create()を使って生成する場合
Object.create()に渡された最初の引数が使われます。

オブジェクトのプロパティ属性について

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

概要

オブジェクトのプロパティには属性があります。

属性は大きく分けて「書き込み可能」「列挙可能」「再定義可能」などがあります。

また、アクセサプロパティのゲッターメソッドとセッターメソッドをプロパティ属性として考えるとすると、アクセサプロパティには書き込み可能属性がないと言えます。

データプロパティの属性は、値も属性のひとつとみなし、書き込み可能、列挙可能、再定義可能、の合計4つの属性があると言えます。
(アクセサプロパティには値の属性はないものと考えます)

まとめるとアクセサプロパティは、ゲッター、セッター、列挙可能、再定義可能、の4つの属性があると言えます。

プロパティのメタ属性の取得と設定


上記の概要で書いた4つの属性について、プロパティディスクリプタと呼ばれるオブジェクトを使います。

プロパティディスクリプタはプロパティのメタ属性と言え、データアクセスに対しての取り決めを決定しています。

データプロパティについては、次のようなメタ属性の名前を持ちます。
value
writable
enumerable
configurable
また、アクセサプロパティについては、次のようなメタ属性を持ちます。
get
set
enumerable
configurable
アクセサプロパティは、valueとwritableのメタ属性はありません。

試しに、前回の投稿で書いたサンプルコードに対して、プロパティディスクリプタオブジェクトを取得して試してみます。
var test = {
	a: '10',
	b: '20',
	get access_a() {
		return this.a;
	}
}

//プロパティディスクリプタオブジェクトを使用してtestオブジェクトのプロパティを出力
console.log(Object.getOwnPropertyDescriptor(test, 'a'));
上記のサンプルコードを実行した結果は次のようになります。

{…}

configurable: true

enumerable: true

value: "10"

writable: true

&lt;prototype&gt;: {…}

__defineGetter__: function __defineGetter__()

__defineSetter__: function __defineSetter__()

__lookupGetter__: function __lookupGetter__()

__lookupSetter__: function __lookupSetter__()

constructor: function Object()

hasOwnProperty: function hasOwnProperty()

isPrototypeOf: function isPrototypeOf()

propertyIsEnumerable: function propertyIsEnumerable()

toLocaleString: function toLocaleString()

toSource: function toSource()

toString: function toString()

valueOf: valueOf()

ブラウザの開発ツールの出力なので、出力結果がまとめられている箇所があり、出力結果のオープン/クローズで表示されるので一部抜粋しています。

上記の出力結果では
configurable: true

enumerable: true

value: "10"

writable: true
という先ほどあげたメタ属性についての記述があります。
また、上記の結果から、testオブジェクトのaプロパティは、データプロパティであることがわかります。(get、setが無く、valueやwritable属性が表示されています)

また、getOwnPropertyDescriptor()の処理対象はオブジェクトに対して独自プロパティのみが対象となります。
継承されたプロパティ等は対象ではない為に、継承オブジェクトには明示的にgetOwnPropertyDescriptor()を書く必要があります。

definePropertyについて

ここまでは、getOwnPropertyDescriptorを使ってオブジェクトのプロパティを調べる形でしたが、
オブジェクトのプロパティに対して、属性を設定(変更)するメソッドについても調べてみます。

オブジェクトに対してプロパティの属性を変更するには、
defineProperty
を使います。

書き方は
Object.defineProperty()
という形になり、()内で設定したいオブジェクトと、プロパティ名、それと属性(上記でいうメタ属性)を設定します。

具体的には、次のようになります。
var test = {
	a: '10',
	b: '20',
	get access_a() {
		return this.a;
	}
}

//definePropertyを使用して属性の変更
Object.defineProperty(test, 'a', {
	value: '30',
	writable: false,
});

//testオブジェクトのプロパティaに対し、文字列40を代入する
test.a = '40';

//内容を確認
console.log(test.a); //出力結果「30」
上記の例はtestオブジェクトのプロパティaに対して、メタ属性を変更しています。また、値の設定についても同時に行っています。

出力結果については、「30」と出力され、writableでfalseにしている為、値の代入を行っても書き変わらない挙動になります。

以上のように、オブジェクトのプロパティに対しての属性の設定を簡単に試してみました。

上記以外の命令や書き方があるので、ブログに追記する形で更新いたします。

ゲッターメソッドとセッターメソッド

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

概要

javascriptのオブジェクトについては、前回の投稿で簡単なオブジェクトの書き方、使い方を例にあげました。

オブジェクトについて、もう少し入り組んだ概念について詰めます。

オブジェクトに対してゲッターとセッターと呼ぶメソッドを指定でき、それぞれプロパティという位置づけで解釈することができます。

また、ゲッターとセッターについては、それぞれアクセサプロパティと、データプロパティという名前で区別して考えることができます。

アクセサプロパティについて

アクセサプロパティは書き込み不可の属性です。

プロパティがゲッターとセッターの両方のメソッドを持つ場合、 読み書き可のプロパティになります。

ゲッターしかない場合は、読み出し専用のプロパティになり、
セッターしかない場合は、書き込み専用のプロパティになります。

また、データプロパティは単純に値のみを持ちます。

簡単な例

具体的なゲッターメソッドは次のように定義します。

//ゲッター
var test = {
	a: '10',
	b: '20',
	get access_a() {
		return this.a;
	}
}

//呼び出す時の書き方
console.log(test.access_a);
この場合は、変数testに対して、ゲッターメソッド「access_a」を定義しています。

access_aを呼び出すと、メソッド内でプロパティaの値が、返されます。

文字通り「get」キーワードを下記、その後にメソッド名と処理を書きます。

次にセッターメソッドの場合は次のように定義します。

//セッター
var test = {
	c: '30',
	d: '40',
	get access_c() {
		return this.c;
	},
	set access_c(value) {

		//わかりやすいように10倍に計算する
		var sub_c = value * 10;

		this.c = sub_c;
	},
}

//呼び出す時の書き方
console.log(test.access_c); //出力結果「30」

//testオブジェクトのセッタプロパティに対して値を代入
test.access_c = 50;

console.log(test.access_c); //出力結果「500」

上記のようにセッターメソッドを使う時は、オブジェクトのプロパティに対して値を代入するように書きます。

例ではわかりやすいように、セッターメソッド内で値を10倍にして返しています。

セッターに値を入れた後に、改めてゲッターメソッドを使うと、出力結果は計算された結果が返ってきます。

オブジェクトのプロパティにアクセスする方法

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

概要

javascriptのオブジェクトのプロパティにアクセスする方法を試してみます。 簡単な方法として、for in を使ってオブジェクトにアクセスしてみます。 具体的には下記のように書きます。
var test = {a:10, b:20, c:30}

for (hoge in test) {
	console.log("hoge -> " + hoge);
}

//出力結果は次のようになります
hoge -> a
hoge -> b
hoge -> c
上記のようにfor inを使うとオブジェクト内の「プロパティ」にアクセスすることができます。 では、オブジェクト内の「値」にアクセスするにはどうすればよいか、試してみます。
var test = {a:10, b:20, c:30}

for (hoge in test) {
	console.log("hoge -> " + test[hoge]);
}

//出力結果は次のようになります
hoge -> 10
hoge -> 20
hoge -> 30
最初の例と似ていますが、for inで回した値を参照するタイミングで「オブジェクト[プロパティ]」という記述することで内容を取得することが可能になります。 上記の例は必ずしもこう書かないといけないわけではないので、参考例として書いています。 また、オブジェクトが継承されているケースもあるので、プログラムの実装時には挙動に注意が必要です。

オブジェクトの内容を確認する

オブジェクトの内容が、配列、文字列、オブジェクト等、不明な場合は下記のように書いて簡易的に調べることができます。
// 「obj_check」は調べたいオブジェクト
for(let key in obj_check){
    console.log(key + " : " + obj_check[key]);
}
ただし、最近のブラウザの開発ツールはどんどん改善されているので、いちいちコードを書かなくてもオブジェクトの内容を把握できる方法があるかと思います。 簡易的なチェックという認識で使用するとよさそうです。


オブジェクトのプロパティの削除

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

概要

javascriptでの、オブジェクトのプロパティの削除についてです。

これまでにもこのブログで出てきましたが、オブジェクトのプロパティを削除するには、delete演算子を使います。

前回のオブジェクト生成の例をもとにdelete演算子の挙動をみてみます。

//新規でオブジェクトを生成する
var test = {a:1, b:2}
  
//.で読み出す
console.log(test.a);
  
//[]で読み出す
console.log(test["a"]);

//delete演算子を使って、プロパティ「a」を削除する
delete test.a;

//delete演算子を使って、プロパティ「b」を削除する
delete test[b];

上記の場合、前者の削除の方法は.を使ってプロパティを指定しています。
後者の削除の方法は[]を使ってプロパティを指定しています。
どちらも同じ同様の動きになります。

注意する点としては、継承されたオブジェクトの継承プロパティは削除されないことです。

delete演算子はプロパティの削除が成功した場合にtrueを返します。
delete演算子は、再定義可の属性がfalseになっているプロパティは削除しないです。

また、注意しなければならないのは、use strictモードの場合と非use strictモードの場合で、挙動が異なる点です。
use strictモードでは再定義不可のプロパティを削除するとtype error例外が発生します。
非use strictモードでは同様の削除をするとfalseを返します。

オブジェクトのプロパティ呼び出しについて

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

概要

オブジェクトのプロパティの呼び出しについてです。

プロパティを呼び出す時の注意点としては、次のような点があります。

・存在しないプロパティから値を取得する
・独自プロパティから値を取得する
・継承したオブジェクトのプロパティから値を取得する
・存在しないオブジェクトからプロパティの値を取得する

上記の点では、存在しないプロパティから値を取得しようとしてもエラーにはならず、undefinedになります。
また、独自プロパティ、継承したオブジェクトのプロパティから値を取得しようとしても存在しないプロパティの場合はundefinedになります。

ただし、存在しないオブジェクトからプロパティを読み出すとエラーになります。(上記4つめのケース)

オブジェクトの継承とプロトタイプオブジェクト、プロトタイプへの参照と代入の挙動について

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

概要

javascriptのオブジェクトと、オブジェクトの継承、そしてオブジェクトのプロパティへのアクセスについてまとめます。

javascriptのオブジェクトの独自プロパティについては、そのオブジェクトとプロトタイプオブジェクトから継承したプロパティがあります。

オブジェクトのプロパティへのアクセスについて

例としてオブジェクトAの独自プロパティpを参照しようとする場合、
オブジェクトAの独自プロパティpにアクセスします。

この際、独自プロパティpがオブジェクトAに存在しなければ、
オブジェクトAのプロトタイプオブジェクトA'(ここでは便宜上A’とします)のプロパティpがあるかチェックします。

プロトタイプオブジェクトA’が、プロトタイプチェーンにより、さらにプロトタイプオブジェクトが継承されている場合は、その継承しているプロトタイプオブジェクト(オブジェクトA”とします)のプロパティpがあるかチェックします。

上記のような流れで任意のオブジェクトの任意のプロパティへアクセスする挙動になります。

文章にするとわかりにくい面があるので、簡単なイメージ図にしていみました。

オブジェクトのプロパティへ値を代入する場合

上記の例とは反対に、オブジェクトAのプロパティpに対して、値を代入する場合の挙動をまとめます。

参照する場合とは反対に代入する場合は、動きが変わってきます。

まず、オブジェクトAにプロパティpが存在する場合は、
プロパティpの値がそのまま変更されて終了します。
この場合、プロトタイプチェーンで継承されているオブジェクトのプロパティは無関係で影響を受けません。

次に、オブジェクトAにプロパティpが存在しない場合は、
オブジェクトAに対して、プロパティpが新規追加されます。
この場合も、プロトタイプチェーンで継承されているオブジェクトのプロパティは無関係で影響を受けません。


オブジェクトのプロパティを連想配列として解釈する

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

概要

前回の投稿では、生成したオブジェクトのプロパティにアクセスする方法を書きました。

//新規でオブジェクトを生成する
var test = {a:1, b:2}
 
//.で読み出す
console.log(test.a);
 
//[]で読み出す
console.log(test["a"]);

上記の方法で、2つ目に書いた[]で読み出す方法について、詳しくみてみます。
.で呼び出す方法と[]で呼び出す方法は、どちらも同じ結果を得ることができますが、[]で呼び出す方法については、文字列を使って呼び出していることが特徴的です。

文字列で呼び出すことが特徴なので、オブジェクトを配列と見立てると連想配列として考えることができます。

より、簡単な方法でオブジェクトの中身を確認する

コンソール上で [object Object] という表示になった場合、
以下の方法で内容を確認することができます。

console.log(JSON.stringify(確認したいオブジェクト));

連想配列としてのオブジェクト

連想配列としてのオブジェクトとして考えると、javascript特有の柔軟な配列操作が可能になります。

例えば以下のように、オブジェクトのプロパティ名が動的に変わる場合でも処理がされます。

var test = {a:1, b:2}

//新たなプロパティを指定して値をセット
//例1
test["c"] = 3;

//例2
test["d"] = 4;


//プロパティ名に、動的な変数を使用
var o = 1;
//例3
test["e" + o] = 5;

//例4
test["e" + (o + 1)] = 6;


//プロパティ名に、動的な変数を使用(その2)
var p = 3;
//例5
test["e" + p] = 7;

//例6
test[p] = 8;


//プロパティ名に、動的な変数を使用(その3)
var q = 4;

//例7
test[q] = 9;

//例8
test[q + 4] = 10;


//確認の為、呼び出し
console.log(test["a"]);   //出力結果:1
console.log(test["b"]);   //出力結果:2
console.log(test["c"]);   //出力結果:3
console.log(test["d"]);   //出力結果:4
console.log(test["e1"]);  //出力結果:5
console.log(test["e2"]);  //出力結果:6
console.log(test["e3"]);  //出力結果:7
console.log(test[p]);     //出力結果:8
console.log(test["p"]);   //出力結果:undefined
console.log(test[q]);     //出力結果:9
console.log(test[4]);     //出力結果:9
console.log(test["q"]);   //出力結果:undefined
console.log(test[8]);     //出力結果:10

後半の例6、例7、例8は少し紛らわしく書きました。

例6について

オブジェクトのプロパティを文字列を示すダブルクォートで囲っていないので、
これは単純に配列のn番目に値を代入する動きになります。

その証拠として、例8ではプロパティを単なる数値として直接指定して値を呼び出しても出力結果が「10」という形で取り出せます。

また、例6に対して、

test["p"]

とした場合の出力結果はundefinedになります。

これはtest[p]という(pは数値の3が代入されている)オブジェクトのプロパティへ代入していますが、pは数値の3なので配列の3番目(0はじまりだと4番目)に値を代入している解釈になるので、
文字列としてのtest[“p”]という形で値を取り出そうとしても存在しない為にundefinedになります。

例7について

オブジェクトのプロパティに対して数値を指定している為、
値を参照する際にも、test[“q”]という形で文字列として取り出そうとするとundefinedになります。

例8について

オブジェクトのプロパティが数値の加算になっているので、
配列の添え字として解釈され、問題なく値が取得できます。

オブジェクトのプロパティ読み出しについて

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

オブジェクトのプロパティを読み出す方法

オブジェクトのプロパティを読み出す方法は、
以前の投稿でも取り上げましたが、次のように書きます。

var test = {a:1, b:2}

//.で読み出す
console.log(test.a);

//[]で読み出す
console.log(test["a"]);

上記はtestオブジェクトを生成し、その中からaというプロパティの値を読み出しています。

オブジェクトのプロパティに値を代入する方法

上記の例とは逆にオブジェクトのプロパティに値を代入する方法は次のように書きます。

//上記で生成したオブジェクトを使用
test.a = 10;

//.で読み出す
console.log(test.a);

test["b"] = 20
//[]で読み出す
console.log(test["b"]);

オブジェクトの生成

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

概要

javascript内でオブジェクトを生成する方法を試してみます。

オブジェクトの生成の方法は、次のとおりです。

①オブジェクトリテラルを使って生成
②newキーワードを使って生成
③Object.create()を使って生成

③の方法はECMAScript5の言語仕様に則ってプログラミングする場合に有効です。

オブジェクトリテラル

オブジェクトリテラルを使った生成を試してみます。
変数を宣言し、その変数に対して、新規のオブジェクトを生成し、代入します。

オブジェクトリテラルは、中括弧を使い、内部には「プロパティ名」と「値」をペアでカンマ区切りで記述します。
プロパティ名と値はコロンで区切ります。

//オブジェクトリテラルによる生成

//空のオブジェクトを生成する
var testobject = {};
console.log(testobject); //出力結果「Object {  }」

//要素を2つ持つオブジェクトを生成する
var testobject2 = {a:10, b:20};
console.log(testobject2); //出力結果「Object { a: 10, b: 20 }」

//オブジェクト内の要素が他のオブジェクトのデータを扱う場合
var testobject3 = {c:testobject2.a, d:testobject2.b};
console.log(testobject3); //出力結果「Object { c: 10, d: 20 }」

リテラルを使ったオブジェクトで、プロパティ名に予約語を使う場合は注意が必要です。
ECMAScriptのバージョンによって挙動が変わるので、
各バージョンにより、挙動を確かめてプログラムする必要があります。
一般的にはプロパティ名には予約語ではない文字列を割り当てたほうがプログラミングしやすいかもしれないです。

newキーワードを使った生成

new演算子を使ってオブジェクトを生成します。
new演算子のあとに関数呼び出しを記述して、オブジェクトを生成します。
オブジェクトが生成されるときに必ず実行される関数をコンストラクタと呼び、内部で初期化等の動作をします(各関数によって動作が違うので、詳しくはリファレンスを調べる必要があります)

実際の動きについて、簡単なコードを書いて出力結果を確認してみます。

//newキーワードで生成

//空のオブジェクト生成
var testobj1 = new Object();
console.log(testobj1); //出力結果「Object {  }」

//空の配列オブジェクトを生成
var testobj2 = new Array();
console.log(testobj2); //出力結果「Array []」

//日時を扱うオブジェクトを生成
var testobj3 = new Date();
console.log(testobj3); //出力結果「Date 2018-11-14T12:30:02.403Z」