Convertirse en un mejor desarrollador front-end usando fundamentos en lugar de heurísticaNuestra experiencia muestra que los educadores no técnicos y las personas autodidactas a menudo confían no en principios teóricos, sino en métodos heurísticos.
Heurística: patrones y reglas comprobadas que un desarrollador ha aprendido de la práctica. Pueden funcionar de manera imperfecta o limitada, pero en un grado suficiente, y no requieren un pensamiento serio. Aquí hay algunos ejemplos de heurística:
- "Use
$(document).ready(function(){})
para inicializar el código en los sitios jQuery" - "El
var self = this
construcción es necesaria para llamar a un método en una función de devolución de llamada" - "Las funciones de flecha no tienen
return
"
Al mismo tiempo, el principio teórico puede usarse para encontrar soluciones a otros problemas. Él es invariablemente fiel y a menudo determina el dispositivo mismo de uno u otro elemento. Los principios teóricos incluyen, por ejemplo:
Tenga en cuenta: citamos solo ejemplos heurísticos para enfatizar la naturaleza artesanal de la heurística en comparación con el rigor de los fundamentos teóricos. Ninguno de los ejemplos heurísticos es universal para todos los casos, pero funcionan en un número suficiente de situaciones para que los desarrolladores que los usan reciban un código de trabajo sin una comprensión completa de su funcionamiento.
Argumentos para un enfoque teórico
A menudo nos encontramos con el hecho de que los desarrolladores sin educación técnica no están dispuestos a resolver problemas utilizando principios teóricos. Como regla general, esto se explica por el hecho de que al comienzo de sus carreras no tuvieron la oportunidad de aprenderlos, y dado que los métodos heurísticos funcionan satisfactoriamente, continúan usándolos.
Sin embargo, a pesar de la aparente complejidad, aprender una teoría puede ser muy útil. Por qué Entonces, esa teoría le permitirá sentirse seguro de que su solución funciona, así como mostrar de forma independiente las respuestas a nuevas preguntas, sin tener que buscar las soluciones de otra persona. A corto plazo, los algoritmos heurísticos pueden parecer una solución simple y rápida, pero a menudo conducirán a soluciones imperfectas, si es que lo hacen.
Además, al confiar en métodos heurísticos, nunca aprenderá a resolver problemas de verdad. A menudo puede encontrar una solución que funcione, pero tarde o temprano se detendrá, de lo que no verá una salida. Los programadores de C&P confían en la heurística en su trabajo.
Criterios de nivel de habilidad de desarrollador
Al entrevistar a los desarrolladores frontend, establecemos una tarea de programación para ellos y decimos que son libres de usar cualquier fuente, ya sea Google o Stack Overflow. De esta manera, es fácil determinar si el desarrollador es adherente a la heurística o la teoría.
El primero, sin excepción, copia el código de ejemplos más o menos adecuados con Stack Overflow. Solo cuando el código no funciona según lo planeado, comienzan a ajustarlo por sí mismos. A menudo fallan.
Estos últimos tienden a buscar respuestas en la documentación de la API. Allí encuentran información sobre cuántos parámetros toma una función en particular, o la sintaxis específica de la forma expandida de la propiedad CSS deseada.
Ya en los primeros cinco minutos de la entrevista puede determinar exactamente a qué tipo de programadores pertenece el candidato.
Ejemplo
Toma a Bill como ejemplo. Completó varios cursos de capacitación, resolvió una serie de tareas en JavaScript y escribió sitios web en su tiempo libre, pero en realidad no aprendió JavaScript.
Una vez que Bill se encuentra con un objeto como este:
const usersById = { "5": { "id": "5", "name": "Adam", "registered": true }, "27": { "id": "27", "name": "Bobby", "registered": true }, "32": { "id": "32", "name": "Clarence", "registered": false }, "39": { "id": "39", "name": "Danielle", "registered": true }, "42": { "id": "42", "name": "Ekaterina", "registered": false } }
Tal objeto puede mostrar una lista de usuarios y si se han registrado para un evento en particular.
Supongamos que Bill necesita recuperar una lista de usuarios registrados. En otras palabras, filtrarlos.
.filter()
código en el que se
.filter()
método
.filter()
para filtrar la lista. Entonces intenta algo como:
const attendees = usersById.filter(user => user.registered);
Y aquí está lo que obtiene:
TypeError: usersById.filter is not a function
"Algo sin sentido", piensa Bill, porque vio el código en el que
.filter()
funcionaba como filtro.
El problema es que Bill confió en el método heurístico. No entiende que
filter
es un método definido en matrices, mientras que
usersById
es un objeto normal que no tiene un método de
filter
.
El confundido Bill busca en Google el "
filtro javascript ". Encuentra muchas referencias a matrices y se da cuenta de que necesita convertir
usersById
en una matriz. Luego, al preguntar "
javascript para convertir un objeto en una matriz ", encuentra ejemplos usando
Object.keys()
en Stack Overflow. Después de eso, intenta:
const attendees = Object.keys(usersById).filter(user => user.registered);
Esta vez no se muestra el error, pero, para sorpresa de Bill, el campo de
attendees
permanece vacío.
El hecho es que
Object.keys()
devuelve las claves del objeto, pero no su valor. De hecho, el nombre de la variable de
user
es fácilmente engañoso, porque no es un objeto de
user
, sino un identificador, es decir, una cadena. Como el atributo
registered
no está definido para cadenas, el
filter
trata cada entrada como falsa y la matriz se deja vacía.
Bill analiza más de cerca las respuestas de Stack Overflow y realiza el siguiente cambio:
const attendees = Object.keys(usersById).filter(id => usersById[id].registered);
Esta vez el resultado es mejor:
["5", "27", "39"]
. Pero Bill quería obtener objetos de visitante, no su identificación.
Para comprender cómo filtrar a los visitantes, Bill molesto busca un "
filtro de objetos javascript ", examina los resultados de búsqueda de Stack Overflow y encuentra
esta respuesta con el siguiente código:
Object.filter = (obj, predicate) => Object.keys(obj) .filter( key => predicate(obj[key]) ) .reduce( (res, key) => (res[key] = obj[key], res), {} );
Bill copia estas líneas e intenta:
const attendees = Object.filter(usersById, user => user.registered);
Todo funciona, aunque no está claro por qué. Bill no entiende por qué
reduce
necesita
reduce
y cómo se usa. Además, Bill no entiende que acaba de definir un nuevo método no estándar para el
Object
global.
Pero a Bill no le importa, ¡funciona! Las consecuencias aún no le interesan.
¿Qué hizo mal Bill?
Bill probó un método heurístico para resolver el problema y se topó con los siguientes problemas:
- Usando
.filter()
en una variable, Bill obtuvo un TypeError
. No entendió que el filter
no filter
definido en objetos ordinarios.
- Utilizó
Object.keys()
para "convertir el objeto en una matriz", pero esto solo no trajo ningún resultado. Necesitaba crear una matriz de valores de objetos. - Incluso después de recibir los valores y usarlos como condición para el filtrado, recibió solo identificadores en lugar de los objetos de usuario asociados con estos identificadores. Esto se debe a que la matriz filtrada contenía una ID, no objetos de usuario.
- Con el tiempo, Bill abandonó este enfoque y encontró una solución que funciona en Internet. Sin embargo, todavía no entendía cómo funciona, y no perderá tiempo resolviéndolo, porque tiene otras cosas que hacer.
Este es un ejemplo artificial, pero nos hemos encontrado muchas veces con desarrolladores que resuelven problemas de la misma manera. Para resolverlos de manera efectiva, debe alejarse de los métodos heurísticos y estudiar la teoría.
Pasemos a lo básico
Si Bill propugnara un enfoque teórico, el proceso se vería así:
- Para identificar los datos de entrada dados y determinar la salida deseada, en el sentido de sus propiedades: “Tengo un objeto cuyas claves son cadenas que representan ID y los valores son objetos que representan usuarios. Quiero obtener una matriz cuyos valores serán objetos de usuario, pero solo objetos de usuarios registrados "
- Para comprender cómo buscar dentro de un objeto: “Sé que puedo obtener una matriz de claves en un objeto llamando a
Object.keys()
. Quiero obtener una matriz porque las matrices admiten la enumeración " . - Para darse cuenta de que este método ayuda a obtener las claves, y necesita transformar las claves en valores, y recordar sobre el
map
, un método obvio de crear una nueva matriz mediante la transformación de los valores de otra matriz:
Object.keys(usersById).map(id => usersById[id])
- Para ver que ahora tiene una matriz de objetos de usuario que se pueden filtrar y que contiene valores reales que desea filtrar:
Object.keys(usersById).map(id => usersById[id]).filter(user => user.registered)
Vaya Bill de esta manera, podría trabajar para nosotros.
¿Por qué la gente no recurre a la teoría con más frecuencia?
A veces simplemente no la conocen. En la mayoría de los casos, están demasiado ocupados y no pueden encontrar el tiempo para aprender esta forma de resolver problemas: solo necesitan todo para funcionar. Se arriesgan a convertir este enfoque en un hábito que se convertirá en un obstáculo para el desarrollo de sus habilidades.
Para evitar tales errores, siempre comience con una teoría. En cada etapa del proceso, piense en qué tipo de datos está tratando. En lugar de confiar en patrones familiares todo el tiempo, considere los tipos de datos primitivos: matriz, objeto, cadena, etc. Cuando utilice una función o método, consulte la documentación para saber exactamente qué tipos de datos los admiten, qué argumentos toman y cuál es el resultado.
Con este enfoque, puede encontrar una solución de trabajo en el primer intento. Puede estar seguro de su corrección, ya que seleccionó especialmente sus acciones en función de la entrada dada y la salida deseada. Profundice en los conceptos básicos de cada operación (tipos de datos y valores de retorno), en lugar de formulaciones comerciales confusas (como "usuarios registrados").