- 技術 💻
プロトタイプとは|意味・MVPとの違い・JavaScriptのプロトタイプを解説
結論
プロトタイプとは「試作品・原型」のことで、作る前に確かめるための仮の形です。JavaScript では、オブジェクトが機能を借りる「親のオブジェクト」を指します。
この記事でわかること
- ITでの意味の見分け表と、PoC・MVPなど4語との違い
- プロトタイピングの5手順と、27行のHTMLの試作品
- プロトタイプチェーンと class の関係をNode.jsで9例
「プロトタイプ」は、使う場面によって指すものが変わります。開発やデザインでは「試作品」、JavaScript では「オブジェクトの親」、C 言語では「関数の型の宣言」です。
flowchart LR
A[プロトタイプ<br>最初の型・原型] --> B[開発・デザイン<br>試作品]
A --> C[JavaScript<br>オブジェクトの親]
A --> D[C言語<br>関数の宣言]
まず、自分が調べている「プロトタイプ」がどれなのかを見分けます。
| 場面 | プロトタイプの意味 | 詳しい節 |
|---|---|---|
| システム・アプリ開発 | 本番の前に作る、動く試作品 | 開発のプロトタイプ |
| UI デザイン(Figma など) | 画面をつなぎ、操作を試せる模型 | ツール |
| JavaScript | オブジェクトが機能を借りる親 | JavaScript のプロトタイプ |
| 開発手法 | 試作と評価を繰り返すプロトタイプモデル | プロトタイプモデル |
| C 言語・C++ | 関数の引数と戻り値の型の宣言 | プロトタイプ宣言 |
| 車・家電・ゲームなど | 量産の前の試作機・試作車 | 意味と語源 |
確認環境:Ubuntu 24.04.4 LTS。JavaScript のプロトタイプは Node.js 22.22.2 で9つのスクリプトを実行し、出力・エラー文・メモリの量を載せました(メモリは --expose-gc で GC を走らせてから process.memoryUsage() で計測)。HTML のプロトタイプは Chromium 141.0.7390.37(playwright-core 1.64.1 から起動、画面幅 390px)でクリックして画面の移り変わりを確かめました。C 言語のプロトタイプ宣言は gcc 13.3.0 でコンパイル・実行しました。言葉の意味は Weblio(IT用語辞典バイナリ)と NTT東日本 Biz Drive の用語解説、JavaScript の仕様は MDN Web Docs、--disable-proto は Node.js の公式ドキュメント、Figma のプロトタイプ機能は Figma ヘルプセンター、GCC 14 の変更は GCC の公式ページで確かめました(確認日 2026年10月23日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「プロトタイプとは|意味・MVPとの違い・JavaScriptのプロトタイプを解説」
プロトタイプとは(意味・語源・読み方)
この節では、プロトタイプという言葉のもとの意味と、IT でどう使われるかが分かります。
プロトタイプ(prototype)は、製品の原型や試作品のことです。 IT用語辞典バイナリは「ある製品の原型あるいは試作品のこと」と説明し、システム開発では、実際に近いシステムのイメージを試作品として見せ、利用者の意見や要求をはっきりさせる、としています。
- 語源:NTT東日本 Biz Drive の用語解説によると、「prot」はギリシャ語の protos(最初の)から来ていて、type(型)と合わさって「最初の型=原型」という意味になりました。
- 読み方:英語の発音は /ˈproʊtəˌtaɪp/(プロウタタイプ)。日本語では「プロトタイプ」、略して「プロト」と言うこともあります。
- 反対の言葉:試作品に対して、量産して売る形は「量産品」「製品版」と呼びます。
身近なたとえで言うと、プロトタイプは「家を建てる前の模型」や「料理の試食」です。全部作ってから「思っていたのと違う」と分かると、やり直しに大きな手間がかかります。小さく作って見せ、早いうちにずれを見つけるのがプロトタイプの役目です。
1開発・デザインの試作品
画面の模型や、一部だけ動くアプリ。本番の前に、使う人に触ってもらって意見をもらう。
2JavaScript のプロトタイプ
オブジェクトがつながっている「親」のオブジェクト。自分に無い機能は親から借りる。
3C 言語のプロトタイプ宣言
関数を使う前に、引数と戻り値の型をコンパイラに知らせる1行。
3つは別の話ですが、「最初に置く型」という意味は共通しています。 試作品は製品の型、JavaScript のプロトタイプは新しいオブジェクトの型、プロトタイプ宣言は関数の型です。この記事は前半で開発の試作品、後半で JavaScript のプロトタイプを扱います。
開発のプロトタイプを作る目的とメリット
この節では、システム開発でなぜ試作品を作るのか、何が得られるのかが分かります。
開発のプロトタイプの目的は、作り始める前に「本当にこれでよいか」を、安く早く確かめることです。 文章の仕様書だけでは、発注した人・使う人・作る人が、それぞれ違う画面を思い浮かべていることがあります。動く形を見せると、そのずれがすぐに表に出ます。
| 目的 | 確かめること | 例 |
|---|---|---|
| 認識をそろえる | 関係者が同じものを思い浮かべているか | 画面の流れを見せて「この順番で合っていますか」 |
| 使いやすさを試す | 迷わずに操作できるか | 5人に予約の操作をしてもらう |
| 技術を試す | その方法で実現できるか、速さは足りるか | 外部 API を1本だけつないでみる |
| 需要を試す | 欲しいと思う人がいるか | 見本を見せて反応を集める |
| 見積もりを固める | どこが難しいか、何が要るか | 試作で分かった難所を工数に反映 |
主なメリットです。
- 手戻りが減る:作ってから直すより、試作の段階で直すほうが変更の量が小さく済みます。
- 言葉で説明しにくい要求が出てくる:「ここにも検索が欲しい」のような要求は、触ってみて初めて出てくることが多いです。
- 判断が早くなる:複数の案を試作して比べると、会議で言葉だけで比べるより決めやすくなります。
PoC・MVP・モックアップ・ワイヤーフレームとの違い
この節では、プロトタイプと混同しやすい4つの言葉との違いが、1つの表で分かります。
いちばん大きな違いは「何を確かめるために作るか」です。 ワイヤーフレームとモックアップは画面の設計図と見た目、プロトタイプは操作の流れ、PoC は技術的にできるか、MVP は市場で売れるかを確かめます。
| 名前 | 確かめること | 動くか | 主に見せる相手 |
|---|---|---|---|
| ワイヤーフレーム | 画面に何をどこへ置くか | 動かない(線と箱) | チーム・発注者 |
| モックアップ | 色・文字・画像を入れた見た目 | 動かない(静止画) | 発注者・デザイナー |
| プロトタイプ | 操作の流れ・使いやすさ | 画面遷移などが動く | 利用者・発注者 |
| PoC(概念実証) | 技術的に実現できるか | 核の部分だけ動く | 開発チーム・決裁者 |
| MVP(実用最小限の製品) | 市場で使われるか・売れるか | 本物として動く | 実際の利用者 |
会議室予約アプリを例にすると
PoC なら「社内の予定表システムと API でつながるか」だけを確かめる
順番の目安は「ワイヤーフレーム → モックアップ → プロトタイプ → MVP」で、PoC は技術に不安があるときに途中で行います。ただし、全部を必ず作るわけではありません。小さなアプリなら、ワイヤーフレームから直接プロトタイプを作ることもあります。
MVP の作り方と PoC との違いは、関連記事「MVPとは|PoC・プロトタイプとの違いと作り方」で扱っています。ワイヤーフレームの描き方は、関連記事「ワイヤーフレームの書き方:作り方の手順とテキストで作る例・テンプレート」で扱っています。
プロトタイプの種類(忠実度・使い捨て型と進化型)
この節では、プロトタイプを「どれくらい本物に近いか」と「作った後どうするか」の2つの軸で分けられます。
忠実度で分ける:ローファイとハイファイ
本物にどれくらい近いかを「忠実度(フィデリティ)」と言います。 低いものをローファイ、高いものをハイファイと呼びます。
| 比べる点 | ローファイ(低忠実度) | ハイファイ(高忠実度) |
|---|---|---|
| 形 | 紙の手描き・線と箱の画面 | 色・文字・画像が本番に近い画面 |
| 作る時間 | 数分〜数時間 | 数日〜数週間 |
| 向いている段階 | 最初。案をたくさん出して比べる | 後半。細かい操作や見た目を確かめる |
| もらえる意見 | 流れ・項目の過不足 | 文字の読みやすさ・ボタンの位置・動き |
| 注意点 | 見た目の意見はもらえない | 完成品と思われやすい |
使い捨て型(スローアウェイ)
確かめたら捨て、本番は作り直す
早く作ることを優先。コードの品質は問わない
例:紙の試作、Figma の画面、1日で書いた HTML
進化型(エボリューショナリー)
試作を少しずつ直して、本番へ育てる
最初から本番と同じ技術・品質で作る
例:MVP として出し、使われながら機能を足す
どちらにするかは、作る前に決めておきます。 使い捨てのつもりで急いで書いたコードが、そのまま本番になると、テストや設計の不足が後で問題になります。
見せ方で分ける:3つの種類
上位の解説記事でよく使われる分け方です。
| 種類 | 何を見せるか | 例 |
|---|---|---|
| ファンクショナル(機能) | 機能が動くか | 計算・検索だけ動く画面 |
| デザイン(見た目) | 画面の見た目・操作感 | Figma のクリックで動く画面 |
| コンテクスチュアル(利用場面) | 使う場面の流れ | 使っている様子の動画・紙芝居 |
プロトタイピングの進め方(5つの手順)
この節では、プロトタイプを作って確かめる流れ(プロトタイピング)を、手順どおりに進められます。
確かめたい問いを1つ決める
「予約まで迷わず3回のクリックで進めるか」のように、答えが出る形で書く。問いが無いと、作っても何も分からない。
範囲と忠実度を決める
問いに関係する画面だけを作る。流れを見るならローファイ、見た目を見るならハイファイ。使い捨てか進化型かもここで決める。
時間を区切って作る
データは仮のもの、保存はしない、エラー処理は無し、と割り切る。作り込みすぎると、意見をもらう前に時間がなくなる。
使う人に触ってもらう
説明せずに操作してもらい、迷った場所・止まった場所・声に出した感想を記録する。作った人の説明が要る所が直す候補。
直すか、捨てるか、進むかを決める
分かったことを要件や設計書に書き戻す。問いの答えが出たら次へ進み、出なければ直して手順4に戻る。
大事なのは、手順1の「問い」と手順5の「書き戻し」です。 試作品そのものは捨てても、分かったこと(どこで迷ったか、何が足りなかったか)は要件定義書や画面設計書に残します。アジャイル開発では、この「作って確かめる」流れを短い期間で何度も回します。
プロトタイプを作るツール(紙・Figma・HTML・AI)
この節では、プロトタイプを何で作るか、段階ごとの選び方が分かります。
| 道具 | 向いている段階 | 特徴 |
|---|---|---|
| 紙とペン・付箋 | 最初の案出し | 数分で描ける。誰でも参加できる |
| ホワイトボード・Excalidraw | 画面の流れの検討 | 手描き風の図を共有しながら直せる |
| Figma | 見た目と操作の確認 | 画面をつなぎ、クリックで動かせる |
| Penpot | 同上(オープンソース) | オープンソースで、自分のサーバーにも置ける |
| HTML・CSS・JavaScript | 本番に近い操作の確認 | ブラウザで動く。進化型にもできる |
| 生成AI(v0 など) | 画面の案を素早く出す | 文章から画面のコードを作る |
Figma のプロトタイプ機能の用語
Figma のヘルプセンターでは、Figma のプロトタイプを「利用者がデザインの中をどう進むかを見せる、操作できるフローの集まり」と説明しています。設定に出てくる言葉は次のとおりです。
| 用語 | 意味 |
|---|---|
| ホットスポット | 操作のきっかけになる部分(ボタン・リンク・画像など) |
| コネクション | ホットスポットから行き先へ引く矢印 |
| トリガー | 進むための操作(タップ・クリック・ホバー・ドラッグなど) |
| アクション | 進み方(別のフレームへ移る・外部の URL を開くなど) |
| アニメーション | 切り替わり方。長さは 1 ミリ秒〜10 秒 |
| フローと開始点 | つながった画面のまとまりと、その最初の画面 |
| オーバーレイ | 今の画面の上に重ねて出す画面(メニュー・確認など) |
作ったプロトタイプは、閲覧の権限がある人がプレゼンテーションの画面で再生でき、コメントで意見を残せます。手描きの図は、関連記事「Excalidrawとは・使い方|ショートカット・書き出し・料金を実例で解説」で扱っています。AI で画面のコードを作る方法は、関連記事「v0とは|使い方・料金・ローカルで動かす手順をNext.jsで実演」で扱っています。
HTMLで作る動くプロトタイプ(27行・JavaScriptなし)
この節では、HTML と CSS だけで「クリックで画面が切り替わる」試作品を作り、ブラウザで実際に動いた結果を見られます。
会議室予約アプリの「一覧 → 詳細 → 予約完了」の3画面です。予約は保存されず、データも仮のものです。確かめたいのは「迷わず予約までたどり着けるか」だけなので、それ以外は作りません。
<!doctype html>
<html lang="ja">
<meta charset="utf-8">
<title>会議室予約(プロトタイプ)</title>
<style>
body { font-family: sans-serif; max-width: 360px; margin: 16px auto; }
section { display: none; } section:target, section.home { display: block; }
:target ~ section.home { display: none; }
button, a.btn { display: block; width: 100%; padding: 12px; margin: 8px 0; }
</style>
<section id="detail">
<h1>会議室A(6人)</h1>
<p>空き:10:00〜11:00 / 14:00〜15:00</p>
<a class="btn" href="#done">14:00〜15:00 で予約する</a>
<a href="#">← 一覧に戻る</a>
</section>
<section id="done">
<h1>予約しました</h1>
<p>会議室A 14:00〜15:00</p>
<a href="#">一覧に戻る</a>
</section>
<section class="home">
<h1>空いている会議室</h1>
<a class="btn" href="#detail">会議室A(6人) 空き2枠</a>
<a class="btn" href="#detail">会議室B(12人) 空き1枠</a>
</section>
</html>
URL の #detail のような部分(フラグメント)が指す画面だけを、CSS の :target で表示しています。一覧の画面は最後に置き、ほかの画面が表示されているときは :target ~ section.home で隠します。
Chromium で開き、利用者と同じ順にクリックした結果です(playwright-core で操作)。
Chromium 141.0.7390.37
開いた直後 URL末尾=(なし) 見出し=「空いている会議室」
会議室Aを押す URL末尾=#detail 見出し=「会議室A(6人)」
予約を押す URL末尾=#done 見出し=「予約しました」
一覧に戻る URL末尾=(なし) 見出し=「空いている会議室」
JavaScript の行数: 0 / ページのエラー: 0
この試作品を5人に触ってもらい、「会議室B を押したのに会議室A が出た」と言われたら、それが1つ目の学びです。 仮のデータで両方とも同じ詳細画面に飛ぶのは試作品の割り切りですが、利用者が気にする所だと分かります。このように、プロトタイプは「わざと作っていない所」をはっきりさせて見せます。
プロトタイプのデメリットと失敗しやすい点
この節では、プロトタイプで起きやすい失敗と、その防ぎ方が分かります。
| 失敗 | 起きること | 防ぎ方 |
|---|---|---|
| 作り込みすぎる | 意見をもらう前に時間と費用を使い切る | 問いに関係する画面だけ。時間を区切る |
| 完成品だと思われる | 「もうできているのに、なぜ数か月かかるの」 | 試作品であること・作っていない所を先に伝える |
| 試作のコードが本番になる | テスト・エラー処理・安全対策が無いまま公開 | 使い捨てか進化型かを最初に決める |
| 意見が多すぎて広がる | 要望を全部入れて、範囲が膨らむ | 問いに関係する意見と、それ以外を分ける |
| 学びが残らない | 次の工程で同じ議論をくり返す | 要件定義書・画面設計書に書き戻す |
| 少人数の評価をうのみにする | 一部の人の好みで決めてしまう | 試す人の数と立場を記録し、結論の重みを決める |
NTT東日本 Biz Drive の用語解説も、小さな試行で大きなサービスの成功を見通すのは難しいこと、関わる部署や会社が多いと調整の手間が増えることを、デメリットとして挙げています。
- 1確かめたい問いが1文で書けているか「使いやすいか」ではなく「予約まで迷わず進めるか」のように答えが出る形
- 2作らない所を決めて、見せる相手に伝えたか保存しない・データは仮・エラー処理なし、など
- 3使い捨てか進化型かを決めたか使い捨てなら、本番では作り直す前提で見積もる
- 4本物の個人情報や秘密のデータを使っていないか外部の人に見せる試作品は、仮のデータで作る
- 5分かったことを設計書に書き戻したか試作品を捨てても、学びは残す
プロトタイプモデル(開発手法)とは
この節では、試作と評価をくり返す開発の進め方「プロトタイプモデル」と、ほかの手法との違いが分かります。
プロトタイプモデル(プロトタイピングモデル)は、開発の早い段階で試作品を作って利用者に評価してもらい、要求を固めてから本番の開発に進む手法です。 要求があいまいなシステムで、後から大きな手戻りが出るのを防ぎます。
flowchart TD
A[要求を聞く] --> B[試作品を作る]
B --> C[利用者が評価する]
C -- 直す所がある --> B
C -- 要求が固まった --> D[設計・実装・テスト]
D --> E[リリース]
| 手法 | 進め方 | 向いている場合 |
|---|---|---|
| ウォーターフォール | 要件→設計→実装→テストを順に1回 | 要求がはっきりしていて変わりにくい |
| プロトタイプモデル | 試作と評価で要求を固めてから開発 | 要求があいまい・画面の操作が大事 |
| スパイラルモデル | 試作を含む工程を何周も回して育てる | 大きくて危険の多いシステム |
| アジャイル | 短い期間で動くものを何度も出す | 要求が変わりやすい・早く出したい |
表のとおり、プロトタイプモデルは「要求を固める」ところに試作を使い、アジャイルは「動くものを出し続ける」ことで要求の変化に合わせます。
JavaScriptのプロトタイプとは(オブジェクトの「親」)
この節から後半です。JavaScript の「プロトタイプ」が何で、なぜ [].map() のように自分で作っていない機能が使えるのかが分かります。
JavaScript では、すべてのオブジェクトが「プロトタイプ」と呼ばれる別のオブジェクトへのつながりを1本持っています。 自分が持っていないプロパティやメソッドを使おうとすると、JavaScript はプロトタイプの中を探しに行きます。MDN は、このつながりを仕様の書き方にならって [[Prototype]] と書き、Object.getPrototypeOf() で読み、Object.setPrototypeOf() で変えられる、と説明しています。
たとえると、プロトタイプは「分からないことを聞きに行く親」です。子が知らないことは親に聞き、親も知らなければ祖父母に聞きます。誰も知らなければ「分からない(undefined)」です。
// 1) 見つからないプロパティはプロトタイプを探しに行く
const animal = { eats: true, walk() { return `${this.name} が歩く`; } };
const rabbit = Object.create(animal); // rabbit のプロトタイプを animal にする
rabbit.name = "うさぎ";
console.log("rabbit :", rabbit);
console.log("rabbit.eats :", rabbit.eats);
console.log("rabbit.walk() :", rabbit.walk());
console.log("自分の持ち物か eats:", Object.hasOwn(rabbit, "eats"));
console.log("自分の持ち物か name:", Object.hasOwn(rabbit, "name"));
console.log("プロトタイプは animal:", Object.getPrototypeOf(rabbit) === animal);
console.log("rabbit.flies :", rabbit.flies);
Node.js 22.22.2 で実行した出力です。
rabbit : { name: 'うさぎ' }
rabbit.eats : true
rabbit.walk() : うさぎ が歩く
自分の持ち物か eats: false
自分の持ち物か name: true
プロトタイプは animal: true
rabbit.flies : undefined
rabbit 自身が持っているのは name だけですが、eats と walk() も使えました。 どちらも親の animal から借りています。walk() の中の this は、借りた先の animal ではなく、呼び出した rabbit を指すので「うさぎ が歩く」になります。どこにも無い flies は undefined です。
プロトタイプチェーンの仕組み(Node.jsで確かめる)
この節では、親の親…とたどる「プロトタイプチェーン」が、身近な値でどうつながっているかが分かります。
プロトタイプにもプロトタイプがあり、この連なりをプロトタイプチェーンと呼びます。 MDN によると、チェーンは null をプロトタイプに持つオブジェクトまで続き、null が最後の輪になります。上の rabbit をたどると、次のようになりました。
チェーン: rabbit → animal → Object.prototype → null
数値・文字列・配列・関数など、よく使う値のチェーンをたどったスクリプトと出力です。
function chain(v) {
const out = [];
for (let p = Object.getPrototypeOf(v); p !== null; p = Object.getPrototypeOf(p)) {
out.push((Object.hasOwn(p, "constructor") ? p.constructor.name : "?") + ".prototype");
}
return out.concat("null").join(" → ");
}
const samples = [
["{}", {}], ["[1,2]", [1, 2]], ["\"abc\"", "abc"], ["42", 42], ["true", true],
["() => {}", () => {}], ["new Date()", new Date()], ["new Map()", new Map()],
["/a/", /a/], ["Promise.resolve()", Promise.resolve()], ["new TypeError()", new TypeError()],
];
for (const [label, v] of samples) console.log(label.padEnd(18), chain(v));
console.log('[].map === Array.prototype.map :', [].map === Array.prototype.map);
console.log('Object.hasOwn([1,2], "map") :', Object.hasOwn([1, 2], "map"));
console.log('Object.getOwnPropertyNames(Array.prototype).length:', Object.getOwnPropertyNames(Array.prototype).length);
{} Object.prototype → null
[1,2] Array.prototype → Object.prototype → null
"abc" String.prototype → Object.prototype → null
42 Number.prototype → Object.prototype → null
true Boolean.prototype → Object.prototype → null
() => {} Function.prototype → Object.prototype → null
new Date() Date.prototype → Object.prototype → null
new Map() Map.prototype → Object.prototype → null
/a/ RegExp.prototype → Object.prototype → null
Promise.resolve() Promise.prototype → Object.prototype → null
new TypeError() TypeError.prototype → Error.prototype → Object.prototype → null
[].map === Array.prototype.map : true
Object.hasOwn([1,2], "map") : false
Object.getOwnPropertyNames(Array.prototype).length: 40
配列の map は配列自身ではなく、親の Array.prototype が持っています。 Array.prototype には map・filter・push など40個のプロパティがあり、すべての配列がそれを共有しています。文字列や数値のような値(プリミティブ)も、使うときに String.prototype などのメソッドを借ります。TypeError は Error.prototype を経由するので、チェーンが1段長くなっています。
flowchart TD
A[tama<br>自分の name] --> B[Cat.prototype<br>meow]
B --> C[Animal.prototype<br>walk]
C --> D[Object.prototype<br>toString など]
D --> E[null<br>ここで終わり]
上の図は、後の「class とプロトタイプの関係」で作る tama(Cat のインスタンス)のチェーンです。tama.toString() を呼ぶと、tama → Cat.prototype → Animal.prototype と探し、Object.prototype で見つかります。
同じ名前を自分に付けると親のものは隠れる
class Counter { constructor() { this.n = 0; } }
const a = new Counter(), b = new Counter();
Counter.prototype.hello = function () { return "後から追加したメソッド"; };
console.log("a.hello():", a.hello(), "/ b.hello():", b.hello());
a.hello = () => "a だけの hello"; // 自分に同じ名前を付ける
console.log("a.hello():", a.hello(), "/ b.hello():", b.hello());
delete a.hello;
console.log("delete 後 a.hello():", a.hello());
const base = { tags: [] }; // 配列を親に置くと…
const u1 = Object.create(base), u2 = Object.create(base);
u1.tags.push("admin");
console.log("u2.tags:", u2.tags, "← u1 にだけ入れたつもり");
a.hello(): 後から追加したメソッド / b.hello(): 後から追加したメソッド
a.hello(): a だけの hello / b.hello(): 後から追加したメソッド
delete 後 a.hello(): 後から追加したメソッド
u2.tags: [ 'admin' ] ← u1 にだけ入れたつもり
読み取れることです。
- 親に後から足したメソッドは、すでに作ったインスタンス(a と b)でもすぐ使えました。 インスタンスは親をコピーせず、つながりを持っているだけだからです。
- 自分に同じ名前を付けると、そちらが先に見つかり、親のものは隠れます(シャドーイング)。消すと、また親のものが見えます。
- 親に配列を置くと、すべての子で同じ配列を共有します。 u1 に入れた "admin" が u2 にも出ました。子ごとに持たせたい値は、コンストラクターの中で
this.tags = []のように作ります。
prototypeと__proto__とgetPrototypeOfの違い
この節では、名前が似ていて混乱しやすい prototype・__proto__・Object.getPrototypeOf() の違いが分かります。
| 書き方 | 何か | 持っているもの |
|---|---|---|
関数.prototype |
new で作るインスタンスの親になるオブジェクト | 関数(とクラス) |
obj.__proto__ |
obj の親を読み書きする以前からある書き方 | Object.prototype を継いだオブジェクト |
Object.getPrototypeOf(obj) |
obj の親を読む標準の方法 | すべてのオブジェクト |
Object.setPrototypeOf(obj, p) |
obj の親を変える標準の方法 | すべてのオブジェクト(遅い) |
Object.create(p) |
親を p にした新しいオブジェクトを作る | — |
function Dog(name) { this.name = name; }
Dog.prototype.bark = function () { return `${this.name}: ワン`; };
const pochi = new Dog("ポチ");
console.log("pochi.bark() :", pochi.bark());
console.log("pochi.__proto__ === Dog.prototype :", pochi.__proto__ === Dog.prototype);
console.log("getPrototypeOf(pochi) === Dog.prototype:", Object.getPrototypeOf(pochi) === Dog.prototype);
console.log("Dog.prototype.constructor === Dog :", Dog.prototype.constructor === Dog);
console.log("pochi.prototype :", pochi.prototype);
console.log("Dog.__proto__ === Function.prototype :", Object.getPrototypeOf(Dog) === Function.prototype);
console.log("pochi instanceof Dog :", pochi instanceof Dog);
pochi.bark() : ポチ: ワン
pochi.__proto__ === Dog.prototype : true
getPrototypeOf(pochi) === Dog.prototype: true
Dog.prototype.constructor === Dog : true
pochi.prototype : undefined
Dog.__proto__ === Function.prototype : true
pochi instanceof Dog : true
flowchart TD
F[関数 Dog] -- .prototype --> P[Dog.prototype<br>bark と constructor]
I[インスタンス pochi] -- getPrototypeOf --> P
P -- .constructor --> F
Dog.prototype は「Dog から作るインスタンスの親」で、Dog 自身の親ではありません。 pochi の親が Dog.prototype で、Dog(関数)自身の親は Function.prototype でした。インスタンスの pochi には prototype というプロパティは無く、undefined です。MDN も、関数の prototype プロパティと、オブジェクトの [[Prototype]] を混同しないように、と注意しています。instanceof は「Dog.prototype が pochi のチェーンの中にあるか」を調べています。
proto は使わず getPrototypeOf を使う
MDN は Object.prototype.__proto__ を「非推奨(Deprecated)」としています。Web の互換性のために、以前からある機能として仕様に残されているだけで、代わりに Object.getPrototypeOf() と Object.setPrototypeOf() を使うよう勧めています。なお、オブジェクトリテラルの中で { __proto__: p } と書いて作るときに親を決める書き方は、別の機能で、標準で非推奨ではありません。
Node.js には、__proto__ を使えなくする --disable-proto というオプションがあります。同じスクリプトを3通りで動かしました。
const o = {};
console.log("getPrototypeOf:", Object.getPrototypeOf(o) === Object.prototype);
console.log("__proto__ :", o.__proto__ === Object.prototype);
== node(オプションなし)
getPrototypeOf: true
__proto__ : true
== node --disable-proto=delete
getPrototypeOf: true
__proto__ : false
== node --disable-proto=throw(スタックトレースは省略)
getPrototypeOf: true
Error: Accessing Object.prototype.__proto__ has been disallowed with --disable-proto=throw
delete では __proto__ が消えて undefined になり、throw では読んだ時点でエラー(コード ERR_PROTO_ACCESS)になりました。 Object.getPrototypeOf() は、どの場合も同じ結果でした。新しく書くコードでは getPrototypeOf を使うと、このオプションを付けた環境でも動きます。
classとプロトタイプの関係
この節では、ES2015 から使える class が、中ではプロトタイプで動いていることを確かめます。
JavaScript の class は、コンストラクター関数とプロトタイプを書きやすくした書き方(シンタックスシュガー)です。 MDN も、class はコンストラクター関数の上の書き方であり、新しい継承の仕組みを持ち込むものではない、と説明しています。
class Animal {
constructor(name) { this.name = name; }
walk() { return `${this.name} が歩く`; }
static kind() { return "動物"; }
}
class Cat extends Animal {
meow() { return `${this.name}: ニャー`; }
}
const tama = new Cat("タマ");
console.log("typeof Cat :", typeof Cat);
console.log("tama.meow(), tama.walk() :", tama.meow(), "/", tama.walk());
console.log("getPrototypeOf(tama) === Cat.prototype :", Object.getPrototypeOf(tama) === Cat.prototype);
console.log("getPrototypeOf(Cat.prototype) === Animal.prototype:", Object.getPrototypeOf(Cat.prototype) === Animal.prototype);
console.log("getPrototypeOf(Cat) === Animal(static も継承):", Object.getPrototypeOf(Cat) === Animal, Cat.kind());
console.log("Object.hasOwn(tama, 'meow') :", Object.hasOwn(tama, "meow"));
console.log("Object.getOwnPropertyNames(Cat.prototype) :", Object.getOwnPropertyNames(Cat.prototype));
typeof Cat : function
tama.meow(), tama.walk() : タマ: ニャー / タマ が歩く
getPrototypeOf(tama) === Cat.prototype : true
getPrototypeOf(Cat.prototype) === Animal.prototype: true
getPrototypeOf(Cat) === Animal(static も継承): true 動物
Object.hasOwn(tama, 'meow') : false
Object.getOwnPropertyNames(Cat.prototype) : [ 'constructor', 'meow' ]
typeof Cat は "function" で、class の中に書いたメソッドは Cat.prototype に入っていました。 extends Animal は、Cat.prototype の親を Animal.prototype にし、さらに Cat(関数)自身の親を Animal にします。そのため static メソッドの Cat.kind() も使えました。
関数で書いた場合と class で書いた場合の違い
同じことを書いても、細かいところは違いました。
for...in(関数で prototype に代入): [ 'name', 'bark' ]
for...in(class のメソッド) : [ 'name' ]
Object.keys(pochi) : [ 'name' ]
TypeError: Class constructor Cat cannot be invoked without 'new'
Dog('ミケ') の戻り値: undefined / globalThis.name: ミケ
| 比べる点 | 関数+prototype | class |
|---|---|---|
| メソッドの置き場所 | Dog.prototype.bark = … と自分で書く |
自動で Cat.prototype に入る |
| for...in に出るか | 出る(bark が出た) | 出ない(列挙されない) |
| new を忘れたとき | エラーにならず、this がずれる | TypeError で止まる |
| 継承の書き方 | Object.create などで自分でつなぐ | extends と super |
関数を new なしで呼ぶとエラーにならず、globalThis.name に "ミケ" が書き込まれました(strict モードでないとき)。class なら、このまちがいをエラーで知らせてくれます。新しいコードは class で書き、仕組みとしてプロトタイプを理解しておくと、ライブラリの中身やエラーを読むときに役立ちます。class の書き方は、関連記事「JavaScriptクラス入門|private・static・継承を比較表で解説」で扱っています。
メソッドを prototype に置くとメモリが少なくて済む
class のメソッドは prototype に1つだけ置かれ、全インスタンスで共有されます。インスタンスごとに関数を作る書き方と、100万個作ったときのメモリを比べました(GC の後の heapUsed の増え方。100万個を入れる配列の分を含みます。2回実行して同じ値でした)。
class P { constructor(i) { this.i = i; } get() { return this.i; } }
measure("prototype に置く(class)", (i) => new P(i));
measure("毎回作る(this.get = ...)", (i) => { const o = { i }; o.get = function () { return this.i; }; return o; });
1個あたり40バイトと144バイトで、約3.6倍の差でした。 class のフィールドに書いたアロー関数(onClick = () => …)も、インスタンスごとに作られます。
Object.keys(b1) : [ 'label', 'onClick' ]
getOwnPropertyNames(Btn.prototype) : [ 'constructor', 'render' ]
b1.render === b2.render : true
b1.onClick === b2.onClick : false
render() のようなメソッドは prototype にあって共有され(true)、フィールドの onClick は別々の関数でした(false)。this を固定したいイベント処理ではフィールドのアロー関数が便利ですが、数が多いオブジェクトではメモリが増えることを覚えておきます。
プロトタイプでよくあるエラーと注意点
この節では、プロトタイプまわりで実際に出たエラー文から、原因と直し方を引けます。
Node.js 22.22.2 で出たエラー文です。
Object.create(null) の toString → TypeError: Cannot convert object to primitive value
Object.create(1) → TypeError: Object prototype may only be an Object or null: 1
循環するプロトタイプ → TypeError: Cyclic __proto__ value
undefined のプロパティ → TypeError: Cannot read properties of undefined (reading 'name')
存在しないメソッドを呼ぶ → TypeError: o.save is not a function
凍結したプロトタイプの変更 → TypeError: #<Object> is not extensible
| エラー文 | 原因 | 直し方 |
|---|---|---|
o.save is not a function |
チェーンのどこにも save が無い | 綴り・new の付け忘れ・継承のつなぎ方を確かめる |
Cannot read properties of undefined (reading 'name') |
チェーンをたどっても無く undefined だった | 値があるか確かめる(?.) |
Class constructor Cat cannot be invoked without 'new' |
class を関数として呼んだ | new Cat() で呼ぶ |
Object prototype may only be an Object or null: 1 |
Object.create に数値などを渡した | オブジェクトか null を渡す |
Cyclic __proto__ value |
親子が輪になるようにつないだ | チェーンが null で終わるようにする |
Cannot convert object to primitive value |
親が null のオブジェクトを文字列にした | 親が null だと toString も無い |
#<Object> is not extensible |
凍結したオブジェクトの親を変えた | 作るときに Object.create で親を決める |
Accessing Object.prototype.__proto__ has been disallowed |
--disable-proto=throw の環境で proto を使った | Object.getPrototypeOf に書き換える |
親が null のオブジェクト(Object.create(null))
Object.create(null) で作ったオブジェクトは親を持たず、Object.prototype の toString なども持ちません。キーと値を入れるだけの入れ物として使われます。
Object.keys(dict) : [ '__proto__' ]
Object.getPrototypeOf(dict): null
"toString" in dict : false / "toString" in {}: true
このオブジェクトに dict.__proto__ = "ただのキー" と書くと、親は変わらず、__proto__ という名前の普通のキーができました。 MDN の説明どおりの動きです。ふつうの {} では "toString" in {} が true になるので、利用者が入力したキーをそのまま使う辞書には、Object.create(null) か Map を使います。
組み込みのプロトタイプに足さない
Array.prototype などに自分のメソッドを足すと、関係のないコードにまで影響します。
Array.prototype.last = function () { return this[this.length - 1]; };
const arr = ["a", "b"];
console.log("arr.last():", arr.last());
const keys = []; for (const k in arr) keys.push(k);
console.log("for...in のキー:", keys);
console.log("for...of の値 :", [...arr]);
arr.last(): b
for...in のキー: [ '0', '1', 'last' ]
for...of の値 : [ 'a', 'b' ]
すべての配列の for...in に last が出てきました。 MDN は、組み込みのプロトタイプを広げることを「モンキーパッチ」と呼び、将来の JavaScript と名前がぶつかるおそれがあるため、新しい機能を以前のエンジンで使えるようにする目的以外では行わないよう勧めています。配列の要素を回すときは for...of を使います。
setPrototypeOf は遅い
MDN は、作った後で Object.setPrototypeOf() を使って親を変えることを、どのブラウザ・エンジンでも「とても遅い操作」としています。親は作るときに Object.create(親) か class で決めます。また、チェーンの奥にあるプロパティや、どこにも無いプロパティを探すと、チェーンを最後までたどるので、そのぶん時間がかかります。
C言語のプロトタイプ宣言とは
この節では、C 言語で「プロトタイプ」と言うときの意味と、書かないと何が起きるかを、gcc の実物で確かめます。
C 言語の関数プロトタイプ宣言は、関数を使う前に「引数と戻り値の型」をコンパイラに知らせる1行です。 double half(double x); のように、本体の { } を書かずにセミコロンで終えます。ヘッダーファイル(.h)に並んでいるのは、主にこの宣言です。
/* half.c */
double half(double x) { return x / 2; }
/* main_noproto.c:プロトタイプ宣言が無い */
#include <stdio.h>
int main(void) {
printf("half(3.0) = %f\n", half(3.0));
return 0;
}
$ gcc -o noproto main_noproto.c half.c
main_noproto.c:4:32: warning: implicit declaration of function 'half' [-Wimplicit-function-declaration]
main_noproto.c:4:26: warning: format '%f' expects argument of type 'double', but argument 2 has type 'int' [-Wformat=]
$ ./noproto
half(3.0) = 0.000000
double half(double x); を main の前に1行足すと、警告なしでコンパイルでき、結果は half(3.0) = 1.500000 になりました。
宣言が無いと、gcc 13.3.0 は警告を出したうえで「戻り値は int」と仮に決めてコンパイルし、1.5 ではなく 0.000000 と表示されました。 実行してもエラーにならないので、警告を見落とすと気づきにくい誤りです。GCC の公式ページによると、GCC 14 からは宣言していない関数を呼ぶことはできなくなり、エラーとして扱われます。C 言語の基本は、関連記事「C言語とは|入門から環境構築・ポインタ・mallocまで実行例で解説」で扱っています。
よくある質問
Q. プロトタイプとは簡単に言うと何ですか? A. 本番を作る前に作る「試作品・原型」です。家を建てる前の模型のように、小さく作って見せ、使う人の意見をもらって、作り直しを減らすために使います。
Q. IT でのプロトタイプの意味は? A. システムやアプリ開発では、画面の流れや一部の機能が動く試作品を指します。JavaScript ではオブジェクトの親、C 言語では関数の型の宣言のことで、場面によって意味が変わります。
Q. プロトタイプと MVP の違いは? A. プロトタイプは操作の流れや使いやすさを確かめる試作品で、保存などは動かないこともあります。MVP は本物として動く最小限の製品で、実際の利用者に使ってもらい、市場で使われるかを確かめます。
Q. プロトタイプと PoC の違いは? A. PoC(概念実証)は「技術的に実現できるか」を確かめるもので、画面が無いこともあります。プロトタイプは「使う人にとってよいか」を確かめるもので、画面と操作が中心です。
Q. プロトタイプとモックアップ・ワイヤーフレームの違いは? A. ワイヤーフレームは線と箱で配置を描いた設計図、モックアップは色や画像を入れた静止画の見た目です。プロトタイプは、クリックで画面が切り替わるなど操作できる点が違います。
Q. プロトタイプは何で作りますか? A. 最初は紙やホワイトボード、見た目と操作を試すなら Figma や Penpot、本番に近い動きを試すなら HTML・CSS・JavaScript が使われます。この記事では、27行の HTML で3画面の試作品を作り、ブラウザで動かしました。
Q. プロトタイプモデルとは? A. 開発の早い段階で試作品を作って利用者に評価してもらい、要求を固めてから本番の設計・実装に進む開発手法です。要求があいまいなシステムで、手戻りを減らすために使われます。
Q. JavaScript のプロトタイプとは? A. すべてのオブジェクトが持っている「親のオブジェクト」へのつながりです。自分に無いプロパティやメソッドは親、親の親…とたどって探します。この連なりをプロトタイプチェーンと呼び、null で終わります。
Q. prototype と proto の違いは?
A. prototype は関数(とクラス)が持つプロパティで、new で作るインスタンスの親になります。__proto__ は、あるオブジェクトの親を読むための以前からある書き方で、MDN では非推奨です。親を読むときは Object.getPrototypeOf() を使います。
Q. JavaScript の class とプロトタイプは別物ですか?
A. 別物ではありません。class はコンストラクター関数とプロトタイプを書きやすくした書き方で、typeof は "function"、メソッドは クラス名.prototype に入っていました。
Q. プロトタイプ宣言とは? A. C 言語・C++ で、関数を使う前に引数と戻り値の型を知らせる宣言です。書かないと gcc 13.3.0 では警告のうえ誤った値(0.000000)になり、GCC 14 からはエラーになります。
まとめ
- プロトタイプは「試作品・原型」。語源はギリシャ語の protos(最初の)。IT では、開発の試作品・JavaScript のオブジェクトの親・C 言語の関数の宣言の3つの意味がある。
- 開発のプロトタイプは、作る前に「これでよいか」を安く早く確かめるもの。ワイヤーフレーム(配置)・モックアップ(見た目)・PoC(技術)・MVP(市場)とは、確かめることが違う。
- 進め方は「問いを決める → 範囲と忠実度を決める → 時間を区切って作る → 触ってもらう → 書き戻す」。27行の HTML でも、クリックで動く3画面の試作品が作れた。
- 作り込みすぎ・完成品と思われる・試作のコードが本番になる、が失敗しやすい点。使い捨てか進化型かを先に決める。
- JavaScript では、自分に無いものをプロトタイプチェーンでたどって探す。配列の map は Array.prototype にあり、チェーンは null で終わる。
- 親を読むのは
Object.getPrototypeOf()。__proto__は非推奨。class の中身もプロトタイプで、メソッドを prototype に置くと100万個で 38.2MB と 137.3MB の差が出た。
参考・出典
確認日はいずれも 2026年10月23日です。
- Weblio 辞書「prototype」(IT用語辞典バイナリ):https://www.weblio.jp/content/prototype
- NTT東日本 Biz Drive「プロトタイプ」:https://business.ntt-east.co.jp/content/bizdrive/word/prototype.html
- MDN Web Docs「Inheritance and the prototype chain」:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain
- MDN Web Docs「オブジェクトのプロトタイプ」:https://developer.mozilla.org/ja/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes
- MDN Web Docs「Object.prototype.proto」:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
- MDN Web Docs「Object.setPrototypeOf()」:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
- Node.js Documentation「Command-line API(--disable-proto)」:https://nodejs.org/api/cli.html
- Figma Learn「Guide to prototyping in Figma」:https://help.figma.com/hc/en-us/articles/360040314193-Guide-to-prototyping-in-Figma
- Penpot:https://penpot.app/
- GCC「Porting to GCC 14」:https://gcc.gnu.org/gcc-14/porting_to.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「プロトタイプとは|意味・MVPとの違い・JavaScriptのプロトタイプを解説」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。