Conceptos básicos de JavaScript para principiantes

El material, cuya traducción publicamos hoy, está dedicado a los conceptos básicos de JavaScript y está destinado a programadores principiantes. Se puede considerar como una pequeña referencia a las construcciones básicas de JS. Aquí, en particular, hablaremos sobre el sistema de tipo de datos, sobre variables, sobre matrices, sobre funciones, sobre prototipos de objetos y sobre algunas otras características del lenguaje.



Tipos de datos primitivos


Los siguientes tipos de datos primitivos están disponibles en JavaScript: number , boolean , string , undefined , null . Cabe señalar de inmediato que, cuando se trabaja con tipos de datos primitivos, por ejemplo, con literales de cadena, nosotros, incluso sin realizar una conversión explícita, podremos acceder a sus métodos y propiedades. El punto aquí es que cuando se intenta realizar tales operaciones, los literales se equipan automáticamente con el contenedor de objetos apropiado.

▍ Números


JavaScript solo tiene un tipo de número: son números de coma flotante de doble precisión. Esto lleva al hecho de que los resultados del cálculo de algunas expresiones son aritméticamente incorrectos. Es posible que ya sepa que en JS el valor de la expresión 0.1 + 0.2 no 0.1 + 0.2 0.3 . Al mismo tiempo, cuando se trabaja con números enteros, estos problemas no se observan, es decir, 1 + 2 === 3 .

JavaScript tiene un objeto Number , que es un contenedor de objetos para valores numéricos. Los objetos de tipo Number se pueden crear utilizando un comando de la forma var a = new Number(10) , o puede confiar en el comportamiento automático del sistema descrito anteriormente. Esto, en particular, le permite llamar a métodos almacenados en Number.prototype como se aplica a literales numéricos:

 (123).toString();  //"123" (1.23).toFixed(1); //"1.2" 

Hay funciones globales diseñadas para convertir valores de otros tipos a un tipo numérico. Esto es parseInt() , parseFloat() y la construcción Number() , que en este caso actúa como una función normal que realiza la conversión de tipos:

 parseInt("1")       //1 parseInt("text")    //NaN parseFloat("1.234") //1.234 Number("1")         //1 Number("1.234")     //1.234 

Si durante la operación con números se obtiene algo que no es un número (durante algunos cálculos, o al intentar convertir algo en un número), JavaScript no arrojará un error, pero presentará el resultado de una operación como el valor NaN (No-un-Número, No es un número). Para verificar si un cierto valor es NaN , puede usar la función isNaN() .

Las operaciones aritméticas de JS funcionan de una manera bastante familiar, pero debe prestar atención al hecho de que el operador + puede realizar la suma de números y la concatenación de cadenas.

 1 + 1      //2 "1" + "1"  //"11" 1 + "1"    //"11" 

▍Strings


Las cadenas de JavaScript son cadenas de caracteres Unicode. Los literales de cadena se crean encerrando el texto que se va a encerrar entre comillas dobles ( "" ) o simples ( '' ). Como ya se mencionó, cuando trabajamos con literales de cadena, podemos confiar en el contenedor de objetos correspondiente, en cuyo prototipo hay muchos métodos útiles, entre ellos substring() , indexOf() , concat() .

 "text".substring(1,3) //ex "text".indexOf('x')   //2 "text".concat(" end") //text end 

Las cadenas, como otros valores primitivos, son inmutables. Por ejemplo, el método concat() no modifica una cadena existente, sino que crea una nueva.

▍ Valores lógicos


El tipo de datos lógico en JS está representado por dos valores: true y false . El idioma puede convertir automáticamente varios valores a un tipo de datos lógico. Entonces, falso, además del valor lógico false , son los valores null , undefined '' (cadena vacía), 0 y NaN . Todo lo demás, incluidos los objetos, representa verdaderos significados. En el curso de las operaciones lógicas, todo lo que se considera verdadero se convierte en true , y todo lo que se considera falso se convierte en false . Eche un vistazo al siguiente ejemplo. De acuerdo con los principios anteriores, una cadena vacía se convertirá en false y false como resultado de la ejecución de este código, la cadena This is false llegará a la consola.

 let text = ''; if(text) { console.log("This is true"); } else { console.log("This is false"); } 

Los objetos


Los objetos son estructuras dinámicas que consisten en pares clave-valor. Los valores pueden tener tipos de datos primitivos, pueden ser objetos o funciones.

Los objetos son más fáciles de crear usando la sintaxis literal del objeto:

 let obj = { message : "A message", doSomething : function() {} } 

Las propiedades de un objeto se pueden leer, agregar, editar y eliminar en cualquier momento. Aquí se explica cómo hacerlo:

  • Propiedades de lectura: object.name, object[expression] .
  • Escritura de datos en propiedades (si la propiedad a la que se accede no existe, se agrega una nueva propiedad con la clave especificada): object.name = value , object[expression] = value .
  • Eliminar propiedades: delete object.name , delete object[expression] .

Aquí hay algunos ejemplos:

 let obj = {}; //    obj.message = "A message"; //    obj.message = "A new message"; //   delete object.message; //   

Los objetos en el lenguaje se implementan como tablas hash. Se puede crear una tabla hash simple usando el Object.create(null) :

 let french = Object.create(null); french["yes"] = "oui"; french["no"]  = "non"; french["yes"];//"oui" 

Si el objeto necesita ser inmutable, puede usar el Object.freeze() .

Para iterar sobre todas las propiedades de un objeto, puede usar el Object.keys() :

 function logProperty(name){ console.log(name); //  console.log(obj[name]); //   } Object.keys(obj).forEach(logProperty); 

▍ Comparación de valores de tipos y objetos primitivos


En el trabajo práctico con valores primitivos, puede, como ya se mencionó, percibirlos como objetos que tienen propiedades y métodos, aunque no son objetos. Los valores primitivos son inmutables, la estructura interna de los objetos puede cambiar.

Variables


En JavaScript, las variables se pueden declarar utilizando las palabras clave var , let y const .

Con la palabra clave var , puede declarar una variable y, si es necesario, inicializarla con un cierto valor. Si la variable no se inicializa, su valor undefined está undefined . Las variables declaradas usando la palabra clave var tienen alcance funcional.

La palabra clave let es muy similar a var , la diferencia es que las variables declaradas con la palabra clave let tienen alcance de bloque.

Las variables declaradas usando la palabra clave const también tienen un alcance de bloque, que, dado el hecho de que los valores de tales variables no pueden cambiarse, se denominarán más correctamente "constantes". La palabra clave const , que "congela" el valor de una variable declarada al usarla, se puede comparar con el método Object.freeze() , que "congela" objetos.

Si una variable se declara fuera de una función, su alcance es global.

Matrices


Las matrices en JavaScript se implementan utilizando objetos. Como resultado, cuando hablamos de matrices, de hecho, discutimos objetos similares a las matrices. Puede trabajar con elementos de matriz utilizando sus índices. Los índices numéricos se convierten en cadenas y se usan como nombres para acceder a los valores de los elementos de la matriz. Por ejemplo, una construcción de la forma arr[1] similar a una construcción de la forma arr['1'] , y ambas darán acceso al mismo valor: arr[1] === arr['1'] . De acuerdo con lo anterior, una matriz simple declarada por el comando let arr = ['A', 'B', 'C'] se representa como un objeto de la siguiente forma:

 { '0': 'A', '1': 'B', '2': 'C' } 

Eliminar elementos de la matriz con el comando delete deja agujeros en él. Para evitar este problema, puede usar el comando splice() , pero funciona lentamente, porque, después de eliminar un elemento, mueve los elementos restantes de la matriz, desplazándolos realmente al comienzo de la matriz, a la izquierda.

 let arr = ['A', 'B', 'C']; delete arr[1]; console.log(arr); // ['A', empty, 'C'] console.log(arr.length); // 3 

Los métodos de matriz facilitan la implementación de estructuras de datos como pilas y colas:

 //  let stack = []; stack.push(1);           // [1] stack.push(2);           // [1, 2] let last = stack.pop();  // [1] console.log(last);       // 2 //  let queue = []; queue.push(1);           // [1] queue.push(2);           // [1, 2] let first = queue.shift();//[2] console.log(first);      // 1 

Las funciones


Las funciones en JavaScript son objetos. Las funciones pueden asignarse a variables, almacenarse en objetos o matrices, pasarse como argumentos a otras funciones y devolverse desde otras funciones.

Hay tres formas de declarar funciones:

  • Declaración de función clásica (Declaración de función o Declaración de función).
  • El uso de expresiones funcionales (Function Expression), que también se denominan literales funcionales (Function Literal).
  • Usando la sintaxis de las funciones de flecha (Función de flecha).

▍ Declaración de función clásica


Con este enfoque para declarar funciones, se aplican las siguientes reglas:

  • La primera palabra clave en una línea de declaración de function es function .
  • Las funciones deben tener asignado un nombre.
  • La función se puede usar en el código antes de su declaración debido al mecanismo de elevar la declaración de la función a la parte superior del alcance en el que se declara.

Así es como se ve una declaración de función clásica:

 function doSomething(){} 

▍ Expresiones funcionales


Cuando se usan expresiones funcionales, se debe considerar lo siguiente:

  • La palabra clave de function ya no es la primera palabra en una línea de declaración de función.
  • Un nombre de función es opcional. Es posible utilizar expresiones funcionales anónimas y con nombre.
  • Los comandos para invocar tales funciones deben seguir los comandos para su declaración.
  • Dicha función se puede iniciar inmediatamente después de la declaración utilizando la sintaxis de IIFE (Expresión de función invocada inmediatamente, llamada inmediatamente expresión de función).

La expresión funcional se ve así:

 let doSomething = function() {} 

▍ Funciones de flecha


Las funciones de flecha, de hecho, pueden considerarse "azúcar sintáctico" para crear expresiones funcionales anónimas. Cabe señalar que tales funciones no tienen sus propias entidades this y arguments . La declaración de la función de flecha se ve así:

 let doSomething = () = > {}; 

▍ Formas de llamar a funciones


Las funciones se pueden invocar de varias maneras.

Llamada a la función normal


 doSomething(arguments) 

Llamada de función en forma de un método de objeto


 theObject.doSomething(arguments) theObject["doSomething"](arguments) 

Llamada a la función del constructor


 new doSomething(arguments) 

Llamar a una función usando el método apply ()


 doSomething.apply(theObject, [arguments]) doSomething.call(theObject, arguments) 

Llamar a una función usando el método bind ()


 let doSomethingWithObject = doSomething.bind(theObject); doSomethingWithObject(); 

Se pueden invocar funciones con más o menos argumentos que el número de parámetros que se especificaron cuando se declararon. En el curso del trabajo de la función, los argumentos "adicionales" simplemente serán ignorados (aunque la función tendrá acceso a ellos), los parámetros faltantes obtendrán el valor undefined .

Las funciones tienen dos pseudoparámetros: this y arguments .

▍ Palabra clave esto


La this representa el contexto de una función. El valor al que apunta depende de cómo se llamó la función. Estos son los significados de this palabra clave dependiendo de cómo se llama la función (se describen arriba con ejemplos de código, cuyas construcciones se usan aquí):

  • La llamada de función habitual es window / undefined .
  • Una llamada a función en forma de método de objeto es theObject .
  • Una llamada a función en forma de constructor es un objeto nuevo.
  • Llamar a una función utilizando el método apply() : theObject .
  • Llamar a una función utilizando el método bind() : theObject .

▍ Argumentos de palabras clave


La palabra clave arguments es un pseudo-parámetro que da acceso a todos los argumentos utilizados para llamar a la función. Parece una matriz, pero no una matriz. En particular, no tiene métodos de matriz.

 function reduceToSum(total, value){ return total + value; } function sum(){ let args = Array.prototype.slice.call(arguments); return args.reduce(reduceToSum, 0); } sum(1,2,3); 

Una alternativa a la palabra clave arguments es la nueva sintaxis para los parámetros restantes. En el siguiente ejemplo, args es una matriz que contiene todo lo que se pasó a la función cuando se llamó.

 function sum(...args){ return args.reduce(reduceToSum, 0); } 

ReturnRetorno del operador


Una función que no tiene una return devolución volverá undefined . Con la palabra clave return , preste atención a cómo funciona el mecanismo de inserción automática de punto y coma. Por ejemplo, la siguiente función no devolverá un objeto vacío, sino un valor undefined :

 function getObject(){ return { } } getObject() 

Para evitar un problema similar, el corchete de apertura debe colocarse en la misma línea que la return :

 function getObject(){ return { } } 

Escritura dinámica


JavaScript es un lenguaje de escritura dinámico. Esto significa que los valores específicos tienen tipos, pero las variables no. Durante la ejecución del programa, se pueden escribir valores de diferentes tipos en la misma variable. Aquí hay un ejemplo de una función que funciona con valores de diferentes tipos:

 function log(value){ console.log(value); } log(1); log("text"); log({message : "text"}); 

Para averiguar el tipo de datos almacenados en una variable, puede usar el operador typeof() :

 let n = 1; typeof(n);   //number let s = "text"; typeof(s);   //string let fn = function() {}; typeof(fn);  //function 

Modelo de ejecución de subproceso único


El tiempo de ejecución de JavaScript es de un solo subproceso. Esto, en particular, se expresa en la imposibilidad de ejecutar simultáneamente dos funciones (si no tiene en cuenta las posibilidades de ejecución de código asíncrono, que no tocamos aquí). El tiempo de ejecución tiene una llamada Cola de eventos, que almacena una lista de tareas que deben procesarse. Como resultado, el problema de los bloqueos de recursos mutuos no es típico para un esquema de ejecución JS de subproceso único, por lo tanto, el mecanismo de bloqueo no es necesario aquí. Sin embargo, el código que cae en la cola de eventos debe ejecutarse rápidamente. Si se sobrecarga con trabajo pesado, en la aplicación del navegador, el hilo principal, la página de la aplicación no responderá a las acciones del usuario y el navegador ofrecerá cerrar esta página.

Manejo de excepciones


JavaScript tiene un mecanismo para manejar excepciones. Funciona de acuerdo con un principio bastante habitual para tales mecanismos: el código que puede causar un error se ejecuta utilizando la construcción try/catch . El código en sí está en el bloque try , los errores se procesan en el catch .

Es interesante observar que a veces JavaScript, en caso de emergencia, no genera mensajes de error. Esto se debe al hecho de que JS no arrojó errores hasta la adopción del estándar ECMAScript 3.

Por ejemplo, en el siguiente fragmento de código, fallará un intento de cambiar un objeto "congelado", pero no se lanzará una excepción.

 let obj = Object.freeze({}); obj.message = "text"; 

Algunos de los errores JS "silenciosos" aparecen en modo estricto, puede habilitarlo usando la construcción "use strict"; .

Sistema prototipo


La base de mecanismos JS como las funciones de constructor, el Object.create() , la palabra clave de class , se basa en un sistema prototipo.
Considere el siguiente ejemplo:

 let service = { doSomething : function() {} } let specializedService = Object.create(service); console.log(specializedService.__proto__ === service); //true 

Aquí, para crear un objeto de servicio specializedService , cuyo prototipo era hacer un objeto de service , se utilizó el comando Object.create() . Como resultado, resulta que se puede llamar al método doSomething() accediendo al objeto specialService. Además, esto significa que la propiedad __proto__ del objeto __proto__ apunta a un objeto de service .

Ahora cree un objeto similar usando la palabra clave de class :

 class Service { doSomething(){} } class SpecializedService extends Service { } let specializedService = new SpecializedService(); console.log(specializedService.__proto__ === SpecializedService.prototype); 

Los métodos declarados en la clase de Service se agregarán al objeto Service.prototype . Las instancias de la clase Service tendrán el mismo prototipo ( Service.prototype ). Todas las instancias delegarán llamadas de método al objeto Service.prototype . Como resultado, resulta que los métodos se declaran solo una vez, en Service.prototype , después de lo cual son "heredados" por todas las instancias de la clase.

▍ Cadena de prototipo


Los objetos pueden ser "herederos" de otros objetos. Cada objeto tiene un prototipo, cuyos métodos están disponibles. Si intenta acceder a una propiedad que no está en el objeto en sí, JavaScript comenzará a buscarla en la cadena de prototipos. Este proceso continuará hasta que se encuentre la propiedad, o hasta que la búsqueda llegue al final de la cadena.

Acerca de la programación funcional en JavaScript


En JavaScript, las funciones son objetos de primera clase; el lenguaje admite el mecanismo de cierre. Esto abre el camino para implementar técnicas de programación funcional en JS. En particular, estamos hablando de la posibilidad de utilizar funciones de orden superior.

Un cierre es una función interna que tiene acceso a las variables declaradas dentro de la función principal, incluso después de que se ejecuta la función principal.

Una función de orden superior es una función que puede tomar otras funciones como argumentos, devolver funciones o hacer ambas cosas.

La programación funcional en JS está cubierta en muchas publicaciones. Si está interesado, aquí hay algunos materiales sobre este tema dedicados a funciones de primera clase , composición , decoradores , cierres y legibilidad de código escrito en un estilo funcional.

Resumen


El poder de JavaScript reside en su simplicidad. Comprender los mecanismos básicos del lenguaje le permite al programador que usa JS aplicar estos mecanismos de manera más efectiva y sienta las bases para su crecimiento profesional.

Estimados lectores! ¿Qué características de JavaScript crees que causan la mayoría de los novatos?

Source: https://habr.com/ru/post/es416375/


All Articles