JavaScript | 第13章「クラスの使用」

javascrpit JavaScript
スポンサーリンク

See the Pen JavaScript Class Animation Diagram by MONO365 -Color your days- (@monoqlo365) on CodePen.

import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";

// Default export: interactive React component that visualizes how JS classes work
// - Uses Tailwind CSS for styling (assumed available in host)
// - Uses framer-motion for animations
// - Shows steps: Class declaration, constructor, instance creation, prototype chain, inheritance, super(), static, private fields

export default function ClassAnimation() {
  const [step, setStep] = useState(0);

  const steps = [
    {
      id: 0,
      title: "クラスの宣言 (Class declaration)",
      desc: "class Color { constructor(r,g,b) { ... } } のようにクラスは“設計図”として定義されます。",
    },
    {
      id: 1,
      title: "コンストラクタとインスタンス (constructor & instance)",
      desc: "new Color(10,20,30) によりインスタンスが生成され、constructor 内の初期化処理が走ります。",
    },
    {
      id: 2,
      title: "プロトタイプチェーン (prototype chain)",
      desc: "インスタンスはクラスの prototype を参照し、メソッドは prototype 上で共有されます。",
    },
    {
      id: 3,
      title: "継承 (extends / super)",
      desc: "class ColorWithAlpha extends Color は親の機能を引き継ぎ、super() で親の constructor を呼べます。",
    },
    {
      id: 4,
      title: "静的メソッド / プライベートフィールド (static / #private)",
      desc: "static はクラス自体に属し、# はプライベートで外部から参照できません。",
    },
  ];

  const next = () => setStep((s) => Math.min(s + 1, steps.length - 1));
  const prev = () => setStep((s) => Math.max(s - 1, 0));

  return (
    <div className="p-6 max-w-5xl mx-auto">
      <h1 className="text-2xl font-bold mb-4">クラスの内部をアニメーションで理解する</h1>
      <p className="mb-4 text-sm text-gray-600">
        ステップを進めると、クラス定義 → インスタンス生成 → プロトタイプチェーン → 継承 の流れが
        アニメーションで表示されます。
      </p>

      <div className="flex gap-4 mb-6">
        <div className="flex-1 bg-white rounded-2xl shadow p-4">
          <h2 className="font-semibold">ステップ</h2>
          <div className="mt-3">
            {steps.map((s) => (
              <button
                key={s.id}
                onClick={() => setStep(s.id)}
                className={`block w-full text-left px-3 py-2 my-1 rounded ${
                  s.id === step ? "bg-sky-100 border-l-4 border-sky-500" : "hover:bg-gray-50"
                }`}
              >
                <div className="text-sm font-medium">{s.title}</div>
                <div className="text-xs text-gray-500">{s.desc}</div>
              </button>
            ))}
          </div>

          <div className="mt-4 flex gap-2">
            <button onClick={prev} className="px-3 py-2 rounded bg-gray-100">戻る</button>
            <button onClick={next} className="px-3 py-2 rounded bg-sky-500 text-white">進む</button>
            <div className="ml-auto text-xs text-gray-500 self-center">Step {step + 1}/{steps.length}</div>
          </div>
        </div>

        <div className="flex-1 bg-white rounded-2xl shadow p-4">
          <h2 className="font-semibold">解説</h2>
          <div className="mt-3 text-sm text-gray-700">
            <strong>{steps[step].title}</strong>
            <p className="mt-2">{steps[step].desc}</p>

            {/* show code snippets per step */}
            <div className="mt-3">
              <CodeSnippet step={step} />
            </div>
          </div>
        </div>
      </div>

      {/* visualization area */}
      <div className="bg-white rounded-2xl shadow p-6 h-96 relative overflow-hidden">
        <AnimatePresence mode="wait">
          {step === 0 && (
            <motion.div
              key="s0"
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -20 }}
              transition={{ duration: 0.4 }}
              className="h-full flex items-center justify-center"
            >
              <div className="flex gap-6 items-center">
                <Card title="class Color" subtitle="設計図(関数オブジェクト)">
                  <pre className="text-xs">constructor(r,g,b) {'{'} this.r = r; this.g = g; this.b = b; {'}'}
                  </pre>
                </Card>
                <Arrow right />
                <div className="text-sm text-gray-600">クラス宣言は内部的に「関数オブジェクト」として作られる</div>
              </div>
            </motion.div>
          )}

          {step === 1 && (
            <motion.div
              key="s1"
              initial={{ opacity: 0, scale: 0.95 }}
              animate={{ opacity: 1, scale: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 0.4 }}
              className="h-full flex items-center justify-center"
            >
              <div className="flex items-center gap-8">
                <Card title="class Color" subtitle="設計図">
                  <pre className="text-xs">constructor(r,g,b) {'{'} this.r = r; this.g = g; this.b = b; {'}'}
                  </pre>
                </Card>

                <motion.div
                  initial={{ x: -100, opacity: 0 }}
                  animate={{ x: 0, opacity: 1 }}
                  transition={{ duration: 0.6 }}
                >
                  <Arrow right />
                </motion.div>

                <motion.div
                  initial={{ scale: 0.8, opacity: 0 }}
                  animate={{ scale: 1, opacity: 1 }}
                  transition={{ duration: 0.6 }}
                >
                  <Card title="instance: c" subtitle="new Color(10,20,30)">
                    <pre className="text-xs">{`{ r:10, g:20, b:30 }`}</pre>
                  </Card>
                </motion.div>
              </div>
            </motion.div>
          )}

          {step === 2 && (
            <motion.div
              key="s2"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              className="h-full flex items-center justify-center"
            >
              <div className="flex gap-8 items-start">
                <div>
                  <Card title="instance: c" subtitle="{ r:10, g:20, b:30 }" small>
                    <pre className="text-xs">(own properties)</pre>
                  </Card>
                  <div className="mt-6 flex items-center justify-center">
                    <Arrow down />
                  </div>
                </div>

                <div className="flex flex-col items-center">
                  <Card title="Color.prototype" subtitle="メソッドがここにある">
                    <pre className="text-xs">getHex() {'{'} ... {'}'}</pre>
                  </Card>

                  <div className="mt-6 flex items-center justify-center">
                    <Arrow down />
                  </div>

                  <Card title="Object.prototype" subtitle="最上位のプロトタイプ">...</Card>
                </div>

                <div className="ml-6 text-sm text-gray-600 max-w-xs">
                  <p>プロパティ参照時、まずインスタンス自身を見て、なければ prototype を上へ辿ります。これがプロトタイプチェーンです。</p>
                </div>
              </div>
            </motion.div>
          )}

          {step === 3 && (
            <motion.div
              key="s3"
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -20 }}
              className="h-full flex items-center justify-center"
            >
              <div className="flex gap-8 items-center">
                <div>
                  <Card title="class Color" subtitle="親クラス" small>
                    <pre className="text-xs">constructor(r,g,b){...}</pre>
                  </Card>
                </div>

                <div>
                  <Arrow right />
                </div>

                <div>
                  <Card title="class ColorWithAlpha extends Color" subtitle="子クラス">
                    <pre className="text-xs">constructor(r,g,b,a){ super(r,g,b); this.a=a; }</pre>
                  </Card>
                </div>
              </div>
            </motion.div>
          )}

          {step === 4 && (
            <motion.div
              key="s4"
              initial={{ opacity: 0, scale: 0.98 }}
              animate={{ opacity: 1, scale: 1 }}
              exit={{ opacity: 0 }}
              className="h-full flex items-center justify-center"
            >
              <div className="flex gap-8 items-center">
                <Card title="Color" subtitle="静的メソッド & プライベート">
                  <pre className="text-xs">static isValid(){...}\n#values = [r,g,b]</pre>
                </Card>

                <div className="ml-4 text-sm text-gray-600 max-w-md">
                  <p>- <strong>static</strong> はクラス自体に紐づく。<br />- <strong>#</strong> はプライベートで外から参照できない。</p>
                </div>
              </div>
            </motion.div>
          )}
        </AnimatePresence>
      </div>

      <div className="mt-6 text-sm text-gray-500">ヒント: 各ステップのコードをよく読んで、実際にブラウザのコンソールで試してみましょう。</div>
    </div>
  );
}


// Small helper components used in the visualization
function Card({ title, subtitle, children, small }) {
  return (
    <div className={`rounded-xl p-4 shadow-md ${small ? "w-56" : "w-72"} bg-white border`}>
      <div className="text-sm font-medium">{title}</div>
      <div className="text-xs text-gray-500">{subtitle}</div>
      <div className="mt-2 text-xs text-slate-700 whitespace-pre-wrap">{children}</div>
    </div>
  );
}

function Arrow({ right, down }) {
  if (right) {
    return (
      <div className="flex items-center">
        <svg width="60" height="24" viewBox="0 0 60 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M2 12 H48" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" />
          <path d="M40 6 L48 12 L40 18" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </div>
    );
  }
  if (down) {
    return (
      <div className="flex items-center">
        <svg width="24" height="60" viewBox="0 0 24 60" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M12 2 V48" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" />
          <path d="M6 40 L12 48 L18 40" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </div>
    );
  }
  return null;
}
HTML

エディタにプレビューできる React コンポーネント(JS Classes — アニメーション図解)を用意してあります。
左のステップ一覧で切り替えながら、クラス宣言 → インスタンス生成 → プロトタイプチェーン → 継承 → static/private をアニメーションで確認できます。

タイトルとURLをコピーしました