関数

概要

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

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

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

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

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

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

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

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

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

概要

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

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

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

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

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

ES6の配列メソッド

概要

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

forEach

forEachは前のブログ投稿にも出てきました。
次のようなコードを書きます。

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

配列test1に対して、全ての要素を加算し、最終的な合計値を出力しています。

出力結果

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

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の多次元配列

概要

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

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

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

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

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

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

//要素の全てを回して処理
for (var 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次元以上の配列も応用次第で活用することができます。

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

概要

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

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の関数型プログラムに発展することができ、それはまた別な投稿で取り上げたいと思います。

要素の追加と削除

概要

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

要素の追加

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

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プロパティについて

概要

配列の長さについて試してみます。
配列の長さは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から埋まっていない配列

概要

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

特徴

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

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

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

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

概要

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」が配列に格納されている動きになります。

配列について

概要

これまでに何度もサンプルコードを書いてきましたが、
改めて配列の生成について、確かめてみます。

配列の生成方法(その1)

配列の生成がどのようにされるかを試してみるために、実際に次のようなコードを書いてみます。

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

//要素が5つの配列
let test2 = [1, 10, 20, 30];

//要素の型が異なる配列
let test3 = [2, "20", 5, "v"];

//要素に式を使った配列
let val = 10;
let test4 = [val, val+5, val+10];

//オブジェクトリテラルを用いた配列
let test5 = {
	a: '10',
	b: '20',
	c: '20'
}

//配列リテラルで定義
let test6 = [10, , 15];

console.log(test1);
console.log(test2);
console.log(test3);
console.log(test4);
console.log(test5);
console.log(test6);

上記のコードの実行結果は次のようになります(ブラウザの開発者ツールで確認)。

Array []
Array(4) [ 1, 10, 20, 30 ]
Array(4) [ 2, "20", 5, "v" ]
Array(3) [ 10, 15, 20 ]
Object { a: "10", b: "20", c: "20" }
Array(3) [ 10, <1 empty slot>, 15 ]

また、Arrayという箇所については、内部を展開して確認できるようになっているので、
その部分を展開すると、次のようになります。

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


(4) […]
0: 1
1: 10
2: 20
3: 30
length: 4
<prototype>: Array []


(4) […]
0: 2
1: "20"
2: 5
3: "v"
length: 4
<prototype>: Array []

(3) […]
0: 10
1: 15
2: 20
length: 3
<prototype>: Array []


Object { a: "10", b: "20", c: "20" }


(3) […]
0: 10
2: 15
length: 3
<prototype>: Array []

上記の出力結果の中の「<prototype>: Array []」という箇所も、さらに展開できるので、ひとつの「<prototype>: Array []」を開いてみると次のように出力されます。

これは、配列オブジェクトに対してのprototypeオブジェクトのプロパティが出力されています。

<prototype>: []
concat: function concat()
constructor: function Array()
copyWithin: function copyWithin()
entries: function entries()
every: function every()
fill: function fill()
filter: function filter()
find: function find()
findIndex: function findIndex()
flat: function flat()
flatMap: function flatMap()
forEach: function forEach()
includes: function includes()
indexOf: function indexOf()
join: function join()
keys: function keys()
lastIndexOf: function lastIndexOf()
length: 0
map: function map()
pop: function pop()
push: function push()
reduce: function reduce()
reduceRight: function reduceRight()
reverse: function reverse()
shift: function shift()
slice: function slice()
some: function some()
sort: function sort()
splice: function splice()
toLocaleString: function toLocaleString()
toSource: function toSource()
toString: function toString()
unshift: function unshift()
values: function values()
Symbol(Symbol.iterator): function values()
Symbol(Symbol.unscopables): Object { copyWithin: true, entries: true, fill: true, … }
<prototype>: {…}
__defineGetter__: function __defineGetter__()
__defineSetter__: function __defineSetter__()
__lookupGetter__: function __lookupGetter__()
__lookupSetter__: function __lookupSetter__()
__proto__: 
constructor: function Object()
hasOwnProperty: function hasOwnProperty()
isPrototypeOf: function isPrototypeOf()
propertyIsEnumerable: function propertyIsEnumerable()
toLocaleString: function toLocaleString()
toSource: function toSource()
toString: function toString()
valueOf: function valueOf()
<get __proto__()>: function __proto__()
<set __proto__()>: __proto__()

配列の生成方法(その2)

上記の方法とは別にArray()コンストラクタを使って配列を生成する方法もあります。

その場合は次のようなコードを書きます。

let test7 = new Array();

上記のtest7をコンソールログに出力してみると、次のようになります。

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

当然のように内容が空(lengthが0)の空配列が生成されます。

生成時に予め要素数を指定する場合は次のように書きます。

let test8 = new Array(5);

上記の場合は、配列生成時に5つの要素を持つ配列を作ります。

また、要素数と、要素の値を指定して配列を生成する場合は次のように書きます。

let test9 = new Array(5, 10, 20, "abc", "def");

配列を配列リテラル(方法1)で生成する場合と、Array()コンストラクタを使って生成する場合と、それぞれ書き方が異なりますが、
どちらも配列を生成することができるので、要素数や要素内の値の有無、プログラム時の状況に合わせて生成方法を決めることがよいと思われます。

配列

概要

javascriptの配列についてまとめます。

これまでもサンプルコードで配列を多く書いてきましたが、
javascript特有の配列についての特徴をまとめてみます。

配列の特徴

・javascriptの配列には型という概念がない
・配列の要素については、どの型の値も格納できる
・配列の要素に、配列を格納することもできる
・配列の要素に、オブジェクも格納することができる
・配列インデックスは0から
・インデックスは32ビットの整数値
・最大インデックス値は4294967295
・要素数は動的に変化する
・インデックス値は要素に対して飛び飛びでも問題ない

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

概要

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

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

toString()

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

toLocaleString()

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

valueOf()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

概要

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

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

オブジェクトからクラス属性を取得するには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]」という文字列が必ず出力されており、その後にオブジェクトのクラス属性が出力されます。

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

概要

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

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

プロトタイプ属性

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

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

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

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

概要

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

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

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

データプロパティの属性は、値も属性のひとつとみなし、書き込み可能、列挙可能、再定義可能、の合計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にしている為、値の代入を行っても書き変わらない挙動になります。

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

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

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

概要

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倍にして返しています。

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

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

概要

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で回した値を参照するタイミングで「オブジェクト[プロパティ]」という記述することで内容を取得することが可能になります。 上記の例は必ずしもこう書かないといけないわけではないので、参考例として書いています。 また、オブジェクトが継承されているケースもあるので、プログラムの実装時には挙動に注意が必要です。


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

概要

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を返します。

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

概要

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

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

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

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

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

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

概要

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

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

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

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

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

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

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

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

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

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

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

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

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

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

概要

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

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

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

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

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

連想配列としてのオブジェクトとして考えると、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について

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

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

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

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

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"]);

オブジェクトの生成

Object.create()について

javascriptでは新しいオブジェクトを生成するタイミングでメソッドが定義される仕組みがあります。
Object.create()メソッドがそれにあたります。
引数の指定の仕方によって、生成されるオブジェクトに対してのプロトタイプとしての扱いが変わるので、注意が必要です。

Object.create()の挙動について

Object.create()はプロトタイプオブジェクトを引数にして呼び出します。
nullを渡すとプロトタイプを持たないオブジェクトを生成します。
空のオブジェクトを生成する場合は、Object.prototypeを引数にします。

プロトタイプを持つオブジェクト生成

オブジェクトを生成するタイミングで、任意のプロトタイプを生成することができます。

プロトタイプ

概要

javascriptにはプロトタイプという概念があります。
このプロトタイプについて、調べてみます。

プロトタイプは、全てのオブジェクトに関連つけられているオブジェクという考え方ができます。

ここで言う全てのオブジェクトというのは、javascriptが標準で利用できるオブジェクトで、それらのオブジェクトに対して、プロトタイプと関連づいていると言えます。

オブジェクトをnewする

オブジェクトをnewするタイミングで、プロトタイプオブジェクトを持っている状態になります。
newで生成されたオブジェクトは、Object.prototypeを継承しています。
新規に生成されるオブジェクトの種類によって、継承するプロトタイプも異なってきます。
例えば、

new Array()

で生成するオブジェクトに対しては、Array.prototypeの値(コンストラクタも)を継承します。

プロトタイプチェーンについて

上記のnew Array()の場合、オブジェクトを生成したタイミングで、Array.prototypeとObject.prototypeの両方を継承します。
この両方を継承したオブジェクトの形をプロトタイプチェーンと呼び、両方のプロパティとメソッドが利用可能になる特徴があります。

オブジェクトの生成

概要

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」

オブジェクト

概要

javascriptのオブジェクトについての概要です。

・オブジェクトは複合型のデータ型になります。
・複数のデータをまとめて保持し、読み書きが可能です。
・データをプロパティとして呼ぶこともある。
・プロパティは原則、文字列として指定する。
・各プロパティ(文字列)に対し、値をマッピングする。
・マッピングしたものには呼び名がつけられる(ハッシュ、連想配列等)

・オブジェクトは、文字列、数値、真偽値(true/false)、null、undefined以外の値になる。
・オブジェクトは可変の振る舞いをする。(値を参照で操作する)

・オブジェクトの操作は、生成、プロパティへの値の代入、読み出し、削除など。
・同名のプロパティはNG。
・プロパティは「名前と値」の組み合わせ。その他「プロパティ属性」という値を持つ。
・プロパティ属性は「書き込み可」「列挙可」「再定義可」がある。

・オブジェクトはオブジェクト属性を持つ。「プロトタイプ」「クラス」「拡張可フラグ」
・オブジェクトは大きく3種類あり、「ネイティブオブジェクト」「ホストオブジェクト」「ユーザ定義オブジェクト」
・プロパティは大きく2種類あり、「独自プロパティ」「継承プロパティ」

javascriptのオブジェクトの特徴

オブジェクト自身のプロパティの他、ほかのオブジェクトのプロパティも「継承」することができる。
継承の対象となるオブジェクトをプロトタイプと呼びます。

オブジェクトの詳細な挙動について

javascriptのプログラムをより深く正確にする為には、
オブジェクトについての理解を深める必要があります。

オブジェクトの詳細な挙動は、一つ一つこのブログで掘り下げて、できるだけサンプルプログラムを書いて、挙動を確かめながら記事化していきます。

文についてまとめ

概要

javascriptの文についてまとめます。

文はまず、式文と宣言文があり、式文は何らかの処理を書くことで作用します。
宣言文は変数や関数を宣言し定義するための文です。

また、実行順序を変更したり制御する為の制御文もあります。
制御文には以下のようなものがあります。
・条件文
if
swich
・ループ文
for
while
・ジャンプ文
break
return
throw

式文

もっとも簡単な文で、変数の代入や、インクリメント、デクリメント、オブジェクトのプロパティ削除のdeleteなども式文として使われます。

複合文

複合文とは文ブロック

{

}

で囲んで記述される文です。
文ブロックは末尾にセミコロンを記述しません。

宣言文

var
function
等の宣言する文が宣言文です。
変数の宣言と関数の宣言を行います。

var

変数を明示的に宣言する文です。

function

関数を明示的に宣言する文です。
javascriptの関数は、次のように宣言する方法

function test() {

}

や、次のように関数を変数に代入する式もあります。

var ftest = function test() {

}

//呼び出す時
ftest();

条件文

指定された式の値に応じて、他の文を実行したり、処理を分岐したりします。
条件文は「if文」や「if else」、「switch文」などあります。

if文

javascriptが条件を判定して、処理を分岐する文です。

if else文

if文と組み合わせて書きます。
これまで何度もサンプルコードを書いてきましたので、詳しい内容は割愛します。

switch文

ある一つの判定要素に対し、条件分岐が多数ある場合に用いる書き方です。
簡単なサンプルを書きます。

var test = 10;

swicth(test) {
	case 1:
		//処理A
		break;
	case 5:
		//処理B
		break;
	case 10:
		//処理C
		break;
	case 15:
		//処理D
		break;
	default:
		//処理E
		break;
}

上記の場合、変数testの値を判定し、処理Cが実行されます。
処理後のbreakを書かなければ、そのまま次の処理(上記の場合は処理D)が実行されます。

if else文のelseを連続的に書くよりは、この書き方のほうが処理のみとおしが立ちやすくなります。

ループ文 – while

とある条件が真の間、処理を繰り返すループです。

ループ文 – do while

とある条件が真の間、処理を繰り返すループです。
while文との違いは、最初の1回目は条件の真偽に関係なく、必ず実行されます。

ループ文 – for

カウンタ変数を持ち、そのカウンタの条件が真の間は処理を繰り返すループです。

ループ文 – for inf

オブジェクトに対して、オブジェクトのプロパティを巡回する時に書きます。

for (var test in testobject) {
	console.log(testobject[test]);
}

のように書きます。
オブジェクトについて深く理解する必要があるので、オブジェクトとプロパティについては、別途堀さげて書きます。

ジャンプ文

ループの処理中に、条件や状態により、処理をジャンプする場合に書きます。
breakやcontinueを使います。

ラベル文

先頭に識別子を書き、その後にコロンを書き、最後に文を記述します。

testlabel:while(1) {
	//ループ内の処理
	
	//ラベルを利用して、ループ処理を実行
	continue testlabel;
}

上記の例はラベル「testlabel」を宣言し、その後のwhileループを割り当てています。
ループ処理内のtestlabelの文が実行されると、ラベルしたwhileが実行されます。
上記のサンプルは極端な例ですが、ラベルの行でジャンプするイメージになります。

break文

ループ処理中に、処理をとめてループを抜ける時に書きます。

continue文

ループ処理中に、continue文より後の処理は実行せず、またループの最初から処理を開始する時に書きます。
一般的には条件文の後に、指定条件の場合にcontinue文を書くケースが多いです。

return文

関数内の処理が完了し、関数を抜けて結果を返す処理として書きます。

throw文

ある例外的な条件やエラーが発生したことを示すシグナルのことです。
次のようにループの処理中に、例外を強制発動することができます。

for (var i=0; i<10; i++) {
	//例外が発生した場合にスローする
	throw new Error("Err");
}

try catch finally文

java言語とよく似ていますが、プログラム中の処理を試み、
例外が発生した場合は、処理をcatchし、全ての処理後にfinallyを実行する文です。

with文

実用性は低いですが、オブジェクトが入れ子になっている場合や、
多数のオブジェクトのプロパティに何度もアクセスする場合に使います。

use strict文

ECMAScript5で導入された指示子です。
この指示子が書かれたプログラムは、厳密なコードであることが要求され、
プログラムもまた厳密に書きます。(とは言っても常に正確に記述することは基本です)

カンマ演算子

概要

一般的にプログラムを書いていると、よく使用するカンマですが、
javascriptではカンマも演算子と解釈するケースがあります。

セパレータとしてのカンマと、演算子としてのカンマがあります。

また、C言語やC++でもカンマ演算子はあります。
言語のできた順からするとCやC++のほうが先になります。
(C言語は1972年頃、C++は1983年頃、javascriptは1995年頃)

カンマ演算子の挙動

カンマ演算子は、左被演算子を評価して、その値を捨て、右被演算子を評価する動きになります。

演算子の実行例

実際にこの演算子について、コーディングして動作を確かめてみます。

//セパレータとしてのカンマ(変数の宣言なので、カンマ演算子では無い)
var x = 10, y = 20, z = 30;

//カンマ演算子としてのカンマ
var ret = (x, y, z);
console.log(ret); //出力結果「30」カンマ区切りの最後の文字列が代入される


//代入処理がされる場合
var ret = x, y, z;
console.log(ret); //出力結果「10」カンマ区切りの最初の文字列が代入される


//加算演算子を含む場合
var ret = (x += 2, y +=3, z += 4);
console.log(ret); //出力結果「34」


//加算演算子を含む場合(括弧をつけない場合)
var ret = x += 2, y +=3, z += 4;
//出力結果は「SyntaxError: unexpected token: '+='」
//javascriptでは、上記の書き方はエラーになる


//インクリメント
var x = 10, y = 20, z = 30;
x++, x++;
console.log(x); //出力結果「12」


//カンマ演算子を二項演算子の左辺に下記、値を代入する
(x, y) = 40;
//出力結果「ReferenceError: invalid assignment left-hand side」
//javascritpの言語仕様としてエラーになる

上記のような結果になります。
「左被演算子を評価して、その値を捨て、右被演算子を評価する」と一言で言っても、括弧の有無で挙動が変わる点に注意が必要です。

void演算子

概要

あまり見慣れないvoid演算子についてまとめます。

void演算子は単項演算子で、オペランド値を廃棄し未定義値を返す動作をします。

実際の使用場面

void演算子は特殊な利用用途以外では、あまりプログラムしません。

働きとして、戻り値がundefineにならなけれならない場合に使用することが多いです。

typeof演算子

概要

typeof演算子についてまとめます。

オペランドは1つのみで演算します。

オペランドの「データ型」(文字列)を返します。

typeof演算子で判定できる値について

typeof演算子は、以下の値を演算(判定)することができます。

undefined
null
true/false
数値
NaN
文字列
関数
ネイティブオブジェクト
ホストオブジェクト

条件演算子

概要

条件演算子は「?:」で記述する演算子です。
一般的にこれは三項演算子と呼ばれ、オペランドを3つ使い演算を行います。
先頭のオペランドの後に「?」を記述し、その後、2つ目のオペランドを記述し、その後「:」に続けて3つ目のオペランドを記述します。

書き方としては、次のようになります。

判定条件 ? 結果1 : 結果2

判定条件がtrueの場合は結果1のオペランドの値を評価し、falseの場合は結果2のオペランドの値を評価します。

三項演算子については、他の言語にもあり、開発時の環境やプログラマによっても意見が分かれるケースがあります。

eval()

概要

eval()について、詳しく調べてみます。

eval()は1つの引数を渡します。
引数として文字列を渡し、それ以外の場合はそのままの値を返します。

引数が解釈できない場合はシンタックスエラー例外になります。
解釈できた場合は、引数を評価します。

実行例1

eval()を呼び出すとき、呼び出したコードの環境変数が使われます。

実際にeval()で呼び出されたコードの変数が使われるかどうか、
例えば、次のようなコードを書いてみます。

var x = "123";

console.log(eval("x"));

出力結果は

123

になり、eval()で「x」という変数を呼び出したことになります。

実行例2

eval()の引数内で変数定義や関数定義も可能です。
また、定義した変数に値を代入することもできます。
例えば、次のようなコードを書いてみます。

var y = "abc";

console.log(eval("y='d'"));

出力結果は

d

という結果になります。
出力結果を確認する為に、console.logを行っていますが、便宜上書いているだけです。

実行例3

eval()での関数定義を試してみます。

eval("function abc() { return 'z'; }");

console.log(abc());

出力結果は

z

という結果になります。

評価式(eval)

概要

javascriptにはソースコード文字列を評価、実行する機能「eval」があります。
このevalはグローバル関数として定義されており、eval()という形で値を評価します。

evalは()内にはいる文字列を評価します。

eval(string)

stringには文字列が入りますが、文字列をそのまま評価するのではなく、動的に評価されます。

簡単に次の例を書いてみます。

console.log(eval("10 + 15"));

出力結果は「25」という結果になります。

では、次のような書き方をするとどうなるか試します。

var test = 30;

console.log(eval(test  + 15));

出力結果は「45」になります。
このように、引数として文字列を渡す場合でも、数値(の式)を渡す場合でも、評価時にjavascriptのコードとして実行される機能をもつことが特徴です。

算術演算と代入演算子

概要

javascriptは算術演算と代入演算子を組み合わせて使用することができます。
例えば二つのオペランドを「+=」という書き方を使って右辺のオペランドを左辺に代入する時、値を加算して代入することが可能です。

サンプル

次のような例を書いてみて動きを確かめます。

var test = 10;

test += 20;

console.log("test -> " + test);

非常に簡単な例ですが、出力結果は「30」と出力されます。
この時に気をつけなければならないのは、上記の例の場合は2つのオペランドが数値であることです。

では、この数値が文字列だった場合、どのような動きになるか試してみます。

var test = '10';

test += '20';

console.log("test -> " + test);

先ほどの例とほとんど同様のコードですが、出力結果は「1020」となります。
代入するタイミングの算術演算子(加算演算子)が、文字列同士を結合する働きをした為です。

代入演算子の種類

代入演算子には、上記の加算演算子の他に、次のような種類があります。

//加算代入演算子
+=

//減算代入演算子
-=

//乗算代入演算子
*=

//除算代入演算子
/=

//剰余代入演算子
%=

//左シフト代入演算子
<<=

//右シフト代入演算子(符号有り)
>>=

//0埋め右シフト代入演算子(符号無し)
>>>=

//ビットごとのAND代入演算子
&=

//ビットごとのOR代入演算子
|=

//ビットごとのXOR代入演算子
^=

それぞれの代入演算子に記載している「=」の前の演算子の性質を持ち、その演算結果を左にあるオペランドに代入する。という働きになります。

代入演算子

概要

javascriptの代入演算子についてです。
これは読んで字のごとく、変数に値を代入する為の演算子で、
これまでのサンプルで当たり前のように使ってきました。

動作について

代入演算子は「=」と記載し、左のオペランドに対し、右のオペランドを代入します。
左側のオペランドは変数、オブジェクトのプロパティです。
右側のオペランドは任意の値(型も任意)です。

代入時に複数の変数に代入することも可能です。

var test;
var test2;

test = test2 = 10;

上記のようにコードを書くと、変数testとtest2にそれぞれ10が入ります。

論理否定演算子

概要

論理否定演算子は

!

で表す単項演算子になります。
一つのオペランドに対して演算を行います。
論理否定演算子が返す値はtrueかfalseのどちらかになります。
通常はif文などの条件式について、条件を反転させた結果を判定する場合に使われます。

論理和演算子

概要

論理和演算子は、2つのオペンランドのうち、一方または両方がtrueになる場合に式全体でtrueを返す演算子です。

式の評価順について

論理和演算子の式の評価は、左側にあるオペランドを評価し、その結果がtrueの場合には右側のオペランドは評価しません。

この性質を利用して関数などの引数のデフォルト値の設定等に使われる場合があります。

function f (a)
{
    //引数aがnullの場合、変数tに1を代入する(デフォルトとして使用)
    var t = a || 1;
}

ただし上記の例の場合、引数aの値の取り扱い方については、意図しない挙動のプログラムになるので、注意が必要です。
javascriptの値として、文字列、数値の他、引数がnull、undefined、NaN等の場合に注意してプログラムする必要があります。

論理演算子 – 論理積演算子

概要

javascriptの論理演算子についてまとめます。

まずは論理積演算子についてです。

論理積演算子は条件式で「&&」と記述して判定します。
条件式の中で2つのオペランドを記述し、それぞれのオペランドがtrueになる場合に式全体でtrueを返します。

また、どちらかのオペランドがfalseになる場合は、他方がtrueでも式全体ではfalseを返します。

実例

簡単な例を書きます。

var testa = 10;
var testb = 20;

//条件式1
if (testa == 10 && testb == 20) {
    console.log(true);
} else {
    console.log(false);
}
//結果、trueと出力される

var testc = 30;
var testd = 40;

//条件式1
if (testc == 10 && testd == 40) {
    console.log(true);
} else {
    console.log(false);
}
//結果、falseと出力される

上記の場合、条件式1のほうは、if文の中の条件式が2つのオペランドともtrueになるので、式全体でtrueを返します。
また、条件式2のほうは、最初のオペランドがfalseになる為、式全体でもfalseを返します。

javascriptは全ての値に対してtrue/falseの評価をすることができます。

falseに評価されるものはnull、undefined、0、-0、NaN、””(空白文字)等です。

式の評価順について

この論理積演算子を考える時、オペランドの左側から判定されています。
この動きをもう少し詳しく調べると、まず左側のオペランドが判定されて、次に右側のオペランドが判定されます。

左側のオペランドがfalseの場合は、式全体としてfalseをすぐに返し、右側のオペランドは評価されない動きをします。

instanceof演算子

概要

instanceof演算子について調べます。
instanceof演算子はオブジェクトがクラスのインスタンスかどうかを判定します。
インスタンスの場合はtrueを返します。

比較する2つのオペランドは、左側はオブジェクト、右側はクラスを指定しなければならないです。

左側のオペランドがオブジェクトではない場合はfalseを返します。
右側のオペランドがクラス(関数)ではない場合は例外が発生します。

javascriptのプロトタイプチェーンという考え方が重要になってきますが、それについては別に調べて記事化します。

in演算子

概要

javascriptのプログラムではあまり見かけない演算子ですが、in演算子についてまとめます。

in演算子は左側の値が右側のオブジェクトのプロパティであればtrueを返します。

演算子の使用上のルール

この演算子を使うには、下記のようなルールがあります。
・左側のオペランドは文字列
・右側のオペランドはオブジェクト

実例1

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

//オブジェクト
var testobject = {
	aaa: "test1",
	bbb: "test2",
	ccc: true
};

console.log("aaa" in testobject);
console.log("bbb" in testobject);
console.log("ddd" in testobject);

この場合の結果は、

true
true
false

になります。
オブジェクト「testobject 」に対し、aaaとbbbはプロパティとして存在しますが、dddは存在しない為、falseになります。

実例2

では、オブジェクト中身を変えて、次のような例を試してみます。

//オブジェクト
var testobject2 = [
	10,
	20,
	30
];

console.log("aaa" in testobject2);
console.log(10 in testobject2);
console.log("10" in testobject2);
console.log("11" in testobject2);

この場合の結果は、

false
false
false
false

になりました。
一見すると2つ目の演算結果はtrueになりそうですが、正確には「プロパティが存在するかどうか」が判定基準になるので、全てfalseになりました。

実例3

では、次のような場合はどうなるか、試してみます。

var testobject3 = [10, 11, 12];

console.log("0" in testobject3);
console.log(1 in testobject3);
console.log(3 in testobject3);

この場合の結果は、

true
true
false

になります。
オブジェクトtestobject3は要素の内容だけが書かれていますが、プロパティは明示的に書いていないものの、配列のN番目という判定がされています。
また、2番目の「1」のin演算子については、判定時に文字列の「”1″」として変換されて演算がされるので注意が必要です。

比較演算子

概要

比較演算子について、調べます。
他のプログラム言語と同様に、比較演算子は2つのオペランドの相対的な順序を調べます。
次に4つの演算子である「小なり演算子」「大なり演算子」「小なりイコール演算子」「大なりイコール演算子」について、実際にコードを書いて調べてみます。

<演算子

1番目のオペランドが2番目のオペランドより小さい場合にtrueを返す。

var test1 = 10;
var test2 = 20;
if (test1 < test2) {
    console.log("true");
}
//出力結果 true

>演算子

1番目のオペランドが2番目のオペランドより大きい場合にtrueを返す。

var test1 = 30;
var test2 = 20;
if (test1 > test2) {
    console.log("true");
}
//出力結果 true

<=演算子

1番目のオペランドが2番目のオペランドより大きい、または等しい場合にtrueを返す。

var test1 = 40;
var test2 = 30;
if (test1 > test2) {
    console.log("true");
}

var test1 = 30;
var test2 = 30;
if (test1 > test2) {
    console.log("true");
}
//両方とも、出力結果 true

>=演算子

1番目のオペランドが2番目のオペランドより小さい、または等しい場合にtrueを返す。

var test1 = 20;
var test2 = 30;
if (test1 > test2) {
    console.log("true");
}

var test1 = 30;
var test2 = 30;
if (test1 > test2) {
    console.log("true");
}
//両方とも、出力結果 true

型の変換について

比較するタイミングでオペランドの型が違う場合は、型の変換が行われてから変換されます。

変換時のルールは次のようになります。

・オペランドがオブジェクトの場合
基本型に変換される。

・基本型に変換後
両方のオペランドが文字列の場合、この2つの文字列をアルファベット順で比較します。

どちらか片方のオペランドが文字列ではない場合、2つのオペランドは数値に変換されて比較されます。

Infinityは他の数値よりも大きいと判断される。
-Infinityは他の数値よりも大きいと判断される。

NaNの場合は、比較演算子は常にfalseを返します。

等値演算子と同値演算子

等値演算子についてまとめます。

2つのオペランドを比較し、等しい場合はtrue、等しくない場合はfalseを返します。
等値演算子は同様の演算子として、同値演算子があります。

それぞれの特徴を以下にまとめます。

■等値演算子
・オペランドを比較する際、型変換が行われ値が比較される
・コードは「==」という記述になる
・比較の際、反対の結果を得るには「!=」というコードになる
・オペランドが同じ型の場合は、値が同一かを判定し、等しければtrueを返す
・型が異なる場合は同じ型に変換してから判定する
・どちらか一方がnullの場合、等しいと判定する
・どちらか一方がundefinedの場合、等しいと判定する
・どちらか一方がtrueの場合、1に変換してから比較される
・どちらか一方がfalseの場合、1に変換してから比較される
・どちらか一方がオブジェクトの場合、オブジェクトを基本型に変換して比較される

■同値演算子
・厳密に型まだ考慮して比較される
・型が違う場合は等しくない(false)が返る
・コードは「===」という記述になる
・比較の際、反対の結果を得るには「!==」というコードになる
・オペランドがnull同士の場合、等しいと判定する
・オペランドがundefinedの場合、等しいと判定する
・両方の値が論理値(true)か論理値(false)であれば等しいと判定する
・NaNがオペランドのどちらかにある場合は等しくないと判定する
・NaNがオペランドの両方ある場合も等しくないと判定する
・両方の値が数値の場合で、同じ値である場合は等しいと判定する
・両方の値が数値の場合で、片方が0の場合も等しいと判定する
・両方の値が同じオブジェクトや配列または関数の場合、等しいと判定する
・異なるオブジェクトを参照している場合は等しくないと判定する

関係演算子

関係演算子とは、2つの値の関係を判定し、その判定結果によって真偽(trueまたはfalse)を返します。
関係演算子の返す値は論理値になります。

関係演算子の種類は「等値演算子」「不等演算子」「同値演算子」「比較演算子」「in演算子」「instanceof演算子」等があります。

詳しくは一つ一つブログ記事化していきます。

ビット演算子

概要

ビット演算子についてのまとめです。

  • javascriptのプログラムではあまり登場しない
  • 2進表記のビットを操作する
  • オペランドとして整数値が必要
  • 32ビット整数表現形式で処理を行う
  • ビット演算子の種類

    ビットを操作する演算子の種類と処理内容です。
    ビット積演算子:整数引数の各ビット単位で論理積を計算する。
    ビット和演算子:整数引数の各ビット単位で論理和を計算する。
    ビット排他的論理和演算子:各ビット排他的論理和を計算する。二つのオペランドのどちらか一報で対応するビットが1の時、結果ビットが1になる。
    ビット否定演算子:単項演算子。オペランドは整数引数が1つのみ。オペランドの全てのビットを反転させる。(符号を変えて1を減算した値)

    シフト演算子

    左シフト演算子:前のオペランドの全てのビットを、後ろのオペランドに指定されただけ左側に移動させる。範囲は0~31までの整数とする。
    符号尽き右シフト演算子:前のオペランドの全てのビットを、後ろのオペランドに指定された数だけ右側に移動させる。範囲は0~31までの整数とする。
    右側からはみ出たビットは捨てられる。
    符号なし右シフト演算子:符号尽き右シフト演算子と同じ動きになるが、左端に入る値が常に0になる。

    単項算術演算子

    概要

    単項算術演算子は、一つのオペランドに対して処理を行う。

    具体的には「+」「-」「++」(インクリメント)「–」(デクリメント)等があります。
    全て右結合性があり、演算の優先順位が高く処理されます。

    単項+

    オペランドを数値に変換し、変換した値を返します。

    単項-

    オペランドを数値に変換します。
    変換結果の符合を反転します。

    インクリメント++

    オペランドをインクリメントします。
    オペランドは「変数」「配列の要素」「オブジェクトプロパティ」等の左辺値である必要があります。

    また、他の言語と同様に「前置」と「後置」により処理が変わります。

    前置の場合は、オペランドをインクリメントし、インクリメント後の値が評価結果になります。
    後置の場合は、オペランドをインクリメントし、インクリメント前の評価結果になります。

    デクリメント++

    オペランドとして左辺値をとします。
    オペランドを数値に変換し、数値から1を減算します。
    インクリメントと同様に前置と後置による処理の違いがあります。

    +演算子

    前回までの演算子で、四則演算の中で「+演算子」だけはオペランドによって動きが変わるという説明を書きました。

    +演算子は、オペランドが数値の場合は加算し、文字列の場合は連結を行います。

    簡単な例として

    var test = 10 + 20;
    console.log(test);
    

    上記の場合は、結果が30と出力され、

    var test = "10" + "20";
    console.log(test);
    

    上記の場合は、1020と出力されます。

    上記の場合の他に、片方のオペランドが文字列で、もう片方のオペランドが数値の場合は、数値のオペランドが文字列に型変換できるオペランドの場合は文字列に変換されて連結されます。

    オペランドのどちらかがオブジェクトの場合、基本型値への変換アルゴリズムが使われ、基本型値に変換されます。

    基本型へ変換された後、片方のオペランドが文字列の場合は、もう片方のオペランドも文字列へ変換され、連結されます。

    それ以外の場合は、両方のオペランドを数値に変換して加算処理されます。

    1 7