Cay S. Horstmann : JavaScript モダンプログラミング完全ガイド

作成日 : 2023-04-12
最終更新日 :

概要

「なぜこの本を書いたのか」から引用する。

Java や、それに似た言語を知っていて、 JavaScript をいまの姿で(歴史的な残滓を除いた形で)学びたいと思っているプログラマは何百万人もいるだろうに、そういう人たちのための本を見つけることができないのは、おかしなことではないか。

だから、私がこの本を書くことになった。

感想

まえがきにある、5 つの大原則は大事だと思う。以下引用する。

  1. 変数は、var でなく、let または const で宣言せよ。
  2. strict モードを使え。
  3. 型を理解し、自動的な型変換を避けよ。
  4. プロトタイプを理解しよう。しかし、クラスとコンストラクタとメソッドには現在の構文を使おう。
  5. this は、コンストラクタまたはメソッドの中でだけ使おう。

難しい

私は頭が悪いので、本書を読み進めるのが難しかった。

演習問題 第4章第8番

各章の末尾には演習問題がある。第4章「オブジェクト指向プログラミング」の演習問題の 8 番 (p.109) をやってみた。

8 抽象クラスの古典的な例が、ツリー(木構造)のノードである。 ノードには、子供のある親ノードと子供のない葉ノードの2種類がある(コードブロックの引用は割愛)。 Java や C++ では、このようにツリーノードをモデリングするだろう。けれどお JavaScript では、 n.depth() を呼び出せるようにするのに、抽象クラスは必要ない。継承なしでクラスを書き直し、 テストプログラムも作ろう。

書き直してみてテストプログラムも作ったが、果たしてこれでいいのだろうか。

コード

/* Cay S. Horstmann : JavaScript モダンプログラミング完全ガイド */
/* p.109 ekz08.js */
"use strict";
class Node {
  constructor(value, children) {
    this.value = value;
    this.children = children;
  }
  depth() {
    return this.children === undefined
      ? 1
      : 1 + Math.max(...this.children.map((n) => n.depth()));
  }
}

const tarao = new Node("Tarao");
const sazae = new Node("Sazae", [tarao]);
const katuo = new Node("Katuo");
const wakame = new Node("Wakame");
const fune = new Node("Fune", [sazae, katuo, wakame]);

console.log(`tarao : ${tarao.depth()}`);
console.log(`sazae : ${sazae.depth()}`);
console.log(`katuo : ${katuo.depth()}`);
console.log(`wakame : ${wakame.depth()}`);
console.log(`fune : ${fune.depth()}`);

実行結果

PS C:> node .\ekz08.js
tarao : 1
sazae : 2
katuo : 1
wakame : 1
fune : 3

本書の記述や例を参考にしたページを作った。正規表現とパターンマッチングなど。

演習問題 第5章第11番

久しぶりに本書を読み返した。第5章「数と日付/時刻」の演習問題の 11 番 (p.127) をやってみた。

11 ある年が「うるう年」()かどうかを判定する関数を、2 種類の異なる実装で書こう。

ここで、所与の年は西暦 y であるとする。方針はいくつか考えられる。

  1. y が 400 で割り切れればうるう年、そうではなく 100 で割り切れれば平年(非うるう年)、そうではなく 4 で割り切れればうるう年、どれでもなければ平年。
  2. y の日数を調べ、365 なら平年、366 ならうるう年。
  3. y の 2 月の月数を調べ、28 なら平年、29 ならうるう年。

第1の方針であれば、次のようになるだろうか。

function isLeapYear(y) {
  if (y % 400 === 0) {
    return true
  } else if (y % 100 === 0) {
    return false
  } else if (y % 4 === 0) {
    return true
  } else {
    return false
  }
}

第2の方針であれば、次のようになるだろうか。

function isLeapYear(y) {
  const before = new Date(y + 1, 0)
  const after = new Date(y, 0)
  return (before - after) / 86400000 === 365 ? true : false 
}

この第2の方針の実装には、本書 p.122 のコラムにある第2の注意を参考にした。

注意:Date オブジェクトを算術演算の式に遣うと、上記の「注意」と同じフォーマットの文字列か、エポックからのミリ秒数への、自動的な変換が行なわれる。 (中略)これが有益なのは、2つの時刻の差を計算するときだけだ。

第3の方針の実装は最初やめるつもりでいた。2 種類の異なる実装で書こう とあったからだ。しかし、面白いので、書いてみた。

function isLeapYear(y) {
  return (new Date(y, 2, 0).getDate() === 29) ? true : false 
}

この実装には、本書 p.123 の注意を参考にした。

注意:もし zeroBasedMonth、day、hours などに範囲外の値を提供すると、日付時刻は黙って調整される。たとえば、new Date(2019, 13, -2) は 2020 年 1 月 29 日になる。

この黙って調整されるという箇所の調整の具体的な内容が、例だけではわかりにくかったので、https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/Date を見てみると、次のように説明されていた。

いずれかの引数が定義された境界を超えた場合、「繰り上げ」が行われます。例えば、 monthIndex に 11 よりも大きな値が渡された場合、その月は年を増加させます。 minutes に 59 よりも大きな値が渡された場合、hours はそれに応じて増加します。したがって、 new Date(1990, 12, 1) は 1991 年 1 月 1 日を返し、 new Date(2020, 5, 19, 25, 65) は 2020 年 6 月 20 日の午前 2 時 5 分を返します。

同様に、何か引数がアンダーフローする場合は、上位の引数を「桁借り」します。例えば、new Date(2020, 5, 0) は、 2020 年 5 月 31 日を返します。

この伝でいくと、new Date(y, 2, 0) が y 年 3 月 1 日の 1 日前、すなわち y 年 2 月 28 日か 29 日かを表すことがわかる。そこで getDate() メソッドを使って、29 か否かを判定すればうるう年か否かがわかることになる。

(この項、2025-11-04)

リンク

(この項、2025-11-04)

書誌情報

書名 JavaScript モダンプログラミング完全ガイド
著者 Cay S. Horstmann
訳者 吉川邦夫
発行日 2020 年 12 月 21 日 初版第1刷
発行元 インプレス
定価 3000 円(本体)
サイズ A5 判変形 ページ
ISBN 978-4-295-01056-2
その他 越谷市立図書館で借りて読む

まりんきょ学問所コンピュータについてコンピュータの本JavaScript, altJS > Cay S. Horstmann:JavaScript モダンプログラミング完全ガイド


MARUYAMA Satosi