はじめに
今までJavaScriptをWebサイト等を参考に適宜コーディングしてきた。
雰囲気で理解していた部分が多かったので、基礎から学び直すことにした!
この記事はその学習の記録である。
文法編
変数宣言について
ES2015(ES6)より以下の2種類の変数宣言が可能になった。
- var
- let
この違いが分かっていなかった。
letは、同名の変数宣言を許容していない。
以下のコードはエラーとなる。
let test = 'test';
let test = 'test';
varは、同名変数の重複を許容している。
なので、以下のコードはエラーとならない。
var test = 'test';
var test = 'test';
以下参考文献からの引用
letの方が厳密に変数のスコープ(有効範囲)を管理できる!
スコープが明確になることはすごくい!
ある程度のソースを書いていると変数を多重定義していて『いざ、ここで使うぞ!」となって、使ってみたら中身が違ったなんてことはよくあるので!
ES2015になって、便利だなと感じた一つ!
分割代入
let data = [1,2,3,4,5,6,7,8];
let [x0,x1,x2,x3,x4,x5,x6,x7] = data;
console.log(x0); → 1
console.log(x1); → 2
console.log(x2); → 3
console.log(x3); → 4
console.log(x4); → 5
console.log(x5); → 6
console.log(x6); → 7
console.log(x7); → 8
let data2 = [11,22,33,44,55,66,77,88,99]
let [y0,y1,y2,...other] = data2;
console.log(y0); → 11
console.log(y1); → 22
console.log(y2); → 33
console.log(other); → [44,55,66,77,88,99]
右辺の配列が個々の要素に分解されて、左辺のそれぞれに対応する変数x0、x1、・・・・、x7へ代入される。
配列要素を即座にバラバラに取り出せるのは、すごく良い!
今まで、逐一配列の要素にアクセスしてチマチマ変数に代入っていうのから開放されるですよ!!
これは、これから多用しちゃうでしょ!
カンマ演算子
こんなテクニックがあったとは!
コンマで区切られた式が先頭から順次実行される。
for(var i = 1, j = 1; i < 5; i++, j++){
console.log('i * j は' + i * j);
}
👇
i * jは1
i * jは4
i * jは9
i * jは16
テクニック編
小数を含む計算
console.log(0.2 * 3);
→ 0.6000000000000001
「0.6」じゃないの??
JavaScriptは数値を内部的に2進数で演算しているための誤差
厳密な結果を得るには・・・・
- 値を一時的に整数に変換
- 計算
- 小数に戻す
console.log(((0.2 * 10) * 3) / 10);
→ 0.6
これは、参考になる。よくよく数学的に考えると整合性がとれた素晴らしいやり方だと思う!
2022/08/13 追記
当テーマに対して、@d247h さんからより手数の少ない方法をコメントにていただきました!
toFixed()
メソッドを使用して、有効数字(桁)で丸める方法です。
小数を含む計算
小数点以下第何位まで欲しいのか決まっているなら、toFixedで誤差を丸めるのが楽かと思います。
@d247hさんご提案の方法console.log( +(0.2 * 3).toFixed(1) ); // 0.6 console.log( 9.87 / 10 ); // 0.9869999999999999 console.log( +(9.87 / 10).toFixed(3) ); // 0.987
有効数字が決まっている際は、有効な計算方法の1つだと思います!
ただ、toFixed
は、四捨五入で丸めるので2つ計算結果を比較する際は丸めることによる誤差が生じてしまう可能性を懸念する。
どんな言語でも言えることだけど、誤差を起こさせない計算方法を取ることが大事だと思っている。
割り算は特に誤差が起きやすい。
言語的にはJavaScriptはFortranやPythonのように四則演算を誤差なく計算する仕組みがなく、計算科学(物理現象のシミュレート)には向かないとよく言われている。
「9.97 / 10」は、上記でまとめた整数に変換して計算する方法でも誤差を排除しきれない
それでは、どうするか。
掛け算に変換して計算するのが良いかなと思っている。
これにより、誤差を排除できる!
console.log(9.87 / 10);
→ 0.9869999999999999
console.log(((9.87 * 100) / 10) / 100);
→ 0.9869999999999999
// 整数に変換して計算しても、計算途中で小数になるため誤差を生む。
// 掛け算にすることで誤差を排除する。
console.log(9.87 * 0.1);
→ 0.987
// 誤差なく計算可能
配列の列挙や順次走査について
配列の最終要素までいうことで、私は今まで以下のように記述していた・・・。
var data = [・・・・・・]
for (var i = 0; i < data.length; i++){
console.log(data[i]);
}
これだとループの度にプロパティにアクセスして処理効率が悪い!!
以下のように記述した方が良い(^^)!
var data = [・・・・・・]
for (var i = 0, len = data.length; i < len; i++){
console.log(data[i]);
}
for文の初期化式で配列のサイズ(data.length)を予め取得して、継続条件にはその取得した変数(len)を使用して判定する。
これにより都度プロパティへアクセスするコストを削減できて処理効率が高くなる。
JavaScriptの関数は引数の数はチェックされない
引数の数がチェックされないので、以下は全て正常に終了する。
function showMessage(value) {
console.log(value);
}
showMessage(); →undefined
showMessage("テスト"); →テスト
showMessage("テスト", "太郎"); →テスト
引数がなくても、引数の数が多くても問題なく稼働してしまう。非常に厄介な性質だ
ここで登場するのが、argumentsオブジェクトです。
argumentsオブジェクトは、関数の呼び出しのタイミングで作成されて呼び出し元から与えられた引数値が格納される。
この変数を利用することで、引数の数が違っている場合にエラーとして返却できる。
function showMessage(value) {
if (arguments.length !== 1) {
throw new Error("引数の数が違っています:" + arguments.length);
}
console.log(value);
}
// showMessage();
try {
showMessage("テスト"); ①
showMessage("テスト", "太郎"); ②
} catch (e) {
window: alert(e.message);
}
①は、正常終了する。
②は、引数の数が異なるためエラーとなる。
関数の引数にオブジェクトを渡す
function test({ objtest }) {
console.log(title);
}
let obj = {
id: "1",
title: "オブジェクトを関数の引数に渡す",
describe: "必要なプロパティが変わっても、呼び出し側に影響がない。",
};
test(obj);
関数の引数としてオブジェクト全体を受け取って、titleプロパティだけを分割代入により取得する。
プロパティがいっぱいあっても関数の呼び出し側で個々のプロパティを意識しなくてもオブジェクトをまるっと関数に渡せば良い。
関数内で必要なプロパティが変わっても呼び出し側のロジック変更は必要ないのが良い。
参考文献
- 山田祥寛、[改訂新版]JavaScript本格入門、技術評論社、2017年11月28日 初版 第4刷
- Number.prototype.toFixed()、2022/08/14 アクセス時点、https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed