JavaScriptで実験データを扱う
前回HTML/CSSで実験プロトコルのWebページを作りました。きれいですが、そのページはゲル画像の「写真」のようなもの — 見ることはできますが、反応しません。
JavaScriptを加えるとWebページが生き生きと動きます。OD値を入力すると自動で濃度を計算し、力価が基準値以下なら警告を出し、96サンプルの結果を一括処理できます。HTMLがゲル画像なら、JavaScriptはその画像を分析するImageJソフトウェアです。
変数:実験ノートにデータを記録する
変数(variable)はデータにラベルを付けて保存することです。実験ノートに「サンプル濃度:2.5 μg/mL」と書き留めるのと同じです。
const geneName = "BRCA1";
const sampleConcentration = 2.5;
let experimentStatus = "進行中";
console.log(geneName); // "BRCA1"
console.log(sampleConcentration); // 2.5
console.log(experimentStatus); // "進行中"constは変わらない値に、letは変わる可能性のある値に使います。遺伝子名は実験中に変わらないのでconst、実験ステータスは「進行中」から「完了」に変わるのでletです。
JavaScriptには3つの基本データ型があります:
// 文字列(String)— 遺伝子名、サンプルラベル
const proteinTarget = "EGFR";
// 数値(Number)— 濃度、OD値、温度
const reactionTemp = 37;
const odValue = 0.85;
// ブーリアン(Boolean)— 真偽判定
const isPositive = true;
const hasContamination = false;
console.log(typeof proteinTarget); // "string"
console.log(typeof reactionTemp); // "number"
console.log(typeof isPositive); // "boolean"文字列は引用符で、数値はそのまま、ブーリアンはtrue/falseで書きます。PCR結果が陽性か陰性か — ちょうど2つの状態だけが存在するのがブーリアンです。
条件分岐:実験結果を判定する
条件分岐(conditional)は「バンドがあれば陽性、なければ陰性」のように状況に応じて異なるアクションをするロジックです。実験で毎日行う意思決定をコードに変換したものです。
const ctValue = 28.5;
const threshold = 35;
if (ctValue < threshold) {
console.log("陽性(Positive)");
} else {
console.log("陰性(Negative)");
}
// 出力: "陽性(Positive)"
console.assert(ctValue < threshold, "Ct 28.5はthreshold 35未満");条件が複数ある場合はelse ifを使います:
const purityRatio = 1.95;
let qualityGrade;
if (purityRatio >= 1.8 && purityRatio <= 2.0) {
qualityGrade = "純粋DNA";
} else if (purityRatio < 1.8) {
qualityGrade = "タンパク質汚染の疑い";
} else {
qualityGrade = "RNA汚染の疑い";
}
console.log(`A260/A280 = ${purityRatio} → ${qualityGrade}`);
// 出力: "A260/A280 = 1.95 → 純粋DNA"
console.assert(qualityGrade === "純粋DNA");ここで===は「正確に同じか?」を比較する演算子です。=(値を入れること)と混同しないでください。実験で「濃度を設定すること」と「濃度を測定して確認すること」は全く異なる行為です。代入は=、比較は===です。
配列とループ:サンプルリストの一括処理
配列(array)は複数のデータを順番に入れる箱です。ゲル上のバンドを左から順に並べたものと同じです。
const sampleIds = ["S001", "S002", "S003", "S004", "S005"];
const odValues = [0.45, 1.23, 0.89, 0.12, 2.15];
console.log(sampleIds[0]); // "S001"(最初)
console.log(sampleIds.length); // 5(総数)
console.log(odValues[2]); // 0.89(3番目)配列のインデックスは0から始まります。96ウェルプレートでA1が0番目のウェルであるのと同じです。
ループ(loop)は96ウェルプレートの全ウェルに同じ処理を施すことです。サンプルが5個でも500個でも、コード1つで全部処理できます。
const geneList = ["TP53", "BRCA1", "EGFR", "KRAS", "MYC"];
for (let i = 0; i < geneList.length; i++) {
console.log(`分析対象 ${i + 1}: ${geneList[i]}`);
}
// 分析対象 1: TP53
// 分析対象 2: BRCA1
// 分析対象 3: EGFR
// 分析対象 4: KRAS
// 分析対象 5: MYC
console.assert(geneList.length === 5, "遺伝子リストは5個");forループは3つの部分で構成されます:開始値(let i = 0)、繰り返し条件(i < geneList.length)、増加式(i++)。毎回iが1ずつ増えながら、配列の最初から最後まで巡回します。
配列と条件分岐を組み合わせれば、大量サンプルのQC判定も自動化されます:
const samples = ["S001", "S002", "S003", "S004"];
const concentrations = [2.5, 0.3, 1.8, 0.1];
const minConcentration = 0.5;
let passCount = 0;
for (let i = 0; i < samples.length; i++) {
if (concentrations[i] >= minConcentration) {
console.log(`${samples[i]}: PASS`);
passCount++;
} else {
console.log(`${samples[i]}: FAIL(再抽出必要)`);
}
}
console.log(`合格: ${passCount}/${samples.length}`);
// S001: PASS
// S002: FAIL(再抽出必要)
// S003: PASS
// S004: FAIL(再抽出必要)
// 合格: 2/4
console.assert(passCount === 2, "0.5以上のサンプルは2個");条件分岐1つ、ループ1つで手動QCチェックが自動化されました。サンプルが100個に増えてもコードはそのままです。
関数:プロトコルを再利用可能にする
関数(function)は実験プロトコルの1ステップを再利用可能にパッケージしたものです。「GC含量計算」というプロトコルを一度定義しておけば、どんな配列でも入れれば結果が出ます。
function calculateGcContent(sequence) {
let gcCount = 0;
for (let i = 0; i < sequence.length; i++) {
if (sequence[i] === "G" || sequence[i] === "C") {
gcCount++;
}
}
return (gcCount / sequence.length) * 100;
}
const gc1 = calculateGcContent("ATGCGATCGA");
const gc2 = calculateGcContent("AAATTTAAATTT");
console.log(`ATGCGATCGA → GC = ${gc1}%`); // 50%
console.log(`AAATTTAAATTT → GC = ${gc2}%`); // 0%
console.assert(gc1 === 50, "ATGCGATCGAのGC含量は50%");
console.assert(gc2 === 0, "AAATTTAAATTTのGC含量は0%");関数の構成要素:
function calculateGcContent(sequence)— 関数名とパラメータ(parameter):プロトコル名と「何を入れるか」{ ... }の中のコード — プロトコルの実際の手順return— 結果を返すこと。実験が終わってデータを報告するステップcalculateGcContent("ATGCGATCGA")— 関数呼び出し。パラメータの位置に入れる実際の値を**引数(argument)**と呼びます
希釈計算も関数にできます:
function dilutionVolume(stockConc, finalConc, finalVol) {
return (finalConc * finalVol) / stockConc;
}
const needed = dilutionVolume(10, 1, 500);
console.log(`原液 ${needed} μL 必要`); // 原液 50 μL 必要
console.assert(needed === 50, "C1V1 = C2V2 検証");一度関数を作っておけば、濃度が変わっても数値を変えるだけです。毎回電卓を叩くよりずっと速くミスもありません。
やってみよう(Faded Example)
空欄を埋めてDNA配列のGC含量判定コードを完成させてください。
const sequence = "ATGCGCTA";const gcThreshold = 40;let gcCount = 0;for (let i = 0; i < sequence.; i++) {if (sequence[i] === "G" || sequence[i] === "") {gcCount++;}}const gcPercent = (gcCount / sequence.length) * ;if (gcPercent >= gcThreshold) {console.log("High GC");} else {console.log("Low GC");}
よくあるエラーと解決法
Q: =と===を混同します
=は値を入れること(代入)、===は同じかどうか比較することです。if (status = "pass")と書くと比較ではなく代入になり、常にtrueになります。条件分岐の中では必ず===を使ってください。
Q: 配列の最後の要素にアクセスするには?
const genes = ["TP53", "BRCA1", "EGFR"]でgenes[3]はundefinedです。配列インデックスは0から始まるので、最後の要素はgenes[genes.length - 1]でアクセスします。
Q: constで宣言した変数を変更しようとするとエラーが出ます
TypeError: Assignment to constant variable. — constは一度決めた値を変更できません。値が変わる必要がある変数はletで宣言してください。
Q: 関数を呼び出したのにundefinedが返ってきます
関数内にreturnがないと結果を返しません。console.log()は画面に出力するだけで値を返しません。計算結果を他で使う必要があるなら必ずreturnを入れてください。
Q: 講義でvarを使っているコードがありますが、let/constと何が違いますか?
varはES6(2015)以前の古い宣言方法で、関数スコープです。let/constはブロックスコープで{}の中だけで有効です。varはブロックを無視して外からもアクセスできるため、予期しないバグを生みます:
if (true) {
var x = 10;
let y = 20;
}
console.log(x); // 10(外からアクセスできてしまう)
// console.log(y); // ReferenceError(ブロック外なのでアクセス不可)古い講座でvarを使っているのは講座の時点がES6以前だからです。現代のJavaScriptでは基本的にconst、値が変わる必要がある時だけletを使ってください。varは使わないでください。
Q: ? :は何ですか?(三項演算子)
条件 ? 値1 : 値2 — 条件がtrueなら値1、falseなら値2を返す演算子です。if/elseを1行に圧縮したものです:
const od = 1.85;
const result = od >= 1.0 ? "pass" : "fail";
// if (od >= 1.0) { result = "pass" } else { result = "fail" } と同じQ: !はどういう意味ですか?
!は論理否定(NOT)演算子です。trueをfalseに、falseをtrueに反転します:
const hasPermission = false;
if (!hasPermission) {
console.log("アクセス拒否"); // これが実行される
}!変数名は「変数がfalseなら」と読めばよいです。!isPassedは「合格していなければ」。
Q: DOMとは何ですか?document.getElementByIdはどこから来たのですか?
DOM(Document Object Model)はブラウザがHTMLを読んでJavaScriptで操作できるツリー構造のオブジェクトに変換したものです。documentはそのツリーの頂点で、getElementById()はツリーから特定の要素を見つけるメソッドです:
// HTML: <div id="result">待機中</div>
const resultDiv = document.getElementById("result");
resultDiv.innerText = "分析完了!";HTMLが「設計図」なら、DOMはブラウザがその設計図で建てた「建物」です。JavaScriptはこの建物の壁を塗ったり家具を動かしたりできます。
Q: バックティック(`)は引用符と何が違いますか?
バックティックはテンプレートリテラルを作る文法です。文字列の中に${変数名}で値を直接挿入できます:
const gene = "TP53";
const od = 2.15;
// 引用符:文字列結合が面倒
const msg1 = gene + "のOD値: " + od;
// バックティック:きれいに挿入
const msg2 = `${gene}のOD値: ${od}`;
// 計算式も可能
const msg3 = `30日 = ${60 * 60 * 24 * 30}秒`;シングルクオート(')やダブルクオート(")で書くと${}がそのまま文字として出力されます。必ずバックティック(`)を使ってください。
Q: 式(expression)と文(statement)とは何ですか?
式は値に評価されるコードです — 変数に格納できます。文は動作するだけで値を生みません:
// 式:値になる
1 + 1 // 2
od >= 1.0 // true
"pass" // "pass"
// 文:動作するだけ(値にならない)
if (true) { } // 条件分岐
for (let i...) { } // ループ関数もreturnがあれば呼び出し結果が値(式)になり、returnがなければundefinedを返します。先ほどの「関数を呼び出したのにundefinedが返ってきます」のQ&Aとつながる概念です。
Q: アロー関数(arrow function)=>とは何ですか?
functionキーワードの代わりに=>で関数を短く書く文法です。コールバック関数でよく使います:
// 従来の方式
samples.filter(function(s) {
return s.status === "pass";
});
// アロー関数方式 — 同じ動作
samples.filter((s) => {
return s.status === "pass";
});
// さらに短く — 本文が1行なら中括弧とreturnを省略可能
samples.filter(s => s.status === "pass");function(x) { return ... }とx => ...はほとんどの場合同じ動作をします。DevBenchでは初心者が読みやすいfunction方式を使いますが、実務コードやAIが生成するコードではアロー関数が圧倒的に多く使われます。
Q: JavaScriptでエラーが出るとなぜ全体が止まるのですか?
JavaScriptはコードを上から順番に実行します。途中でエラーが発生するとその行で止まり、下のコードは実行されません:
console.log("1番目は出力される");
console.log("Hello".indexOf); // エラー発生箇所
console.log("3番目は出力されない"); // 実行されないそのためConsoleにエラーが出たら、エラーメッセージの行番号を先に確認してその行を修正してください。下のコードが動かないのは下の問題ではなく、上のエラーのせいです。
Q: 主要な演算子を一覧で見たいです
| 種類 | 演算子 | 意味 | 例 | 結果 |
|---|---|---|---|---|
| 算術 | + - * / % | 足す、引く、掛ける、割る、余り | 5 % 2 | 1 |
| 比較 | === !== > < | 等しい(型含む)、等しくない、大きい、小さい | 5 === "5" | false |
| 論理 | && || ! | AND、OR、NOT | !true | false |
| 代入 | = += -= | 代入、加算代入、減算代入 | x += 3 | x = x + 3 |
| その他 | typeof ? : | 型確認、三項演算子 | typeof 42 | "number" |
比較演算子で==(緩い比較)と===(厳密な比較)の違いを覚えてください。5 == "5"はtrueですが5 === "5"はfalseです。実務では常に===を使うのが安全です。