Manipulación de datos experimentales con JavaScript
En la sesión anterior, creamos una página web para el protocolo experimental utilizando HTML/CSS. Es limpia, pero esa página es como una "foto" de una imagen de gel: solo se puede ver, no interactuar.
Al añadir JavaScript, la página web cobra vida. Si introduces un valor de OD, calcula automáticamente la concentración; muestra una advertencia si la potencia biológica está por debajo del umbral de referencia; y puede procesar los resultados de 96 muestras a la vez. Si HTML es la imagen del gel, JavaScript es el software ImageJ que analiza esa imagen.
Variables: anotando datos en el cuaderno de laboratorio
Una variable es una forma de asignar un nombre a un dato para almacenarlo. Es como escribir en tu cuaderno de laboratorio "Concentración de la muestra: 2.5 μg/mL".
const geneName = "BRCA1";
const sampleConcentration = 2.5;
let experimentStatus = "en curso";
console.log(geneName); // "BRCA1"
console.log(sampleConcentration); // 2.5
console.log(experimentStatus); // "en curso"const se utiliza para valores que no cambian, y let para valores que pueden cambiar. Dado que los nombres de los genes no cambian durante el experimento, se usa const; como el estado del experimento cambia de "en progreso" a "completado", se utiliza let.
JavaScript tiene tres tipos de datos básicos:
// Cadena (String): nombre de gen, etiqueta de muestra
const proteinTarget = "EGFR";
// Número (Number): concentración, valor de OD, temperatura
const reactionTemp = 37;
const odValue = 0.85;
// Booleano (Boolean): evaluación verdadero/falso
const isPositive = true;
const hasContamination = false;
console.log(typeof proteinTarget); // "string"
console.log(typeof reactionTemp); // "number"
console.log(typeof isPositive); // "boolean"Las cadenas se escriben entre comillas, los números tal cual y los booleanos como true/false. El resultado de la PCR es positivo o negativo: solo existen estos dos estados posibles, lo que define un valor booleano.
Condicionales: interpretar resultados experimentales
Un condicional (conditional) es una lógica que ejecuta acciones distintas según el contexto, como "si hay banda, es positivo; si no, es negativo". Es la traducción a código de las decisiones diarias que se toman en el laboratorio.
const ctValue = 28.5;
const threshold = 35;
if (ctValue < threshold) {
console.log("Positivo");
} else {
console.log("Negativo");
}
// Salida: "Positivo"
console.assert(ctValue < threshold, "Ct 28,5 es menor que el umbral 35");Cuando hay múltiples condiciones, se utiliza else if:
const purityRatio = 1.95;
let qualityGrade;
if (purityRatio >= 1.8 && purityRatio <= 2.0) {
qualityGrade = "ADN puro";
} else if (purityRatio < 1.8) {
qualityGrade = "Posible contaminación por proteínas";
} else {
qualityGrade = "Posible contaminación por ARN";
}
console.log(`A260/A280 = ${purityRatio} → ${qualityGrade}`);
// Salida: "A260/A280 = 1.95 → ADN puro"
console.assert(qualityGrade === "ADN puro");Aquí, === es el operador que compara si son "exactamente iguales". No lo confunda con = (asignar un valor). En un experimento, "establecer la concentración" y "medir y verificar la concentración" son acciones completamente distintas. La asignación se realiza con =, mientras que la comparación se hace con ===.
Arreglos y bucles: procesamiento por lotes de una lista de muestras
Un arreglo (array) es un contenedor que almacena varios datos en orden. Es similar a listar las bandas en un gel de izquierda a derecha, en secuencia.
const sampleIds = ["S001", "S002", "S003", "S004", "S005"];
const odValues = [0.45, 1.23, 0.89, 0.12, 2.15];
console.log(sampleIds[0]); // "S001" (primero)
console.log(sampleIds.length); // 5 (total)
console.log(odValues[2]); // 0.89 (tercero)Los índices del arreglo comienzan en 0. Esto equivale a que A1 sea el pozo número 0 en una placa de 96 pocillos.
Un bucle (loop) aplica el mismo procesamiento a todos los pozos de la placa de 96 pocillos. Ya sean 5 o 500 muestras, todo se procesa con una sola línea de código.
const geneList = ["TP53", "BRCA1", "EGFR", "KRAS", "MYC"];
for (let i = 0; i < geneList.length; i++) {
console.log(`Objetivo de análisis ${i + 1}: ${geneList[i]}`);
}
// Objetivo de análisis 1: TP53
// Objetivo de análisis 2: BRCA1
// Objetivo de análisis 3: EGFR
// Objetivo de análisis 4: KRAS
// Objetivo de análisis 5: MYC
console.assert(geneList.length === 5, "La lista contiene cinco genes");for El bucle consta de tres partes: el valor inicial (let i = 0), la condición del bucle (i < geneList.length) y la expresión de incremento (i++). En cada iteración, i aumenta en 1, recorriendo el arreglo desde el principio hasta el final.
Al combinar arreglos con condicionales, también se automatiza la evaluación del control de calidad (QC) de grandes volúmenes de muestras:
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 (requiere nueva extracción)`);
}
}
console.log(`Aprobadas: ${passCount}/${samples.length}`);
// S001: PASS
// S002: FAIL (requiere nueva extracción)
// S003: PASS
// S004: FAIL (requiere nueva extracción)
// Aprobadas: 2/4
console.assert(passCount === 2, "Hay dos muestras con un valor igual o superior a 0,5");Una sola condición y un solo bucle han automatizado la verificación manual de control de calidad (QC). El código permanece igual aunque el número de muestras aumente a 100.
Función: Hacer reutilizable el protocolo
Una función es una versión reutilizable que encapsula una etapa del protocolo experimental. Una vez definido el protocolo "Cálculo del contenido de GC", basta con introducir cualquier secuencia para obtener el resultado.
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, "El contenido GC de ATGCGATCGA es 50 %");
console.assert(gc2 === 0, "El contenido GC de AAATTTAAATTT es 0 %");Al organizar los componentes de una función:
function calculateGcContent(sequence)— El nombre de la función y los parámetros: el nombre del protocolo y "qué se va a introducir"- El código dentro de
{ ... }— El procedimiento real del protocolo return— Devuelve el resultado. La etapa en la que se informa de los datos al finalizar el experimentocalculateGcContent("ATGCGATCGA")— Llamada a la función. Los valores reales que se colocan en lugar de los parámetros se denominan argumentos
El cálculo del factor de dilución también puede implementarse como una función:
function dilutionVolume(stockConc, finalConc, finalVol) {
return (finalConc * finalVol) / stockConc;
}
const needed = dilutionVolume(10, 1, 500);
console.log(`Se necesitan ${needed} μL de solución madre`); // Se necesitan 50 μL
console.assert(needed === 50, "Verificación de C1V1 = C2V2");Una vez que has definido una función, solo necesitas cambiar los valores numéricos de entrada cuando cambian las concentraciones. Esto es mucho más rápido y evita errores en comparación con pulsar la calculadora cada vez.
Pruébalo tú mismo (Ejemplo guiado)
Completa los espacios en blanco a continuación para terminar el código que determina el contenido de GC de una secuencia de ADN.
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");}
Errores comunes y soluciones
Q: Confundo = con ===
= es para asignar un valor, mientras que === es para comparar si son iguales. Si se escribe if (status = "pass"), en lugar de una comparación se realiza una asignación, lo que siempre resulta verdadero (true). Dentro de las condiciones, utilice siempre ===.
Q: ¿Cómo acceder al último elemento de un array?
En const genes = ["TP53", "BRCA1", "EGFR"], genes[3] es igual a undefined. Dado que los índices de los arrays comienzan en 0, el último elemento se accede mediante genes[genes.length - 1].
Q: Se produce un error al intentar modificar una variable declarada con const
TypeError: Assignment to constant variable. — const no permite cambiar el valor asignado. Para variables cuyo valor debe modificarse, declárelas como let.
Q: Se llama a una función pero aparece undefined
Si la función no contiene return, no devuelve ningún resultado. console.log() solo imprime en pantalla y no devuelve un valor. Si necesita utilizar el resultado del cálculo en otro lugar, asegúrese de incluir return.
Q: Tengo el código de la clase declarado con var, ¿qué diferencia hay con let/const?
var es una forma antigua de declaración anterior a ES6 (2015) y tiene alcance de función. let/const tienen alcance de bloque, por lo que solo son válidos dentro de {}. var ignora el bloque y es accesible desde fuera, lo que puede generar errores inesperados:
if (true) {
var x = 10;
let y = 20;
}
console.log(x); // 10 (accesible también fuera)
// console.log(y); // ReferenceError (no es accesible fuera del bloque)El uso de var en clases antiguas como las de Saehak Coding se debe a que el momento de la clase fue anterior a ES6. En JavaScript moderno, use por defecto const, y utilice let solo cuando sea necesario cambiar el valor. No use var.
P: ¿Qué es ? :? (Operador ternario)
condición ? valor1 : valor2: devuelve valor1 si la condición es verdadera y valor2 si es falsa. Es una versión de if/else comprimida en una sola línea:
const od = 1.85;
const result = od >= 1.0 ? "pass" : "fail";
// Equivale a if (od >= 1.0) { result = "pass" } else { result = "fail" }Q: ¿Qué significa !?
! es el operador de negación lógica (NOT). Invierte true a false y false a true:
const hasPermission = false;
if (!hasPermission) {
console.log("Acceso denegado"); // Esto se ejecuta
}!nombreDeVariable se lee como «si la variable es falsa». !isPassed significa «si no ha pasado».
P: ¿Qué es el DOM? ¿De dónde proviene document.getElementById?
El DOM (Modelo de Objeto del Documento) es una estructura de árbol de objetos en la que el navegador convierte el HTML para que pueda ser manipulado mediante JavaScript. document es la raíz de ese árbol, y getElementById() es un método para encontrar elementos específicos dentro del árbol:
// HTML: <div id="result">En espera</div>
const resultDiv = document.getElementById("result");
resultDiv.innerText = "¡Análisis completado!";Si HTML es el "plano", el DOM es el "edificio" que el navegador construye a partir de ese plano. JavaScript puede pintar las paredes o mover los muebles de este edificio.
P: ¿Qué diferencia hay entre las comillas invertidas (`) y las comillas normales?
Las comillas invertidas son la sintaxis para crear plantillas literales. Permiten insertar valores directamente dentro de una cadena usando ${nombreDeVariable}:
const gene = "TP53";
const od = 2.15;
// Comillas: concatenar cadenas resulta incómodo
const msg1 = "Valor de OD de " + gene + ": " + od;
// Backticks: inserción limpia
const msg2 = `Valor de OD de ${gene}: ${od}`;
// También admite cálculos
const msg3 = `30 días = ${60 * 60 * 24 * 30} segundos`;Si se usan comillas simples (') o comillas dobles ("), ${} se imprime literalmente como texto. Es obligatorio usar acentos graves (`).
Q: ¿Qué son una expresión y una sentencia?
Una expresión es un código que se evalúa a un valor; puede asignarse a una variable. Una sentencia solo realiza una acción y no produce un valor:
// Expresión: produce un valor
1 + 1 // 2
od >= 1.0 // true
"pass" // "pass"
// Sentencia: solo realiza una acción, no produce un valor
if (true) { } // Condicional
for (let i...) { } // BucleUna función, si tiene return, devuelve el resultado de la llamada como un valor (expresión) y, si no tiene return, devuelve undefined. Este es un concepto relacionado con la pregunta y respuesta anterior: "Cuando llamo a una función, aparece undefined".
P: ¿Qué es una función flecha (arrow function) =>?
Es una sintaxis para escribir funciones de forma abreviada, utilizando => en lugar de la palabra clave function. Se utiliza con frecuencia en las funciones de devolución de llamada:
// Forma tradicional
samples.filter(function(s) {
return s.status === "pass";
});
// Función flecha: mismo comportamiento
samples.filter((s) => {
return s.status === "pass";
});
// Más breve: si el cuerpo tiene una línea, se pueden omitir las llaves y return
samples.filter(s => s.status === "pass");function(x) { return ... } y x => ... se comportan de manera idéntica en la mayoría de los casos. DevBench utiliza el estilo function, más fácil de leer para principiantes, pero en código profesional y generado por IA se usan predominantemente las funciones flecha.
Q: ¿Por qué JavaScript se detiene completamente cuando ocurre un error?
JavaScript ejecuta el código línea por línea, de arriba hacia abajo. Si se produce un error en medio, la ejecución se detiene en esa línea y el código restante no se ejecuta:
console.log("Se imprime el número 1");
console.log("Hello".indexOf); // Punto donde se produce el error
console.log("El número 3 no se imprime"); // No se ejecutaPor lo tanto, si aparece un error en la consola, primero verifique el número de línea indicado en el mensaje de error y corrija esa línea. El hecho de que el código inferior no se ejecute se debe a un error superior, no a un problema del propio código inferior.
Q: Quiero ver los operadores principales de un vistazo
| Tipo | Operador | Significado | Ejemplo | Resultado |
|---|---|---|---|---|
| Aritméticos | + - * / % | Suma, resta, multiplicación, división, módulo | 5 % 2 | 1 |
| Comparación | === !== > < | Igual (incluye tipo), distinto, mayor que, menor que | 5 === "5" | false |
| Lógicos | && || ! | AND, OR, NOT | !true | false |
| Asignación | = += -= | Asignar, sumar y asignar, restar y asignar | x += 3 | x = x + 3 |
| Otros | typeof ? : | Verificación de tipo, operador ternario | typeof 42 | "number" |
Recuerde la diferencia entre == (comparación débil) y === (comparación estricta) en los operadores de comparación. 5 == "5" es true, pero 5 === "5" es false. En la práctica profesional, es más seguro usar siempre ===.