Часть 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 .