単項算術演算子

概要

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

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

単項+

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

単項-

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

インクリメント++

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

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

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

デクリメント++

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

+演算子

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

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

簡単な例として

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

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

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

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

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

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

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

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

算術演算子

javascriptの算術演算子についてまとめます。
基本的な算術演算子は、乗算、除算、剰余、加算、減算です。
その他は単項演算子、ビット演算子があります。

乗算、除算、剰余、加算、減算の中で、加算演算子の「+」は動作が他の演算子とことなります。
演算時のオペランドの型が数値の場合は加算され、文字列の場合は結合されます。

その他の乗算、除算、剰余、減算については、オペランドを数値として変換し演算を行います。
オペランドが数値に変換できない場合に「NaN値」に変換され、演算結果がNaNになります。

除算演算子はオペランド同士を割ります。

オペランドA / オペランドB

の場合はオペランドAをオペランドBで割ります。
このときに結果は浮動小数点数になります。

オペランドBが、0の場合、エラーにはならず結果は無限大になります。
ブラウザのコンソールで試してみると

Infinity

という結果になります。

剰余演算子の場合は、除算演算子と近いですが、演算結果はオペランドAをオペランドBで割った余りが出力されます。
例えば次の例の場合

var test = 100;
console.log(test % 30);

出力結果は「10」になります。
オペランドが浮動小数点数の場合でも、結果は小数点を含む演算結果になっります。

演算子の結合性

概要

javacriptの演算子には結合性という重要な考え方があるので、
結合性について試してみます。

演算子には結合の方向があり、左から右に演算を結合する場合と、
反対に右から左に結合する場合あります。

また、同じ優先順位の演算子の場合は、実行順を決定する場合に結合性の順で演算がされます。

例えば減算する演算子「-」の場合、値が3つある場合を試してみます。

var test;
test = 100 - 20 - 10;
console.log(test);

上記の結果は、まず100-20が演算され、その後で80-10が演算されます。
これは演算子の結合性が「左から右に結合している」という仕様に則っている為です。

演算子には結合性の仕様により演算される方向があることに注意が必要です。

評価順序

演算子の評価順については、常に左から右に行われます。
例えば式「test = 100 – 20 – 10;」がある場合、まず評価されるのはtestというオペランドで、
次に100、20、10の順序で評価されていきます。

上記の例は固定の数値ですが、100、20、10の各オペランドが変数や関数の場合は、左から順に右に評価されていきます。

上記の式の場合、右辺に対して括弧をつけることで、演算の順序を変更することは可能です。
演算の順を変えることができても、評価の順は変わらないことに注意が必要です。

演算子の優先順位

javascriptの演算子は他のプログラム言語と同様、演算子に優先順位があります。

演算子の優先順位は優先度が高いものから演算され、優先度の低い演算子は最後に演算されます。

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

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

上記は結果が610になります。
演算子の優先順位が「+」よりも「*」のほうが高い為です。
これを調整するには「()」を使って優先順位を決めることができます。(括弧のほうが演算の優先順位が高くなる為です)

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

上記は結果が900になります。
括弧をつけることで計算式の括弧内が先に演算され、30 * 30が後から演算される為です。

プロパティアクセスと呼び出し式は、全ての演算子より優先順位が高くなります。
演算子の優先順位をコントロールするには、括弧を使って意図した演算になるようにプログラムを書きます。

オペランドと型について

前回の投稿ではオペランドの数について書きました。
今回はオペランドの型についてまとめます。

オペランドは通常、特定の型を示していて、演算子もまた特定の型を返します。

javascriptの演算子はオペランドの型を解釈して演算します。
例えば

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

という書き方は、結果「1020」を返します。(ここでは数値として加算されないことを確認)

また、次のように書くと

var test = "50" * "60";
console.log(test);

結果は「3000」を返します。
同じ文字列のオペランド(の型)同士の演算でも、演算子によって型の解釈が決定づけられます。

では先ほどの例を変更して、次のように書いてみます。

var test = 30 + 40;
console.log(test);

結果は「70」になります。
ダブルクォートで囲んでいないオペランドの為、数値として解釈され、なおかつ演算子は加算演算子として解釈されました。
一番最初の例では、ダブルクォートで囲んだ場合は文字列として解釈されるので、演算子としては文字列連結として演算されたことになります。

このようにjavascriptには、オペランドの型によって働きが変わる演算子があります。

「<」演算子などはオペランドの型によって、数値の大小比較をしたり、アルファベット順で比較したり、解釈が動的に変化します。

演算子とオペランドについて

javascriptの演算子について、まとめます。
具体的な演算子の種類についてはリファレンスを確認することをおすすめします。

演算子はオペランドの数が重要になります。
オペランドとは、被演算子と呼ばれ、数式を構成する要素のうち、演算の対象となる値や変数、定数などを指します。

このオペランドの数が2つ必要な時、二項演算子と呼びます。
たとえば下記の加算演算子や乗算演算子の場合はオペランドが2つです。

console.log(10 + 10);
console.log(20 * 20);

二項演算子とは別に、単項演算子と呼ぶ演算子もあります。
一つのオペランドのみで構成され、演算がされるものです。
例えば下記のようなものです。

var test = 10;
console.log(++test);
console.log(-test);
console.log(!test);
console.log(typeof(test));
delete test;

また、二項演算子、単項演算子の他に三項演算子もあります。
これは様々なプログラムのソースコードを見ていると出てくる場合があります。
演算子だけで書くと「? :」ということになりますが、少しわかりづらいので、簡単な例を書きます。(便宜上、コンソールログに出力しています)

var test = 15;

//三項演算子
console.log(( test > 10 ) ? 'over' : 'under');

上記の場合はtestという変数の値を「?」の前の式で判定し、
真の場合は「:」の前のオペランド、
偽の場合は「:」の後のオペランドとして演算されます。

プログラマや開発プロジェクトによって好みが分かれる書き方ですが、1行でシンプルに書けるメリットがあります。

オブジェクト呼び出し式について

概要

オブジェクト生成式とは、javascript内で新規のオブジェクトを生成して
オブジェクトのプロパティを初期化します。

サンプルコード

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

new オブジェクト();

上記の「オブジェクト」と記載している箇所は生成したいオブジェクト名を書きます。

空のオブジェクトを生成してみます。

new Object();

これをコンソールログで出力すると、次のようになります。

console.log(new Object());

//出力結果
Object {  }

サンプルコード(その2)

もう一つ例を挙げて書いてみます。
例えばDateオブジェクトを生成する場合は、次のように書くこともできます。

new Date();

また、括弧を省いて書くことも可能です。

new Date;

こちらも同様にコンソールログに出力してみます。

console.log(new Date());

//上記の出力結果
Date 2018-08-17T16:16:52.405Z

console.log(new Date);

//上記の出力結果
Date 2018-08-17T16:16:52.405Z

どちらの書き方も同じオブジェクト生成がされている為、出力結果は同じになります。

オブジェクト生成時に生成したオブジェクトに関連するコンストラクタの挙動が重要になります。

コンストラクタについては別投稿として、改めて調べて書きます。

呼び出し式

概要

呼び出し式とは、あまり聞きなれない名前の式ですが、
javascript内で定義された関数を呼び出す時の式です。

具体的には次のようになります。

//Math.maxという関数を呼び出す
Math.max(10, 20, 30);

//自ら定義したsampleという関数を呼び出す
sample(100);

//テスト用配列を定義
var test = [200, 202, 201];
//配列に対しソート関数を呼び出す
test.sort();

上記の例のように関数を呼び出す際の式のことを呼び出し式と呼びます。。

呼び出し式の注意点

呼び出し式が評価される順番は、まず関数が評価され、次に引数式が評価されます。
関数式が呼び出し不可能な場合は例外になります。

呼び出し式の関数を自ら定義した場合は、return文を使うことで関数内部の値を返すことができます。
このreturn文で返された値が呼び出し式の値となります。
もし、定義した関数の中でreturn文を書かないで関数を呼んだ場合はundefinedになります。

//自ら定義したsampleという関数を呼び出す
function sample2(a)
{
	
}
sample2(100);
//結果を確認する為、コンソールに出力する。結果は「undefined」になる
console.log(sample2(100));

メソッド呼び出しについて

呼び出し式がプロパティアクセス式の場合はメソッドが呼び出されます。
メソッド呼び出しの場合、プロパティアクセスの対象となるオブジェクトや配列がthis引数の値になります。
thisの値は関数本体で利用が可能です。

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

概要

javascriptのオブジェクトのプロパティにアクセスする方法について、試してみます。

オブジェクトのプロパティ値にアクセスする時、プロパテアクセス式が評価されます。
式が評価されるタイミングで、オブジェクトのプロパティの値か配列の要素として解釈されます。

具体的には「式.識別子」「式[式]」のように書きます。

「式.識別子」の場合は、式がオブジェクト、識別子がプロパティの名前です。

「式[式]」の場合は、式がオブジェクト(配列)になり、括弧で囲った式でプロパティや配列のインデックスを書きます。

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

実際に下記のようなコードを書いて試してみました。

//検証用オブジェクト1
var test = {a:100, b:200};

//オブジェクト内にオブジェクトがある例
var hoge = {c:300, d:{e:400, f:500}};

//オブジェクトプロパティ式
console.log(test.a);
console.log(hoge.d.e);
console.log(hoge.d.f);

//プロパティaにアクセス(このような書き方も動作可能)
console.log(test["a"]);

上記の場合、検証用オブジェクト1というオブジェクトを用意し、
そのオブジェクトに対し、プロパティにアクセスした結果を出力しています。

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

100
400
500

上記のようにプロパティアクセス式を使う場合は特に問題なく結果が出力されますが、
もしオブジェクトを定義していないものに対してプロパティアクセス式を使った場合、
例外エラーが発生します。

プロパティアクセス式は、まず式を見てから識別子を判別します。

逆に式となるオブジェクトが存在していて、アクセスする要素が無い場合も例外エラーが発生します。

配列の要素へアクセスする方法

オブジェクトとは別に配列にアクセスする場合も試してみます。

配列の要素にアクセスするには括弧を使ってアクセスします。
具体的には以下のように書きます。

//検証用オブジェクト2(配列の場合)
var test2 = [10, 20, 30, 40, [50, 60]];

//配列の2番目の要素にアクセス
console.log(test2[1]);

//配列の5番目の要素の配列の2番目にアクセス
console.log(test2[4][1]);

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

20
60

上記の結果のように、オブジェクト式の後に括弧が連続する場合は、2番目の式を評価して結果を出力します。

要素に他のオブジェクトを持つ場合

下記のように要素内に他の要素を持つオブジェクトに対してプロパティアクセス式を書いた場合について、試してみます。

下記のコードを書いて動作させたところ

//検証用オブジェクト1
var test = {a:100, b:200};

//検証用オブジェクト3(要素に他のオブジェクトを持つ場合)
var test3 = [test, 20, 30, 40, [50, 60]];

//他のオブジェクトのプロパティにアクセス
console.log(test3[0].b);

//他のオブジェクトのプロパティにアクセス(括弧を使用)
console.log(test3[0]["a"]);

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

200
100

正しくプロパティにアクセスできる場合に値が返り、結果が出力されます。

test3という変数(オブジェクト)に対して1つ目の要素に別オブジェクトがある場合、1つ目の括弧の後に括弧を書いて式を評価します。

上記のように、プロパティにアクセスする際、「.」でアクセスする方法と「[要素名]」でアクセスする方法の2つがあります。

二つのアクセス方法には、一見すると結果に違いはありませんが、
「.」識別子のほうはプロパティ名称が明確にわかっている場合に有効になります。
また、プロパティ名称が予約語の場合や、空白などの場合には「[]」の括弧を使ったプロパティアクセスをします。

関数定義式について

javascriptの関数を定義する式のことを関数定義式と呼びます。

関数定義式も関数リテラルと呼ぶこともできます。

これまでに何度もサンプルコードを書いてきましたが、
具体的には、以下のコードのようになります。

var test = function(x) {
	return x * 10;
}

上記の例は無名関数と呼ばれる関数( function(x) の部分)を定義し、変数のtestへ代入しています。

変数のtestは関数として動作します。
例えば test(10) というように呼び出せば100が返ってきます。

上記の関数定義式は一例にすぎず、プログラムの描き方によって関数定義も異なった形で記述します。

オブジェクト初期化子について

概要

前回の投稿は、配列の初期化子について書きましたが、
同様にオブジェクト初期化について調べてみます。

配列の初期化子の書き方と、オブジェクト初期化子の書き方での違いは、
[]
の括弧を使う変わりに、次の中括弧を使います。
{}

サンプル

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

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

//呼び出す時は以下のように
console.log(test.a);

オブジェクトリテラルは入れ子にして書くことも可能です。

オブジェクト初期化子の中に式がある場合、初期化子が評価されるタイミングで初期化子の中の式も評価されます。

オブジェクトリテラルのプロパティ名は文字列として解釈されます。

配列の初期化子について

javascriptの配列の初期化について試してみます。

配列の初期化は、初期化子という式を使って行います。
配列の初期化子は配列リテラルとも呼ばれます。

具体的にはカンマ区切りの値をカッコで囲んだ形になります。

//要素が5個の配列
[0, 1, 2, 3, 4]

//空の配列
[]

//ネストされた配列も可能
[[0, 1, 2, 3, 4], [9, 8, 7, 6, 5]]

上記の場合は固定の値を配列内に記述していますが、
下記のように、要素が値ではなく式になる場合も記述可能です。

//要素に式を持つ配列
[10+20, 50+60, 80+90]

上記はそれぞれ、30、110、170になります。

未定義の要素を含めることも可能です。

//未定義の要素を持つ配列
[100, , , 200]

上記は4つの要素を持ち、2個目、3個目は未定義の要素になります。

単項式について

javascriptでインタプリタが評価して値を生成できるものを式になります。
式には多くの種類がありますが、最も単純な式を単項式と呼び、定数値(リテラル)、キーワード、変数参照があります。

リテラルは、以前の投稿にも記載したとおり、プログラム中に直接埋め込まれた定数値です。

具体的には「数値リテラル」「文字列リテラル」「正規表現リテラル」があります。

また、予約語「true」「false」「null」「this」も単項式として使えます。

スコープチェーンについて

スコープチェーン概要

javascriptを理解する上で重要な考え方としてスコープチェーンという考え方があります。

javascriptでの変数のスコープは、グローバル変数ではプログラム全体で有効になり、
ローカル変数は宣言された関数と、その関数内でネストされた関数内で有効になります。

実際の例として、以下のようなコードを書いて実行してみます。

//グローバル変数
var test = "1";
console.log("グローバル変数 : " + test);

function scope()
{
	//ローカル変数
	var test = "2";
	console.log("ローカル変数1 : " + test);
	
	function nest()
	{
		//ネストされた関数内のローカル変数(再定義は無し)
		console.log("ネスト内のローカル変数 : " + test);
		
		//関数内のローカル変数の値を返す
		return test;
	}
	
	//このタイミングで、関数から返された「3」を返す
	return nest();
}

console.log("関数呼び出し : " + scope());
console.log("グローバル変数 : " + test);

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

グローバル変数 : 1
ローカル変数1 : 2
ネスト内のローカル変数 : 2
関数呼び出し : 2
グローバル変数 : 1

先ほどの「ローカル変数は宣言された関数と、その関数内でネストされた関数内で有効」ということが上記の出力結果の「ネスト内のローカル変数 : 2」という出力結果になることで、理解できます。

上記の例のように、関数内で変数を宣言する場合や、グローバル変数を宣言する場合、
そのコード(ここでは変数の宣言)に関連づけられたスコープチェーンが存在します。

スコープチェーンはそのコードのに対してスコープ内で変数を定義するオブジェクトのリストと言えます。

ローカル変数のスコープチェーン

ローカル変数aを宣言する際、次のような処理がされます。

・変数aのチェーンの先頭を調べる
・aというプロパティを持つ場合、その値が定義される
・もし、プロパティを持たなかった場合、チェーンの次のオブジェクトを探す
・次のオブジェクトでaというプロパティがあれば、その値が定義される
・もし、なければ、同様にチェーンの次のオブジェクトを探す
・最終的にプロパティが見つからなければ、ReferenceErrorとなる

トップレベルのグローバル変数のスコープチェーン

ローカル変数ではなく、トップレベルのグローバル変数の場合は次のようになります。

・グローバル変数は一つだけ定義される
・関数1つに2つのスコープチェーンが存在する(1つ目のスコープチェーンは関数の引数、ローカル変数、2つ目はグローバルオブジェクト)
・ネストされた関数の場合、3つ以上のスコープチェーンになる
・ネストされた関数が定義された時、スコープチェーンが保持される
・ネストされた関数が呼び出された時、オブジェクトが生成される
・ローカル変数が保存される
・上記の生成されたオブジェクトをスコープチェーンに追加する
・関数呼び出しのスコープチェーンを表す新たなスコープチェーンを生成

簡易的に書きましたが、次のネストされた関数の場合と合わせて、全体像を理解することがポイントになります。

ネストされた関数の場合

ネストされた関数の場合は、次のような動きになります。

その側の関数が呼び出されるたびに内側の関数が再び定義され、
外側の関数の呼び出しごとに、スコープチェーンが異なって動作します。

このスコープチェーンが異なって動作する。という考え方がjavascriptのクロージャの考え方につながってくるので重要です。

変数宣言と再定義について

概要

javascriptでグローバル変数を宣言する時、変数の定義がどのように振舞うのかを調べます。

グローバル変数を生成する際は、グローバルオブジェクトのプロパティが定義されます。
strictモード(‘use strict’)を使わない場合、宣言していない変数に値を代入した瞬間にグローバル変数が作られます。

宣言していない変数とは、具体的には次のような例になります。

//varを使わず、いきなり100を代入(グローバル変数)
test = 100;

上記のように、varによる変数宣言をしない場合は、再定義が可能なプロパティとして生成されます。
また、逆の意味でvarによる変数宣言をする場合は、再定義が不可能なプロパティとして生成されます。

サンプルコード

試しに以下のコードを書きます。

グローバル変数を宣言して生成する場合と、宣言なしで生成する場合の動きの違いがわかります。

//varを使ったグローバル変数へ代入
var test = 10;
console.log(test);

//varを使わず、グローバル変数へ代入
test2 = 20;
console.log(test2);

delete test;
console.log(test);

delete test2;
console.log(test2);

再定義が可能かどうかを調べる為に、delete演算子を使って、それぞれの変数の削除を試みています。

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

10
20
10
ReferenceError: test2 is not defined

最後のtest2という変数はdelete演算子によって削除はできません。
これはvar宣言しない場合の変数宣言は再定義可能な(グローバルオブジェクトの)プロパティとして生成されるので、削除ができたということになります。

削除ができた為に、コンソールで出力しようとしたところ、「ReferenceError: test2 is not defined」というエラーになります。

変数宣言についてのまとめ

簡単にまとめると

■var宣言したグローバル変数
再定義不可

■var宣言しないグローバル変数
再定義可能
(delete演算子による削除ができた)

ということになります。

Callオブジェクトについて

Callオブジェクトという概念についてまとめます。

javascriptではグローバル変数はグローバルオブジェクトのプロパティとして解釈されます。
では、ローカル変数の場合はどうなるかと言いますと、関数の呼び出した時に変数オブジェクトが生成されます。
上記の関数を呼び出した時に生成される変数オブジェクトのことをCallオブジェクトと呼びます。

Callオブジェクトは次の情報が含まれています。

関数内のローカル変数の値
関数に渡された引数(名前と値)
引数情報を管理するオブジェクト
thisキーワード

このCallオブジェクトの考え方はスコープチェーンを理解する時に重要になります。

HTMLファイルがUTF-8で書かれていて、画面更新時にはSJISでPOSTする方法

javascriptの言語仕様の話からは少し逸れますが、webアプリケーションを作成する際、画面遷移時に文字コードの制約がある場合の対応方法を記載します。

具体的にはタイトルの通り、HTMLファイルがUTF-8で書かれていて、画面更新時にはSJISでPOSTする方法です。

<form name="form1" method="post" action="http://xxxxxx/" accept-charset='Shift_JIS'>

<input type="hidden" name="test" value="a">

<input type="submit" value="送信" onClick="buff=document.charset; document.charset='Shift_JIS'; document.form[0].submit(); document.charset=buff;">

</form>

送信ボタンのonClickに文字コードを指定して送信する処理を書くことで、更新時にのみ文字コードを変更して値を送信できます。

決済システム等を実装する際、相手側サーバのAPIがSJISしか受け取れない場合で、なおかつサーバ側言語のフレームワークの制約などで文字コードがSJIS以外で固定されているときに、このように書くケースがあります。

現在アクセスしているURL等を取得する

javascriptをブラウザで実行している際、アクセス時のURL等を取得する方法

console.log("location.href -> " + location.href);
console.log("location.host -> " + location.host);
console.log("location.pathname -> " + location.pathname);
console.log("location.search -> " + location.search);
console.log("location.protocol -> " + location.protocol);
console.log("location.hash -> " + location.hash);
console.log("location.hostname -> " + location.hostname);

locationオブジェクトを使い、任意のプロパティの値を使うと取得可能です。

変数の宣言でデフォルト値を指定する

あまり一般的な書き方ではないかもしれませんが、
javascriptのパイプを利用して、変数のデフォルト値を設定する書き方もあります。

var test = test || "abc";
console.log(test);

出力結果は「abc」となります。

通常ではこのような書き方はあまり見かけませんし、このような書き方はしませんが、動作としてデフォルト値のような振る舞いをします。

javascriptでパイプを2つ書く場合、論理和として判定され、左辺か右辺を両方判定し、論理和を返す動きをします。

この動きを利用して、関数内での変数のデフォルト値を設定するケースがあります。
javascriptならではの書き方かもしれませんが、以下のようなコードになります。

function x(test)
{
	//引数の値がなかった場合、abcとする
	test = test || "abc";
	console.log(test);
}

変数のスコープ

概要

javascriptの変数スコープについて動作を検証します。

グローバル変数とローカル変数がどのように解釈されるのか試します。

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

//グローバル変数
var test = "1";
console.log(test);

function scope()
{
	//ローカル変数
	var test = "2";
	
	return test;
}

console.log(scope());
console.log(test);

便宜上、関数名は任意の名称にしています。
コンソールに出力される結果は

1
2
1

となります。

関数scope()を最後に呼んでおり、そのタイミングで関数内のローカル変数が戻り値として返されています。

ローカル変数の扱いについて

ローカル変数のtest = “2”については、scope()の関数の中だけで有効になるので、結果として「2」が出力されます。

ローカル変数は必ずvarで宣言しなければならず、varをつけない場合はグローバル変数として解釈されます。

試しに、先ほどの例を全てvar無しで実行してみます。

//グローバル変数
test = "1";
console.log(test);

function scope()
{
	//ローカル変数ではなく、グローバル変数として解釈される
	test = "2";
	
	return test;
}

console.log(scope());
console.log(test);

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

1
2
2

関数scopeの中でtestに代入された「2」は、その後の処理でも値が変わらずに変数に格納されています。

最後の14行目の変数testについては、グローバル変数として値が置き換わった「2」に変わっています。

関数の入れ子

関数を入れ子にして定義する場合を試します。
入れ子にした関数内の変数はどのように動作するのか、検証してみます。

//グローバル変数
var test = "1";
console.log(test);

function scope()
{
	//ローカル変数
	var test = "2";
	
	function nest()
	{
		//ネストされた関数内のローカル変数
		var test = "3";
		console.log(test);
		
		//関数内のローカル変数の値を返す
		return test;
	}
	
	//このタイミングで、関数から返された「3」を返す
	return nest();
}

console.log(scope());
console.log(test);

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

1
3
3
1

入れ子にした関数内で何を返すかがポイントになります。
また、入れ子にした関数をどう呼ぶか、呼んだ後に何の値を使うのかも、プログラムの構造により、重要になってきます。

変数の宣言(その3)

変数を宣言する方法について検証

変数の宣言について、繰り返して宣言した場合の動きを試します。

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

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

この場合、特にエラーにならずに処理されます。
コンソールログへは20が出力されます。

次に、varで宣言されていない変数を呼び出してみます。

var test = 30;
var test = 40;
console.log(test2);

この場合は「ReferenceError: test2 is not defined」というエラーになります。
宣言されていない変数に対して、変数の呼び出しはNGです。

では、次のような、変数宣言をしていない変数に代入する場合を試してみます。

var test = 30;
var test = 40;
test3 = 50;
console.log(test3);

この場合、上記のコードのみでしたら、エラーにはなりません。
変数test3への代入は問題なく行われ、コンソールログには50が出力されます。

strict modeについて

また、javascriptにはstrict modeと呼ばれる、実行形態があります。
strict modeはECMAScript2015で導入されて、javascriptを実行するブラウザに厳密な解釈をさせる機能と言えます。
試しに上記のコードの例をstrict modeをつけて実行してみます。

'use strict';
var test = 30;
var test = 40;
test3 = 50;
console.log(test3);

この場合は「ReferenceError: assignment to undeclared variable test3」というエラーになり実行が止まります。

まとめ

ポイントとなる考え方はstrict modeにしない場合、なおかつvarをつけずに変数に代入した場合、エラーにはならなずに処理が続行されます。
この時に使用する変数はグローバルオブジェクトのプロパティして解釈されるので、注意が必要です。
また、グローバル変数のような振る舞いをすることになりますが、javascriptでは基本的にはvarを明記して変数宣言を行うようにします。

変数の宣言(その2)

前回に続きjavascriptの変数宣言について調べます。

変数宣言はvarとletがありますが、宣言の方法を調べてみます。

まずは、変数を単体で宣言する場合は以下のように書きます。

var test;
var a;

など、次に、一度に複数の変数を宣言する場合です。

//1行に書く
var test, a;

次は変数の宣言と同時に値を代入する場合です。

var test = 100;
var a = "1000";

//1行に書いて、なおかつ代入する
var test = 100, a = "1000";

変数宣言はforループ文の内部でも宣言して使用します。

for (var i = 0; i < 100; i++) {
	console.log(i);
}

javascriptでは、宣言された変数に値が代入されない場合は、undefinedと解釈されます。

また、変数を宣言する時に型を指定していません。
型の概念はありますが、明示的に宣言せず、プログラムの処理中に型が動的に決定づけられます。

変数の宣言(var と let)

概要

javascriptにおける変数の宣言を詳しく調べてみます。

本ブログでも以前の投稿(http://propansystem.net/blog/2018/06/20/post-6640/)で、変数の宣言について触れていますが、そもそもjavascriptには歴史的な話としてECMAScript(https://ja.wikipedia.org/wiki/ECMAScript)という名称で標準化されており、ECMAScriptにはバージョンが策定されています。

正式にリリースされているECMAScript2015(2015年に策定)になり、バージョンを省略してES6と呼ぶこともあります。

ES6の前までのバージョンでは、主にvarで宣言していましたが、ES6からはletで宣言することも可能です。

var と letについて

両者の違いは変数スコープの違いがあります。

varの宣言は
以下のような挙動になります。

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

{
	var test = 20;
	console.log(test);
}

console.log(test);

出力結果は

10
20
20

となります。
varで宣言された変数はブラケットで囲んだ後の変数testが20に置き換わっています。

続いて、letでの宣言を試します。

let test2 = 30;
console.log(test2);

{
	let test2 = 40;
	console.log(test2);
}

console.log(test2);

出力結果は

30
40
30

になります。
letで宣言された変数はブラケットの後、変数testの値のが30に置き換わっていません。
このようにletでの宣言は厳密にブラケット内で使うことができ、コーディングの際の不具合の低減につながります。

実装上の留意点

ECMAScriptの策定については、2009年に策定されたEdition5から、2015年以降にEdition6に変わりました。

言語仕様が変わると、ブラウザがそれに対応しているかどうか、といった問題も出てくるので、
一概にjavascript(ECMAScript)のバージョンだけを意識してコーディングができるとも言い切れません。
開発環境や利用環境、全体を見てのコーディングが必要です。

オブジェクトの型変換

概要

javascriptのオブジェクトもまた、型変換ができます。

オブジェクトを型変換する場合、論理値への変換は全てtrueに変換されます。
オブジェクトから文字列、数値への変換はメソッドを呼び出して変換します。

変換するメソッドは

toString()

valueOf()

です。

toStringt()メソッドは各クラスの種類に応じた変換結果を文字列や数値を返します。
valueOf()メソッドについては、各オブジェクトに対しての基本型値に変換したものを返します。

まとめると、以下のようになります。

各オブジェクトの変換について

■オブジェクトを文字列に変換する

①対象となるオブジェクトがtoStringt()メソッドを持つ場合
javascriptはtoStringt()メソッドを呼び出す。
基本型値が返されたら、その値を文字列に変換した文字列を返す。

②toStringt()メソッドが定義されていない場合
基本型値が返されなかった場合は、valueOf()メソッドが定義されているかどうかで、動きが変わってきます。
定義されている場合は、valueOf()メソッドを呼び出します。

③toStringt()メソッドもvalueOf()メソッドも定義されていない場合
例外処理となり、エラーとなります。

■オブジェクトを数値に変換する

①valueOf()メソッドが定義されており、返りの値が基本型値の場合、基本型値を数値に変換して返します。

②toStringt()メソッドが定義されている場合、その値を返します。

③toStringt()メソッドもvalueOf()メソッドも定義されていない場合、例外処理となり、エラーになります。

明示的な型変換

概要

javascriptで明示的な型変換をする場合を試してみます。

明示的な型変換は以下の関数を使います。

Boolean()
Number()
String()
Object()

これらの関数をnew演算子を使わないで呼び出すと、明示的な型変換の動作をします。

簡単な例

次にサンプルコードを書きます。(便宜上consoleに出力しています)

//50
console.log(Number("50"));

//false
console.log(String(false));

//true
console.log(Boolean("60"));

//Number{5}
console.log(Object(5));

コメント部分はそれぞれコンソールに出力された値です。

オペランドと演算の関係

javascriptで、オペランド(被演算子)どうしの演算をする場合、動的な型変換が発生します。

例えば

文字列 + 数値

の場合は、片方の数値が文字列に変換されて、連結されます。

単項演算子の!を使う場合は、論理的に真偽を反転させる為、以下のようになります。

//false
console.log(!10);

//false
console.log(!true);

//true
console.log(!false);

意味の無い実験かもしれませんが、単項演算子!で真偽を反転させたものに、文字列を連結して出力すると、

//falsetest
console.log(!10 + "test");

//falsetest
console.log(!true + "test");

//truetest
console.log(!false + "test");

という結果になりました。

また、単項演算子!の真偽の反転は、2重につけることもできます。
試したところ、それぞれ以下のように出力されます。

//false
console.log(!!false);

//falsetest
console.log(!!false + "test");

Numberクラス

NumberクラスではtoString()メソッドがあります。

ある変数に数値を代入し、toString()メソッドを適用してみます。

この時、メソッドに記述する引数に注目します。
メソッドの引数は2進数から36進数まで、明示的に基数を指定することができます。

それぞれメソッド行末のコメントは実際の出力結果になります。

var test = 65;

//引数を指定しない場合、10進数
console.log(test.toString());       //文字列としての「65」を出力

//2進数として指定
console.log(test.toString(2));      //1000001

//4進数として指定
console.log(test.toString(4));      //1001

//8進数として指定
console.log(test.toString(8));      //101

//10進数として指定
console.log(test.toString(10));     //65

//16進数として指定
console.log(test.toString(16));     //41

//30進数として指定
console.log(test.toString(30));     //25

//36進数として指定
console.log(test.toString(36));     //1t

//(実験的に)37進数として指定
console.log(test.toString(37));

一番最後に実験的に37進数を指定すると

RangeError: radix must be an integer at least 2 and no greater than 36

というエラーになります。

Numberクラスのメソッド

また、Numberクラスには上の例(toString)の他、下記のメソッド(一例です)があります。

toLocalString
toFixed
toExponential
toPercision

詳しく調べていくと、Numberクラスのprototypeには以下のメソッドがあります。

Number.prototype.toExponential()
Number.prototype.toFixed()
Number.prototype.toLocaleString()
Number.prototype.toPrecision()
Number.prototype.toSource()
Number.prototype.toString()
Number.prototype.valueOf()

全てのメソッドの意味と実行結果を確かることはしませんが、全てprototype経由でコールされている点に注意が必要です。

いくつかサンプルを書いてみます。(console.logのコメント部はそれぞれ演算結果です)

//ランダムな数字を代入
var test = 2482704.5681;


//文字列に変換され、小数点は指定の桁数になる
//2482705
console.log(test.toFixed(0));

//2482704.568
console.log(test.toFixed(3));

//2482704.568100
console.log(test.toFixed(6));


//数値が指数表現に変換される
//2.5e+6
console.log(test.toExponential(1));

//2.4827e+6
console.log(test.toExponential(4));


//有効桁数が少ない場合、指数表現に変換される
//2.483e+6
console.log(test.toPrecision(4));

//2482704.6
console.log(test.toPrecision(8));

//2482704.568
console.log(test.toPrecision(10));

prototypeの概念は今回の投稿内容とは別の領域になるので、別途詳しく掘り下げます。
簡単にいうと、javascriptの全てのオブジェクトにはprototypeを継承していて、最小のテンプレートという考え方ができます。

また、Numberのグローバル関数としては、次のようなものが定義されています。

Number.isFinite()
Number.isInteger()
Number.isNaN()
Number.isSafeInteger()
Number.parseFloat()
Number.parseInt()

型変換の際に使う関数としては、parseIntとparseFloatになりますが、
parseIntは整数のみ解析できることに対し、
parseFloatは整数と浮動小数点数の両方を解析できます。

以下、簡単なサンプルを書いて試してみます。

var test = 2482704.5681;
console.log(parseInt(test));    //2482704

var test = "24827テスト";
console.log(parseInt(test));    //24827

var test = "24827テスト";
console.log(parseFloat(test));  //24827

var test = 248.4587;
console.log(parseInt(test));    //248

var test = -248.4587;
console.log(parseInt(test));    //-248

var test = 0xFF;
console.log(parseInt(test));    //255

var test = 0xff;
console.log(parseInt(test));    //255

var test = 0xFD;
console.log(parseInt(test));    //253

var test = "0.1";
console.log(parseInt(test));    //0

var test = "0.2";
console.log(parseInt(test));    //0

var test = ".2";
console.log(parseInt(test));    //NaN

var test = "abcd.2";
console.log(parseInt(test));    //NaN

var test = "abcd.3";
console.log(parseFloat(test));  //NaN

実行した結果、上記のようになります。
数値として解釈できない場合には、NaNという処理結果になります。

型変換時の比較について

概要

等値演算子で値が等しいかの判定が行われる時、型変換が実行されます。

例えば、下記のようなコードを書いて出力してみます。
(コメント部分は出力結果です)

//trueと出力
console.log(null == undefined);

//trueと出力
console.log("50" == 50);

//falseと出力
console.log(60 == false);

//trueと出力
console.log(0 == false);

//falseと出力
console.log("70" == false);

//trueと出力
console.log("0" == false);

上記の実行結果で、trueと返るコードがあります。

等値による比較を行っていますが、それぞれ左辺と右辺で異なる型を書いていますが、比較するタイミングで型の変換が行われ、それを基に結果(true/false)が出力されていることがわかります。

例えば「0 == false」の比較では、右辺のfalseが比較時に一度0として変換された後に処理されているのがわかります。

等値演算子と、同値演算子の比較

等値演算子「==」による比較は上記にようになりますが、同値演算子の場合はどうなるでしょうか。

試しに、上記の例の比較部分を全て同値演算子に変更したコードを書いてみます。

//falseと出力
console.log(null === undefined);

//falseと出力
console.log("50" === 50);

//falseと出力
console.log(60 === false);

//falseと出力
console.log(0 === false);

//falseと出力
console.log("70" === false);

//falseと出力
console.log("0" === false);

上記の結果、全てfalseとして判定されました。

同値演算子の場合は、動的に型変換が行われず、厳密に型が違う為にfalseを返す挙動になります。

javascriptでは、値の変換がおこる場合、演算子や文で必要とされる型への変換が動的に行われます。

型の変換

概要

javascriptの変数には型の概念がありますが、明示的に型を指定して変数を宣言することはありません。

例えば変数testを宣言する場合

var test;

としますが、このtestという変数の型は見ただけでは不明です。

プログラムの流れの中で型が決定される場合が多く、また演算中に型が変換(決定)する場合が多くあります。

サンプル

例えば、次のような例をあげます。

//数値文字列が文字列に変換される
console.log(5 + "abc"); //出力結果は「5abc」

//数値同士の文字列連結が文字列に変換される
console.log("5" + "6"); //出力結果は「11」

//文字列が数値に変換される
console.log("5" * "6"); //出力結果は「30」

上記の場合はコンソールに直接出力していますが、これは変数に代入しても結果は同じになります。

グローバルオブジェクト

グローバルオブジェクトについて

概要

グローバルオブジェクトとは、javascriptが実行された時にどこからでも呼び出すことができるオブジェクト。
以下のものがあります。

グローバルオブジェクトの例

グローバルプロパティ
undefined
Infinity
NaN

グローバル関数
isNaN()
perseInt()

コンストラクタ関数
Date()
RegExp()
String()
Object()
Array()

グローバルオブジェクト
Math
JSON

生成のしかた

通常、webアプリケーションを作成する場合はブラウザベースでの開発になります。
ブラウザで実行される場合、Windowオブジェクトがグローバルオブジェクトとなります。

グローバルオブジェクトが生成される際、グローバル値が設定されます。

グローバル変数として宣言した変数はグローバルオブジェクトのプロパティとして解釈されます。
以下に例を書きます。

//グローバル変数を用意する
var test_global = 10;

//windowオブジェクトのプロパティとしてアクセス
console.log(window.test_global);

//出力を確認
//「10」と出力される

nullとundefined

nullとundefinedについて

javascriptの予約語である

null

は値が存在しないことを表す値です。

nullの実行について

次のようなコードを書いて実行してみた結果、
コンソールに出力しても当然、nullが返ります。

var test = null;
console.log(test);

これをtypeof演算子を通して出力してみます。

console.log(typeof(test));

するとobjectという出力結果になります。

undefinedの実行について

このnullとは対照的にundefinedという予約語もあります。

意味としては「値が存在しない」というnullと同じですが、初期化されていない変数の値、オブジェクトのプロパティが存在しない、配列の要素が存在しない場合の、未定義の値を指すときを表します。

nullは予約語ですが、undefinedは予約語ではないです。

undefinedに対してtypeof演算子を使ってみます。

console.log(typeof(undefined));

実行するとそのままundefinedが出力されます。

また、一度変数に入れて実行すると、下記の例では同様にundefinedが出力されます。

var test2 = undefined;
console.log(test2);
console.log(typeof(test2));

nullとundefinedの比較

nullとundefinedを比較してみます。

以下のコードを書いて実行結果をみてみます。

var test_null = null;
var test_undefined = undefined;

if (test_null == test_undefined) {
	console.log("一致する");
} else {
	console.log("一致しない");
}

if (test_null === test_undefined) {
	console.log("一致する");
} else {
	console.log("一致しない");
}

上記の実行結果はともに「一致する」という出力結果になります。
一度変数に入れた両者の値として解釈されるので、等値(==)と同値(===)の判定条件でも同じになりました。
この記事を書いている段階では、変数に代入した後にどう解釈されているのか、もう少し深く理解する必要があります。

では、次のような直接的に書いて両者を比較する場合はどうなるか、試してみます。

if (null == undefined) {
	console.log("一致する");
} else {
	console.log("一致しない");
}

if (null === undefined) {
	console.log("一致する");
} else {
	console.log("一致しない");
}

この場合は、等値(==)の場合は「一致する」として、同値(===)の場合は「一致しない」という結果になりました。

nullとundefinedのオブジェクトについて

nullをtypeof演算子でみるとobjectという出力結果になります。
objectという結果になりますが、nullにはプロパティやメソッドもありません。

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

//何も表示されない
null

//存在しないメソッドを呼ぶ
null.test();

結果、nullに対して存在しないメソッドを呼んでも

TypeError: null has no properties

という出力結果になります。

ここでは存在しないメソッドを呼びましたが、そもそもnullに対してはメソッドを追加することができない為、どのような場合でもTypeErrorになります。

論理値

概要

javascriptの論理値について、プログラムを書いて確かめます。

他のプログラム言語ではboolean型として、書くケースが多い論理値です。
真か偽か、trueかfalseか、といった2つのみの値を持つ変数になりますが、javascriptは特有のケースがあります。

変数の判定式を試す

論理値のサンプルを書いてみます。
以下のコードを書いて実行させてみます。

var test = 10;
if (test == 10) {
	console.log("一致する");
} else {
	console.log("一致しない");
}

実行すると「一致する」という結果になります。これは論理値の比較ではなく、変数の値の比較なので一致するという結果になります。

では、一度代入した変数そのものをif分で比較してみます。

var test = 10;
if (test) {
	console.log("一致する");
} else {
	console.log("一致しない");
}

上記の場合は「test」という部分がtrueかfalseかを判定する論理値になっている状態です。
結果は「一致する」という出力になります。

変数の判定式を試す(その2)

上記のように式全体の場合はわかりやすい例と言えます。

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

var test2 = null;
if (test2) {
	console.log("TRUE");
} else {
	console.log("FALSE");
}

この場合、test2という変数に対し、nullを代入しています。
その変数そのものをif分で判定すると、falseが返り、コンソールログ上の出力結果は「FALSE」となります。

このように、javascriptでは論理値でFALSEと判定される値があります。

必ずfalseになる値について

以下の値はfalseになります。

undefined
null
0
-0
NaN

配列の場合の判定結果

また、次のように配列を記述した場合

var testarray = [1, 2, 3, 4];

if (testarray) {
	console.log("TRUE");
} else {
	console.log("FALSE");
}

この場合の実行結果は「TRUE」になります。

また、論理値にはtoString()メソッドが使え、このメソッドを使った時はtrueかfalseがそのまま文字列として変数などに格納されます。

パターンマッチング

javascriptで文字列に対して正規表現を使ってパターンマッチングすることを試してみます。

概要

通常、javascriptではRegExpコンストラクタを使ってマッチングをします。
マッチする条件としては正規表現で書かれた内容を基にされます。
正規表現については、javascriptとは別カテゴリなので、別途詳しく掘り下げます。

正規表現の記載方法としてはperlのものと同様の書き方が有効なります。

パターンマッチングの例

javascriptでのパターンマッチングについて、一例を書きます。

//「パターン文字列」にマッチするかどうか
/パターン文字列/

//対象文字列の先頭が「パターン文字列」にマッチするかどうか
/^パターン文字列/

//0以外の数字にマッチするかどうか
/[1-9][0-9]/

//大文字小文字区別なしにjavascriptにマッチ
/\bjavascript\b/i

上記のようなパターンマッチが使えるので、次に実際にコードを書いてみます。

パターンマッチングの検証

実際に次のようなコードを書いた場合の、実行結果をコメント部分に書いています。

//対象文字列
var check_string = "abcdefghijkh1234";

上記の対象文字列(変数)に対して、マッチするパターンを決めます。

//文字列「abc」にマッチするパターン
var pattern = /abc/;

//先頭の文字列が「abc」にマッチするパターン
var pattern2 = /^abc/;

//文字列「h」にマッチするか
//(正規表現の最後の「/g」は文字列の最後まで検索を繰り返す
var pattern3 = /h/g;

対象文字列とマッチするパターンを組み合わせ、マッチングを実行します。

//マッチするかどうか検証

//trueが返る
console.log(pattern.test(check_string));

//trueが返る
console.log(pattern2.test(check_string));

//文字列「h」がcheck_stringの何番目になるか
console.log(check_string.search(pattern3));		//7が返る

//文字列「h」がヒット、検索結果は Array [ "h", "h" ] として返される
console.log(check_string.match(pattern3));

上記のコメント部分の結果が得られました。
パターンの記述方法や、マッチさせる方法はこの他にもあるので、実際にコーディングする場面が出てきたら追記します。

文字列操作

javascriptの文字列を操作する方法を試します。

文字列の連結は「+」で行います。
下記、例です。

var string_test = "test" + "1234";

上記の場合、変数string_testにはtest1234という文字列が代入されます。

文字列に対し、標準で装備されているメソッドを使うことができます。
下記、例です。(便宜上、console.log出力はしていません)

string_test.length();
string_test.charAt();
string_test.substring(2, 4);
string_test.slice(1, 3);

等のメソッドがあります。ここではメソッドについては一部抜粋しているだけなので、詳しくはリファレンスサイト等を確認するとよいです。

上記のメソッドについては、変数内の文字列に対して、メソッドを実行した結果を返す点に注意が必要です。
メソッドを実行した後でも変数内の文字列は変わらないです。

文字列リテラルとエスケープシーケンス

javascriptで文字列を扱う場合のまとめ

・単一引用符(シングルクォート)で囲む
・二重引用符(ダブルクォート)で囲む
・複数の行をまたいで記述可能
・複数行に分割する場合は「\」を使う
・文字列中に改行を含める場合は「\n」を使う
・単一引用符内の文字列にシングルクォートを含める場合は、エスケープ処理が必要。エスケープ処理は「\」を使う

また、javascriptはバックスラッシュについては、特別な意味を持ちます。
バックスラッシュの直後に特定の文字を記述すると、特定の制御コードを表します。

一例を挙げると「\n」は改行を表します(これは使用頻度が高いです)
また、以下にエスケープシーケンスの例を抜粋します。

//NULL文字
\0

//バックスペース
\b

//タブ
\t

//改行
\n

//垂直タブ
\v

//改頁
\f

//復帰
\r

//二重引用符
\"

//単一引用符
\'

//バックスラッシュ
\\

//2桁の16進数(XX)で指定するLatin-1文字
\xXX

//4桁の16進数(XXXX)で指定するUnicode文字
\uXXXX

上記の例がバックスラッシュの後に有効な制御コードになります。
この文字列以外の文字を指定すると、バックスラッシュが無視され、バックスラッシュの後の文字列が解釈されます。

日付と時刻

javascriptで日付と時刻を表すにはDate()コンストラクタを使います。

具体的には、下記のような書き方をします。(一例です)

//指定の日時
var date1 = new Date(2018, 5, 5);
var date2 = new Date(2018, 6, 5, 19, 15, 20);
console.log(date1); //Date 2018-06-04T15:00:00.000Z
console.log(date2); //Date 2018-07-05T10:15:20.000Z

//現在の日時
var date3 = new Date();
console.log(date3);

//各種、値の取得
console.log(date3.getFullYear());
console.log(date3.getMonth());
console.log(date3.getDate());
console.log(date3.getDay());
console.log(date3.getUTCHours());
console.log(date3.toString());
console.log(date3.toUTCString());
console.log(date3.toLocaleDateString());
console.log(date3.toLocaleTimeString());
console.log(date3.toISOString());

また、出力される値は型に注意して実装する必要があります。
見た目が数字なので全て数値型であるとは限らない為です。

上記の後半部分の実行結果はそれぞれ以下のように出力されます。

2018
6
5
4
8
Thu Jul 05 2018 17:54:46 GMT+0900
Thu, 05 Jul 2018 08:54:46 GMT
2018/7/5
17:54:46
2018-07-05T08:54:46.762Z

算術演算

javascriptの算術演算は次の演算があります

加算+
減算-
乗算*
除算/
剰余%

また、算術演算の他に、数学演算を行うMathというオブジェクトが標準で利用できます。

下記に一例を記載します。

console.log(Math.pow(3, 8)); //3の8乗
console.log(Math.round(0.7));
console.log(Math.ceil(0.7)); //整数に切り上げ
console.log(Math.floor(0.7)); //整数に切り捨て
console.log(Math.abs(-10)); //絶対値
console.log(Math.max(10, 15, 20)); //最大値
console.log(Math.min(10, 15, 20)); //最小値
console.log(Math.random()); //ランダムな数値を返す
console.log(Math.PI); //円周率
console.log(Math.E); //自然対数の底

/*
実行結果はそれぞれ以下のようになる
6561
1
1
0
10
20
10
0.9465591205735182
3.141592653589793
2.718281828459045
*/

javascriptでは、算術演算に以下のような特徴があります。

・オーバーフロー、アンダーフローが発生した場合にエラーにならない
・0で割った場合にもエラーにならない
・演算結果が最大値より大きくなった場合は無限大(Infinity)という値になる
・負の絶対値が最大を超えた場合、無限大負値になる(-Infinity)
・0を0で割ると、not-a-numberという「NaN」という出力になる
・無限大を無限大で割った場合もNaNになる
・InifinityとNaNはグローバル変数として定義される
・NaNの比較をすると、全ての値と等しくないという判定になる
・NaNとNaNを比較しても、等しくないという判定になる

プログラムを書いて実行した際に、コンソールログを確認してNaNやIntinityの表示が出てきた場合、上記のような算術演算の仕様を再確認することが重要になってきます。

数値を扱うプログラムが中心の場合は特に注意が必要になります。

浮動小数点リテラル

実数を記述する時には浮動小数点リテラルを使います。

浮動小数点リテラルでは、小数や指数も使用できます。

先頭が「整数部」、次に「小数点」、その後に「小数部」という形式になります。

以下、整数部+小数点+小数部の記述例です。

100.5
123.8
.005

次に、指数の記述例です。

7.2e5

これは7.2×10の5乗を表しています。

指数の書き方は「e」または「E」という形で記述します。
どちらでも同じ処理がされます。

そのe(またはE)の後に、プラス「+」かマイナス「-」を記述します。

例えば次のような場合もあります。

3.84e-15

この場合は3.84×10のマイナス15乗ということになります。

整数リテラル

javascriptのプログラム内で数値を表す場合は、10進整数で書きます。

10進整数の例

1
5
10
500

また、16進数(先頭が0xか0Xとして、その後に16進数)で指定することも可能です。

16進整数の例

コメント部分は10進数の値
0xf //15
0xff //255
0xC //12
0x7B //123
0x4D2 //1234
0x3039 //12345

16進数の詳しい説明はこちらのwiki(https://ja.wikipedia.org/wiki/%E5%8D%81%E5%85%AD%E9%80%B2%E6%B3%95)が参考になります

javascriptは8進数には対応していないので、10進数、16進数を使うようにします。(処理系によってはサポートしているが統一されていないのでNGと考えたほうがいいです)

数値

javascriptでの数値の扱いについてまとめ

  • 整数と浮動小数点は区別しない
  • 全ての数値を浮動小数点として扱う
  • 最大値は±1.7976931348623157x10の308乗
  • 小値は±5x10のマイナス324乗
  • 数値形式は-9007199254740992(-2の53乗)~9007199254740992(2の53乗)の整数
    (この範囲を超えると、精度が損なわれる)

 

javascriptの型について、簡単にまとめます。

型は大きく分けて2種類
基本型とオブジェクト型

基本型は、数値、文字列、論理値(真偽)、null、undefinedがある
オブジェクト型は、数値、文字列、論理値、null、undefined以外のjavascriptの値を示す

オブジェクトはプロパティの集合体
プロパティは名前と値の組み合わせを持つ

グローバルオブジェクトは特殊なオブジェクト
関数もオブジェクトとして扱う場合がある
コンストラクタの概念アリ。new演算子でオブジェクトを生成する際に初期化する時に使う関数

クラスはオブジェクト型の派生型にあたるもの
Arrayクラス、Functionクラス、Dateクラス、RegExpクラス、Errorクラスがある

javascriptにはガーベジコレクション機能がある
メモリの開放は自動的にされる

関数(メソッド)はオブジェクトに対して持つ
null、undefinedにはメソッドを呼び出すことはできない

値を変更できる型=オブジェクト(プロパティ)、配列(要素)
値を変更できない型=数値、文字列、論理値、null、undefined

変数には型の概念がない
どの型も代入可能
一度、代入した後に、違う型の変数を代入可能

変数はvarで宣言
構文スコープにより、関数外で宣言した変数はグローバル変数として、関数内と関数外からアクセス可能
関数内で宣言した変数は、関数スコープとなり関数内で利用可能

セミコロンについて

javascriptの行末はセミコロンを記述して、次のプログラムの先頭と明示的に分ける書き方が一般的です。

var a = 100;

ただし、セミコロンは必ず書かなければエラーになるという仕様ではありません。

例えば、下記のようなプログラムの場合、

var a = 100;
var b = 200;

console.log(a); //100
console.log(b); //200

変数aとbにはそれぞれ100と200が代入され、console.logに出力されますが、
次のように、変数aの最後のセミコロンを省いて書いたとしても、エラーにならずに実行されます。

セミコロンを省いた場合、改行が行末(プログラムの区切り)として処理されます。

var a = 100
var b = 200;

console.log(a); //100
console.log(b); //200

また、セミコロンを明示して、次のように1行に複数の命令を書くこともできます。

var a = 100; var b = 200;
console.log(a); //100
console.log(b); //200

ここまでで注意が必要な点は、改行が全て行末として解釈されるわけではないという点です。
次のような例は、処理のされ方がこれまでとは違います。

var a = 700
var b
c
=
800

console.log(a); //700
console.log(b); //undefined
console.log(c); //800

2行目、3行目で改行されていますが、変数bと変数cは別々のものとして解釈されています。
決して変数bcという単位で代入がされるわけではないです。

javascriptとして解釈されるには変数bが一旦、その行で終わったことになり、改めて変数cの暗黙的な定義がされた後に、値が代入されています。

このとき、変数cの行末は改行されていますが、セミコロンとしての解釈ではなく、次の2行の変数の代入までをひとつの命令として解釈しています。

改行がセミコロンとして解釈されない例があります。
下記の命令の場合は、処理に注意する必要があります。

//制御文の場合
return
break
continue

//インクリメント、デクリメント演算子
++
--

ポイントとなる点は、前者は制御文の直後に改行を行わないことと、
後者はインクリメントしたい変数と同じ行に演算子を書くことになります。

識別子

javascriptで変数や関数の名前を指定する際に使う名前を識別子といいます。

識別子は次のように書くルールがあります。

・先頭の文字はunicode文字、アンダーバー、$、とする
・数字は不可
・一般的にはASCII文字を使用する
・unicodeで表現できる文字も使用は可能(実際はあまり使われない)

リテラル

動的に型が決まるjavascriptは変数を宣言する時、型の指定がありません。

プログラムの実行時には、文字列、数値、真偽値、null等の値の型が決定づけられます。

また、データ型はプリミティブ型とオブジェクトに分けられます。

プリミティブ型は基本型と呼ばれています。
オブジェクト型は参照型と呼ばれます。

上記のプリミティブ型は、値を指定すると変更できない性質をもち、
オブジェクト型は値を指定した後に、値自体を変更できる性質があります。

プログラム内に直接記述して表現する値をリテラルと呼びます。
実行時にもプログラムに書いた値がそのままの意味を持ちます。

下記に例を書きます。

10
1.0
"リテラル"
'リテラル'
true
false
null
undefined

上記は実行してもプログラム上の処理はなにもされません。

次のような例もあります

var a = 'test';
console.log(a);

この場合の変数aに代入した「test」もリテラル(文字列)で表現されたものと呼べます。
「test」と一度代入されたものは、プログラム実行中は変更しない(変数自体は書き換えられることがあっても)ので、プリミティブ型の値と呼ぶことができます。

また、オブジェクトリテラル、配列リテラル、関数リテラル、正規表現、などもあります。

{a:5, b:6}
[3, 4, 5, 6]

上記はそのまま記述するとエラーになります。

javascriptのエスケープシーケンス

javascriptは基本的にunicodeで記述します。
実行環境がunicodeをサポートしていない場合、エスケープシーケンスを使って表現することができます。

エスケープシーケンスは「\u」を先頭に下記、その後に4文字のASCII文字を記述して表します。

例えば

という文字を扱う場合は、エスケープシーケンス\uの後に「0151」という4文字の16進数を記述します。

\u0151

エスケープシーケンス後のunicode文字列の一覧については、こちらのwiki(https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF)に詳しくまとめられています。

特殊な文字列を取り扱う必要がある場合は、上記のサイトを参考にしてプログラムを書きます。

javascriptをHTMLタグ内に書く

前回の記事はHTMLソース内にjavascriptを書く方法と、外部ファイルにjavascriptを書く方法を試してみました。

また、HTMLソース内にjavascriptを書く方法は、HTMLソースのタグ内に書く方法もあります。

簡単なサンプルを書いてみました。

<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<script>
//サンプル用関数(ボタン)
function alert_button()
{
	alert('test BUTTON!');
}

//サンプル用関数(リンク)
function alert_link()
{
	alert('test LINK !');
}
</script>

</head>
<body>

<button onclick="alert('test');">タグ内に直接書く</button>
<button onclick="alert_button();">タグ内から関数を呼ぶ</button>
<div onclick="alert_link();">文字に対してクリックイベントを付与</div>

</body>
</html>

ボタンが2個表示あり、HTMLソース内の button に対して、onclickイベントを付与しています。
このイベントはボタンを押したタイミングで発動されるもので、ユーザの動作に応じて命令が実行されます。
(他にもイベントの種類は多数あるので、別途まとめる予定です)

1つ目のボタンにでjavascriptのalert関数をそのまま呼び出していますが、2つ目のボタンには、HTMLのhead内に記述した独自の関数を呼び出しています。

また、ボタンの下の文字列に対しても、クリックした独自の関数を呼ぶようにイベントを付与しています。

どの方法でも動作には変わりはないですが、実装する場面で書き分けてプログラムします。

javascriptを書く場所

javascriptを書く場所は、クライアントサイドでプログラムを動作する場合は、ブラウザ内に直接記述するか、別ファイルとしてjavascriptを記述して読み込む方法があります。

HTMLソース内に直接記述する場合の例です。

<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>

HTMLソース内に直接記述する場合

<script>
//ここに書いた命令がそのまま実行される
alert("クライアントサイドjs");
</script>

</body>
</html>

次の例はHTMLソース内から別ファイル(main.js)として記述したjsファイルを読み込んでいる例です。

<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="./main.js"></script>
</head>
<body>

HTMLソースから別ファイルを読み込む場合

</body>
</html>

どちらも実行されますが、比較的、外部ファイルとして読み込む場合が多く利用されています。
その場合は、読み込み順によってプログラムの実行結果に影響する場合があるので、注意します。

javascirptの条件分岐と、オブジェクトの使い方

javascriptの条件分岐を書きます。
他の言語と同様に条件分岐も一通りあります。

ただ、注意が必要なのは変数の型が明示的ではないので、条件の判定のされ方については、注意を払って書く必要があります。

var a = 3;

//値を条件により判別して分岐
if (a >= 4) {
	//3を超える場合
	console.log("3 over");
} else {
	//3以下の場合
	console.log("3 under");
}


//条件が真の場合、ループ処理し続ける
while (a < 6) {
	a++;
	console.log(a); //4 5 6 と出力
}


//条件が真の場合、ループ処理し続ける
for (var i = 0; i < 5; i++) {
	console.log(i); //1 2 3 4 と出力
}

また、オブジェクトを生成した後、オブジェクトに対してメソッドを追加する方法もあります。

その場合、インスタンスを生成した後にメソッドを追加しても、その生成したインスタンスからメソッドを呼ぶことができます。

下記、サンプルとして動作検証した結果です。

//オブジェクトを生成して、メソッドを追加

//コンストラクタ関数を定義する
function sampleobj(x, y) {
	this.x = x;
	this.y = y;
}

//インスタンスを生成する。newキーワードを使ってコンストラクタ関数を呼ぶ
var p = new sampleobj(3, 3);

//sampleobjオブジェクトのプロトタイプオブジェクトに関数を代入することで
//新規メソッドを定義する(例として、平方根を返すメソッドを追加)
sampleobj.prototype.r = function() {
	return Math.sqrt(
		this.x * this.x + this.y * this.y
	);
};

//sampleobjオブジェクトに対し、rというメソッドが使えるので、
//インスタンス経由でメソッドをコールする
console.log(p.r());

//最終的に「4.242640687119285」という出力がされる

javascriptの関数

javascriptの関数は、一般的なプログラム言語と同様の書き方はもちろん、関数そのものを変数に代入する書き方もあります。

また、配列や値を複数持つ配列をオブジェクトとみなし、オブジェクトに対しての関数をメソッドという名で呼び出して使うことができます。

メソッドについては、独自のメソッドを定義し、オブジェクトに付与して呼び出すこともできます。

下記の一連のプログラムは、実際に書いて動作させてみた結果です。

//関数の定義
function sample(a) {
	return a + 1;
}
//関数のコールと結果
console.log(sample(5)); //6



//関数の定義を変数に代入する方法
var sample_func = function(a) {
	return a + 2;
};
//関数のコールと結果
console.log(sample_func(7)); //9



//関数をオブジェクトと組み合わせる

//空配列を作成する
var a = [];

//配列に値を代入する
a.push(5, 6, 7);

//aをオブジェクトとしてみなすと、オブジェクトに対しての
//関数「メソッド」が使用できる

console.log(a); //Array[5, 6, 7]と表示される

a.reverse();

console.log(a); //Array[7, 6, 5]と表示される





//別々のオブジェクトを持つ配列を宣言する
var testarray2 = [
	{a:0, b:0},
	{a:2, b:2}
];

//配列に対し、独自メソッドを追加する
testarray2.dist = function() {

	var p1 = this[0]; //{a:0, b:0}の値のことを示す
	var p2 = this[1]; //{a:2, b:2}の値のことを示す
	
	//2つのオブジェクトの値(0:0)、(1:1)の平方根を求めて返す
	var a = p2.a - p1.a;
	var b = p2.b - p1.b;
	
	return Math.sqrt(a*a + b*b);
};

//別々のオブジェクトの平方根を求めた結果
console.log(testarray2.dist()); //2.8284271247461903

javascriptの演算子

演算子については他の言語と同様の演算子があります。
console.logで出力を確認して一通りの結果をチェックしました。(コメントアウトしている部分が出力結果)

//四則演算
console.log(5 + 2); //7
console.log(5 - 2); //3
console.log(5 * 2); //10
console.log(5 / 2); //2.5
console.log("5" + "2"); //52(文字列として出力されている)


//基準となる変数を用意
var a = 1;

//インクリメント
console.log(a++); //1

//デクリメント
console.log(a--); //2

//加算して代入
a += 2;
console.log(a); //3

//乗算して代入
a *= 2;
console.log(a); //6


//等値演算子
var b = 5;
var c = 8;

console.log(b == c);  //false
console.log(b != c);  //true
console.log(b < c);   //true
console.log(b <= c);  //true
console.log(b > c);   //false
console.log(b >= c);  //false


//文字列同士の比較
console.log("b" == "c");  //false

//文字列(アルファベット順で判定する
console.log("b" > "c");  //false
console.log("b" < "c");  //true


//論理演算子
console.log((b == 5) && (c == 8));  //true
console.log((b > 6) || (c < 9));    //false

javascriptは型の概念がありますが、プログラム内では明確に宣言をしない為、演算結果に注意してプログラムする必要があります。

javascriptの基本

javascriptを使って開発する場合、動きが派手な演出ができたり、ネットワーク経由で様々なデータのやりとりが出来ますが、まずは地道に基本的な部分から進めます。

javascriptはUnicode文字コードを使って書きます。
プログラム内のキーワード、変数、関数、識別子で大文字と小文字は区別されます。

また、プログラム内で注意が必要な点は以下になります。
・空白は無視される
・改行も無視される(行末が指定されていない場合)
・スペース文字、タブ、垂直タブ、改ページ、ノーブレークスペース、バイトオーダーマーク等も空白として処理される
・行末は、改行、復帰、ラインセパレータ、パラグラフセパレータとする

具体的なコメント、変数宣言、型、オブジェクト、配列、までの基本的な書き方は次のようになります。

//コメント

/*
複数行コメント
*/

//変数の宣言
var a;

//変数への代入
a = 1;

//型
//数値
a = 1;
//数値
a = 0.1;
a = "文字列";
a = '文字列';

//真(boolean)
a = true;
//偽(boolean)
a = false;

//NULL
a = null;

//undefined
a = undefined;

次に、オブジェクトについて。
「console.log」については、ブラウザがサポートしていれば動作のログとして確認可能(ここでは主に確認用に使用します)

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

//オブジェクトへのアクセス(方法1)
console.log(testobject.aaa);
console.log(testobject.bbb);
console.log(testobject.ccc);

//オブジェクトへのアクセス(方法2)
console.log(testobject["aaa"]);
console.log(testobject["bbb"]);
console.log(testobject["ccc"]);

//オブジェクトに新規プロパティ作成後、値を代入する
testobject.ddd = "test3";

//空オブジェクトを作成
testobject.eee = {};

次に配列の書き方です。

//配列
var testarray = [1, 2, 3, 4];

//最初の一つ目を表す
testarray[0];

//配列の要素数
testarray.length;

//配列に新たに要素を追加する
testarray[4] = 5;

//既存の値を更新する
testarray[4] = 6;

//空の配列を作成する
var testarrayempty = [];


//配列内に、別のオブジェクトを格納する
var testarray2 = [
	{a:0, b:0},
	{a:1, b:1}
];

//配列内に、別のオブジェクトを格納する
var testarray3 = {
	data1: [[1, 2], [8, 9]],
	data2: [[2, 3], [6, 7]]
};

基本的な変数、型、配列、オブジェクトの使い方は以上です。

javascript概要

javascriptについて進める前に、どれくらいの粒度で開発を進めていくのかを決めます。

細かく言語仕様をイチからなぞっていくと、リファレンスを紐解くことと同じになってしまい、いたずらに時間だけが過ぎてしまうことになりそうです。

そこで、本ブログでは、次のような粒度で記事化しようと思います。

  1. 言語仕様については、開発時に調査が必要になった場合に掘り下げる
  2. javascriptの概要から始まり、基本的な部分の理解からweb開発に必要なノウハウを蓄積する内容を書く
  3. 実際に開発して動作したコードを、可能な範囲で公開する

粒度がバラバラになるかもしれませんが、javascriptの一通りの範囲を網羅できるように進めていきます。

1 6