Часть 3: Список некоторых общих моментов, которые вы должны знать о JavaScript
В этой серии статей собран список некоторых общеизвестных моментов, которые вы должны знать о JavaScript, что придаст вам большую уверенность, независимо от того, проводите ли вы собеседование или разрабатываете каждый день.
В предыдущей статье мы перечислили некоторые общие сведения, с которыми вы можете ознакомиться, перейдя по следующим ссылкам:
1. Что такое объект Set
и как он работает?
Set
позволяет хранить уникальные значения любого типа, будь то примитивные значения или ссылки на объекты.
Мы можем создать экземпляр Set
с помощью конструктора Set
.
const set1 = new Set(); const set2 = new Set(["a","b","c","d","d","e"]);
Мы можем использовать метод add
, чтобы добавить новое значение в экземпляр Set
. Поскольку метод add
возвращает объект Set
, мы можем объединить несколько вызовов add
в цепочку. Если значение уже существует в объекте Set
, оно не будет добавлено снова.
set2.add("f"); set2.add("g").add("h").add("i").add("j").add("k").add("k");
Мы можем использовать метод has
, чтобы проверить, существует ли конкретное значение в экземпляре Set
.
set2.has("a") // true set2.has("z") // true
Мы можем использовать свойство size
, чтобы получить длину экземпляра Set
.
set2.size // returns 10
Метод clear
можно использовать для удаления всех данных из файла Set
.
set2.clear();
Мы можем использовать объект Set
для удаления повторяющихся элементов из массива.
const numbers = [1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 5]; const uniqueNums = [...new Set(numbers)]; // [1,2,3,4,5,6,7,8]
2.Что такое функция обратного вызова?
Функция обратного вызова — это исполняемый фрагмент кода, который передается в качестве аргумента другому коду. Его цель состоит в том, чтобы быть вызванным в удобное время принимающим кодом, когда это необходимо.
В JavaScript функции — это тип объектов. Как и объекты, функции можно передавать в качестве аргументов другим функциям. Поэтому функция, которая передается в качестве аргумента другой функции, называется функцией обратного вызова.
const btnAdd = document.getElementById('btnAdd'); btnAdd.addEventListener('click', function clickCallback(e) { // do something useless });
В этом примере мы ждем события клика по элементу с id btnAdd
. Если ее щелкнуть, будет выполнена функция clickCallback
. Функция обратного вызова добавляет некоторую функциональность к определенным данным или событиям.
Методы reduce
, filter
и map
в массивах требуют обратного вызова в качестве параметра. Хорошая аналогия обратного вызова — это когда вы звоните кому-то, и если они не отвечают, вы оставляете сообщение, ожидая, что вам перезвонят. Звонок кому-либо или оставление сообщения — это событие или данные, а обратный вызов — это операция, которую вы ожидаете выполнить позже.
3. Что такое модуль ES6?
Модули позволяют нам разделить нашу кодовую базу на несколько файлов для удобства сопровождения и избежать размещения всего кода в одном большом файле. До того, как в ES6 появилась поддержка модулей, было две популярных модульных системы.
- CommonJS-Node.js
- AMD (определение асинхронного модуля) — браузер
В принципе, использовать модули довольно просто. import
используется для извлечения функций или нескольких функций или значений из другого файла, а export
используется для предоставления функций или нескольких функций или значений из файла.
Экспорт
Использование ES5 (CommonJS)
// useing ES5 CommonJS - helpers.js exports.isNull = function (val) { return val === null; } exports.isUndefined = function (val) { return val === undefined; } exports.isNullOrUndefined = function (val) { return exports.isNull(val) || exports.isUndefined(val); }
Использование модулей ES6
// Using ES6 Modules - helpers.js export function isNull(val){ return val === null; } export function isUndefined(val) { return val === undefined; } export function isNullOrUndefined(val) { return isNull(val) || isUndefined(val); }
Импорт функций из другого файла
//ES5 (CommonJS) - index.js const helpers = require('./helpers.js'); // helpers is an object const isNull = helpers.isNull; const isUndefined = helpers.isUndefined; const isNullOrUndefined = helpers.isNullOrUndefined; // or if your environment supports Destructuring const { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js'); ------------------------------------------------------- // ES6 Modules - index.js import * as helpers from './helpers.js'; // helpers is an object // or import { isNull, isUndefined, isNullOrUndefined as isValid } from './helpers.js'; // using "as" for renaming named exports
Экспорт одной функции или экспорт по умолчанию в файл
ES5 (общийJS)
// ES5 (CommonJS) - index.js class Helpers { static isNull(val) { return val === null; } static isUndefined(val) { return val === undefined; } static isNullOrUndefined(val) { return this.isNull(val) || this.isUndefined(val); } } module.exports = Helpers;
Использование модулей ES6
// using ES6 Modules - helpers.js class Helpers { static isNull(val) { return val === null; } static isUndefined(val) { return val === undefined; } static isNullOrUndefined(val) { return this.isNull(val) || this.isUndefined(val); } } export default Helpers
Импорт одной функции из другого файла
Использование ES5 (CommonJS)
//ES5 (CommonJS) - index.js const Helpers = require('./helpers.js'); console.log(Helpers.isNull(null));
Использование модулей ES6
import Helpers from '.helpers.js' console.log(Helpers.isNull(null));
4. Что такое обещание?
Promise — это решение для асинхронного программирования. С точки зрения синтаксиса promise
— это объект, из которого можно получить результат асинхронной операции. Концептуально это представляет собой обязательство предоставить результат через определенный период времени. promise
имеет три состояния: pending
, fulfilled
и rejected
. После изменения состояния оно остается неизменным. После создания Promise
instance он выполняется немедленно.
fs.readFile('somefile.txt', function (e, data) { if (e) { console.log(e); } console.log(data); });
Если у нас есть другая асинхронная операция внутри обратного вызова, это приводит к проблеме. В итоге мы получаем беспорядочный и нечитаемый код. Этот код известен как «ад обратного вызова».
// callback hell fs.readFile('somefile.txt', function (e, data) { //your code here fs.readdir('directory', function (e, files) { //your code here fs.mkdir('directory', function (e) { //your code here }) }) })
Если мы используем promise
в этом коде, он будет более читабельным, понятным и удобным для сопровождения.
promReadFile('file/path') .then(data => { return promReaddir('directory'); }) .then(data => { return promMkdir('directory'); }) .catch(e => { console.log(e); })
promise
имеет три различных состояния:
- pending:начальное состояние, состояние перед выполнением или отклонением.
- выполнено:Операция успешно завершена
- отклонено: операция не удалась
Объект pending
в состоянии pending
запускает состояние fulfilled
/rejected
, передавая разрешенное значение/сообщение об ошибке в соответствующих методах обработки состояния. Когда операция успешно завершена, вызывается метод then
объекта Promise. В противном случае срабатывает метод catch
. Например:
const myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve("Success!"); }, 250); }); myFirstPromise.then((data) => { console.log("Yay! " + data); }).catch((e) => {...});
5. Что такое async/await
и как он работает?
async/await
— это новый метод в JavaScript для написания асинхронного или неблокирующего кода. Он построен на основе промисов и обеспечивает более высокую удобочитаемость и лаконичность асинхронного кода.
async/await
— это новый метод в JavaScript для написания асинхронного или неблокирующего кода. Он построен на основе промисов и обеспечивает более высокую удобочитаемость и лаконичность по сравнению с промисами и обратными вызовами. Однако, прежде чем использовать эту функцию, необходимо изучить основы промисов, потому что, как упоминалось ранее, async/await
построен на основе промисов, что означает, что он все еще использует промисы за кулисами.
Обещание
function callApi() { return fetch("url/to/api/endpoint") .then(resp => resp.json()) .then(data => { //do something with "data" }).catch(err => { //do something with "err" }); }
асинхронно/ждите
В async/await
мы используем синтаксис try/catch
для перехвата исключений.
async function callApi() { try { const resp = await fetch("url/to/api/endpoint"); const data = await resp.json(); //do something with "data" } catch (e) { //do something with "err" } }
Примечание. Использование ключевого слова «async» для объявления функции неявно возвращает обещание.
const giveMeOne = async () => 1; giveMeOne() .then((num) => { console.log(num); // logs 1 });
Примечание: ключевое слово await
можно использовать только внутри файла async function
. Использование ключевого слова await
в любой неасинхронной функции вызовет ошибку. Ключевое слово await
ожидает возврата правого выражения (которое может быть обещанием) перед выполнением следующей строки кода.
const giveMeOne = async () => 1; function getOne() { try { const num = await giveMeOne(); console.log(num); } catch (e) { console.log(e); } } // Uncaught SyntaxError: await is only valid in async function async function getTwo() { try { const num1 = await giveMeOne(); const num2 = await giveMeOne(); return num1 + num2; } catch (e) { console.log(e); } } await getTwo(); // 2
6. В чем разница между оператором спреда и оператором остатка?
Оператор расширения обозначается тремя точками …
и может преобразовывать массив в последовательность аргументов, разделенных запятыми. Проще говоря, это похоже на разбиение большого элемента на отдельные более мелкие элементы, например, как удар ладонью рассеивает твердый объект.
Оператор rest, также обозначаемый тремя точками …
, может быть похож на оператор расширения, но он используется для деструктурирования массивов и объектов. В некотором смысле оператор rest является противоположностью оператора распространения. В то время как оператор расширения «расширяет» массив на несколько элементов, оператор rest «собирает» несколько элементов и «сжимает» их в один элемент.
function add(a, b) { return a + b; }; const nums = [5, 6]; const sum = add(...nums); console.log(sum);
В этом примере мы использовали оператор распространения при вызове функции add
, которая расширила массив nums
. Следовательно, значение параметра a
равно 5, а значение параметра b
равно 6
, в результате чего sum
будет 11
.
function add(...rest) { return rest.reduce((total,current) => total + current); }; console.log(add(1, 2)); // 3 console.log(add(1, 2, 3, 4, 5)); // 15
В этом примере у нас есть функция add
, которая принимает любое количество параметров, складывает их все вместе, а затем возвращает общую сумму.
const [first, ...others] = [1, 2, 3, 4, 5]; console.log(first); // 1 console.log(others); // [2,3,4,5]
Здесь мы используем оператор rest, чтобы извлечь все оставшиеся значения массива и поместить их в другой массив, исключая первый элемент.
7. Что такое параметры по умолчанию?
Параметры по умолчанию — это новый способ определения переменных по умолчанию в JavaScript, доступный в ES6 или ECMAScript 2015.
//ES5 Version function add(a,b){ a = a || 0; b = b || 0; return a + b; } //ES6 Version function add(a = 0, b = 0){ return a + b; } add(1); // returns 1
Мы также можем использовать деструктурирование в параметрах по умолчанию.
function getFirst([first, ...rest] = [0, 1]) { return first; } getFirst(); // 0 getFirst([10,20,30]); // 10 function getArr({ nums } = { nums: [1, 2, 3, 4] }){ return nums; } getArr(); // [1, 2, 3, 4] getArr({nums:[5,4,3,2,1]}); // [5,4,3,2,1]
Мы также можем использовать ранее определенные параметры перед определением последующих параметров.
function doSomethingWithValue(value = "Hello World", callback = () => { console.log(value) }) { callback(); } doSomethingWithValue(); //"Hello World"
8. Что такое объект-обертка?
Теперь давайте рассмотрим типы данных в JavaScript. Типы данных JavaScript делятся на две основные категории: примитивные типы и ссылочные типы.
Примитивные типы: Undefined
,Null
,Boolean
,Number
,String
,Symbol
,BigInt
типы ссылок: Object
,Array
,Date
,RegExp
и т. д. Проще говоря, это объекты.
Среди ссылочных типов есть методы и свойства, которых нет в примитивных типах. Однако мы часто сталкиваемся со следующим кодом:
let name = "maxwell"; console.log(typeof name); // "string" console.log(name.toUpperCase()); // "MAXWELL"
Тип name
является типом string
и принадлежит примитивному типу, поэтому у него нет никаких свойств или методов. Однако в этом примере мы вызываем метод toUpperCase()
, который не выдает ошибку и возвращает значение строки в верхнем регистре.
Причина в том, что значение примитивного типа временно преобразуется или принудительно преобразуется в объект, поэтому поведение переменной name
похоже на поведение объекта. Каждый примитивный тип, кроме null
и undefined
, имеет свой собственный объект-оболочку: String
, Number
, Boolean
, Symbol
и BigInt
. В этом случае name.toUpperCase()
за кадром выглядит так:
console.log(new String(name).toUpperCase()); // "MAXWELL"
После доступа к свойствам или вызова методов вновь созданный объект немедленно отбрасывается.
9. В чем разница между неявным и явным преобразованием типов?
Неявное преобразование типов — это метод преобразования значения в другой тип, который выполняется автоматически без ручного вмешательства.
Предположим, у нас есть следующий пример ниже.
console.log(1 + '6'); // 16 console.log(false + true); // 1 console.log(6 * '2'); // 12
Результат первого оператора console.log
равен 16. В других языках это вызовет ошибку компиляции, но в JavaScript 1
преобразуется в строку, а затем объединяется с оператором +
. Мы ничего не делали; это автоматически делается JavaScript.
Результатом второго оператора console.log
является 1
. В JavaScript false
преобразуется в boolean
значение 0
, а true
преобразуется в 1
. Следовательно, результат 1
.
Результатом третьего оператора console.log
является 12
. Он преобразует «2» в число, а затем умножает его на 6 * 2
, в результате чего получается 12
.
Явное приведение типа, с другой стороны, — это метод преобразования значения в другой тип, когда нам нужно выполнить преобразование вручную.
console.log(1 + parseInt('6'));
В этом примере мы используем функцию parseInt для преобразования «6» в число, а затем используем оператор +, чтобы сложить 1 и 6 вместе.
10. Что такое NaN? И как проверить, является ли значение NaN?
NaN
, сокращение от «Не число», — это значение в JavaScript, возникающее в результате числовых операций или преобразований, которые не могут дать осмысленное числовое значение. Поэтому, когда числовая операция или преобразование дает нечисловое значение, результатом будет NaN
.
let a; console.log(parseInt('abc')); // NaN console.log(parseInt(null)); // NaN console.log(parseInt(undefined)); // NaN console.log(parseInt(++a)); // NaN console.log(parseInt({} * 10)); // NaN console.log(parseInt('abc' - 2)); // NaN console.log(parseInt(0 / 0)); // NaN console.log(parseInt('10a' * 10)); // NaN
В JavaScript есть встроенный метод isNaN
, используемый для проверки того, равно ли значение NaN
. Однако эта функция демонстрирует своеобразное поведение.
console.log(isNaN()); // true console.log(isNaN(undefined)); // true console.log(isNaN({})); // true console.log(isNaN(String('a'))); // true console.log(isNaN(() => { })); // true
Все эти операторы console.log
возвращают true
, даже если значения, которые мы передаем, не равны NaN
.
В ES6 рекомендуется использовать метод Number.isNaN
, поскольку он действительно проверяет, равно ли значение NaN
. В качестве альтернативы мы можем создать собственную вспомогательную функцию для проверки этой проблемы, так как в JavaScript NaN
— единственное значение, которое не равно самому себе.
function checkIfNaN(value) { return value !== value; }
11. Как определить, является ли значение массивом?
Мы можем использовать метод Array.isArray
, чтобы проверить, является ли значение массивом. При передаче аргумента, который является массивом, он возвращает true
; в противном случае возвращается false
.
console.log(Array.isArray(5)); // false console.log(Array.isArray("")); // false console.log(Array.isArray()); // false console.log(Array.isArray(null)); // false console.log(Array.isArray({ length: 5 })); // false console.log(Array.isArray([])); // true
Если среда не поддерживает этот метод, вы можете реализовать файл polyfill
.
function isArray(value){ return Object.prototype.toString.call(value) === "[object Array]" }
Конечно, можно использовать и традиционные методы:
let a = [] if (a instanceof Array) { console.log('is an array') } else { console.log('Non-Arrays') }
12. Как проверить, существует ли свойство в объекте?
Есть три способа проверить, существует ли свойство в объекте.
Первый способ — использовать in
operator:
const o = { "prop" : "rabbit", "prop2" : "tiger" }; console.log("prop" in o); // true console.log("prop1" in o); // false
Второй метод заключается в использовании метода hasOwnProperty
. Метод hasOwnProperty()
возвращает значение boolean
value, указывающее, имеет ли объект указанное свойство как прямое свойство (не унаследованное).
console.log(o.hasOwnProperty("prop2")); // true console.log(o.hasOwnProperty("prop1")); // false
Третий способ заключается в использовании записи в квадратных скобках obj[‘prop’]
. Если свойство существует, оно вернет значение этого свойства; в противном случае он вернет undefined
.
console.log(o["prop"]); // "rabbit" console.log(o["prop1"]); // undefined
Дополнительные материалы на PlainEnglish.io.
Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .