Skip to content

JavaScript

JavaScript

Які існують примітивні типи даних у JS?

У JavaScript існує сім примітивних типів даних

  • number - використовується для представлення чисел, як цілих, так і дробових. Цей тип дозволяє виконувати математичні операції та працювати з числовими значеннями в межах, визначених специфікацією мови. Наприклад, let age = 25; створює змінну із числовим значенням.
  • string - служить для зберігання текстової інформації. Рядки можуть бути укладені в одинарні, подвійні або зворотні лапки. Цей тип даних дозволяє працювати з текстом, проводити маніпуляції та виконувати операції конкатенації. Наприклад, let greeting = "Hello, world!"; створює рядок із вітальним повідомленням.
  • boolean - представляє логічні значення true та false, які використовуються для управління потоком виконання програм, перевірки умов та прийняття рішень.
  • null - спеціальний тип даних, що позначає відсутність значення.
  • undefined - позначає невизначене значення.
  • symbol - представляє унікальні і незмінні ідентифікатори, які часто застосовуються для створення прихованих властивостей об'єктів
  • bigint - дозволяє працювати з цілими числами довільної довжини, що є корисним для обчислень з дуже великими числами, що виходять за межі діапазону типу number.

Ці типи є базовими значеннями і мають ряд особливостей, таких як незмінність і копіювання за значенням.

let bigNumber = 9007199254740991n; // BigInt для великих цілих чисел
let isActive = true; // Boolean
let userName = "Alice"; // String
let result = null; // Null
let невизначено; // Undefined
let uniqueId = Symbol("id"); //Symbol

У чому різниця між null і undefined?

Summary

null і undefined у JavaScript обидва представляють відсутність значення, але null використовується для явної вказівки на порожнє чи неіснуюче значення, тоді як undefined означає, що змінна була оголошена, але їй не було надано жодного значення.

Різниця між null і undefined полягає в їхньому призначенні та використанні. null є ключовим словом, яке свідчить про навмисну відсутність будь-якого значення. Зазвичай використовується для явного надання змінній значення "нічого" або скидання значення об'єкта. Наприклад, коли об'єкт більше не потрібен, можна присвоїти змінної, що зберігає його, значення null, щоб вказати на відсутність об'єкта.

undefined виникає, коли змінна була оголошена, але їй не було надано ніякого значення. Це значення автоматично присвоюється змінним при створенні, якщо їм явно не призначено інше значення. Воно також повертається функцією, якщо вона не має оператора return, або якщо в об'єкті відсутня потрібна властивість.

console.log(null == undefined) // true
console.log(null === undefined) // false

У першому прикладі null і undefined вважаються рівними, тому що вони обидва являють собою «відсутність значення». Однак за суворого порівняння вони вважаються різними, тому що вони різні за типом.

Що таке NaN?

Summary

NaN (Not-a-Number) — це спеціальне значення JavaScript, що позначає результат недопустимої або нечислової операції з числами. Воно належить до типу number. Перевірити, чи дорівнює змінній NaN, можна за допомогою функції isNaN() або методу Number.isNaN().

NaN виникає під час виконання операцій, які можуть повернути дійсне числове значення. Приклади таких операцій включають ділення нуля на нуль, спробу перетворити некоректний рядок на число та інші неприпустимі математичні операції. Незважаючи на назву, NaN має тип number і є частиною стандарту IEEE 754 для представлення чисел з плаваючою комою.

Особливість NaN полягає в тому, що воно не рівне нічому, включаючи само себе. Перевірка NaN === NaN завжди повертає false, що відрізняє його від інших значень JavaScript. Для визначення, чи є значення NaN, використовуються спеціальні функції та методи. Функція isNaN() перевіряє, чи є значення нечисловим, але має недоліки, оскільки намагається перетворити аргумент на число перед перевіркою. Більш надійним методом є Number.isNaN(), який точно перевіряє, чи значення NaN.

// Example 1: Creating NaN
let myNaN = Number("hello"); // Trying to convert a string to a number, resulting in NaN
console.log(myNaN); // NaN

// Example 2: Testing for NaN using isNaN()
if (isNaN(myNaN)) {
    console.log("The variable myNaN is NaN");
} else {
    console.log("The variable myNaN is not NaN");
}

// Example 3: Testing for NaN using the === operator
if (myNaN === NaN) {
    console.log("The variable myNaN is NaN"); // This block of code will not execute because NaN !== NaN
} else {
    console.log("The variable myNaN is not NaN");
}

// Example 4: Special rule for checking for NaN using the === operator
if (Number.isNaN(myNaN)) {
    console.log("The variable myNaN is NaN");
} else {
    console.log("The variable myNaN is not NaN");
}

У чому різниця між операторами == та === ?

Summary

Оператори == і === JavaScript розрізняються тим, що == виконує несуворе порівняння з приведенням типів, тоді як === виконує суворе порівняння без приведення типів.

Оператор == виконує несуворе порівняння, що означає, що порівнювані значення призводять до загального типу. Це може призвести до несподіваних результатів, оскільки JavaScript автоматично перетворює типи даних для порівняння. Наприклад, рядок "5" і число 5 будуть вважатися рівними при використанні оператора == , тому що рядок буде приведений до числа.

console.log("5" == 5); // true

У цьому прикладі рядок "5" приводиться до числа, і порівняння повертає true, хоча типи даних різні.

Оператор === виконує суворе порівняння, перевіряючи як значення, так і тип даних. Це означає, що два значення вважаються рівними тільки в тому випадку, якщо вони мають однаковий тип і те саме значення. Суворе порівняння запобігає ненавмисним результатам, викликаним автоматичним приведенням типів, роблячи код більш передбачуваним і надійним.

console.log("5" === 5); // false

Тут рядок "5" та число 5 не рівні, тому що їх типи різні, і оператор === повертає false.

Для чого використовується оператор !! ?

Summary

Оператор !! в JavaScript використовується для приведення значення до логічного типу (boolean), ефективно перетворюючи будь-яке значення на true або false.

Оператор !! застосовується для перетворення значення на булеве (логічне) значення. Перший оператор ! (логічне НЕ) перетворює значення на його протилежне булеве представлення, а другий ! знову інвертує його, повертаючи вихідне булеве значення. Таким чином, будь-який truthy (справжній) або falsy (хибний) результат можна явно привести до true або false.

let value = "Hello";
let booleanValue = !!value; // true

Тут рядок "Hello" є truthy значенням, та застосування !! повертає true.

Як використовується оператор &&?

Summary

Оператор && (логічне І) в JavaScript використовується для виконання логічних операцій, повертаючи перше хибне значення або останнє справжнє значення, що робить його корисним для перевірки декількох умов.

Оператор && виконує логічні операції між двома операндами. Він повертає перше хибне значення, якщо є, або останнє справжнє значення, якщо всі операнди істинні. Ця властивість дозволяє використовувати оператор && для перевірки кількох умов одночасно, гарантуючи, що всі умови мають бути дійсними для виконання певного блоку коду.

let isLoggedIn = true;
let hasPermission = true;
if (isLoggedIn && hasPermission) {
 console.log("Access granted");
}

Оператор && також використовується для виконання логічного скорочення (short-circuit evaluation). Це означає, що якщо перший операнд хибний, то другий операнд не обчислюється і відразу повертається перший операнд. Ця властивість корисна для запобігання зайвим операціям або викликам функцій, якщо умова вже помилкова. Наприклад, можна переконатися, що об'єкт існує, перш ніж спробувати отримати доступ до його властивості.

let user = {name: "Alice"};
let userName = user && user.name; // 'Alice'

Тут вираз user&&user.name спочатку перевіряє, чи існує об'єкт user. Якщо user дорівнює null або undefined, вираз зупиниться і поверне це значення, уникаючи помилок при спробі доступу до властивості name.

Як використовується оператор ||?

Summary

Оператор || (логічне АБО) в JavaScript використовується для виконання логічних операцій, повертаючи перше істинне значення або останнє помилкове значення, що робить його корисним для встановлення значень за умовчанням і перевірки умов.

Оператор || виконує логічні операції між двома операндами. Якщо перший істинний операнд (truthy), він повертається, інакше повертається другий операнд. Ця властивість дозволяє використовувати оператор || для встановлення значень за замовчуванням. Наприклад, якщо змінна не ініціалізована або має хибне значення (falsy), можна надати їй значення за замовчуванням.

Оператор || також широко використовується в умовних висловлюваннях для перевірки кількох умов. Якщо хоча б одна з умов є істинною, вираз повертає істину, що дозволяє спростити перевірку складних логічних умов. У випадках, коли необхідно перевірити безліч змінних або виразів, цей оператор допомагає зробити код лаконічнішим і зрозумілішим.

Працюючи з функціями оператор || може використовуватися для забезпечення безпечного виконання коду, підставляючи значення за замовчуванням, якщо аргументи функції не були передані. Це особливо корисно при роботі з функціями, які можуть приймати невизначену кількість параметрів або коли параметри можуть бути необов'язковими.

console.log(null || 1 || undefined) // 1
function logName(name) {
    let n = name || Mark
    console.log(n)
}
logName() // Mark

Що таке функція зворотного виклику (callback function)?

Summary

Функція зворотного виклику (callback function) - це функція, яка передається як аргумент іншої функції і викликається цією функцією в певний момент часу або після завершення виконання завдання.

Функції зворотного виклику використовуються для обробки асинхронних операцій, виконання коду після завершення виконання певної операції, обробки подій та реалізації механізму зворотного виклику у різних аспектах розробки.

function fetchData(callback) {
    setTimeout(() => {
        const data = { name: 'John', age: 30};
        callback(data);
    }, 1000);
}
function processData(data) {
    console.log(`Name: ${data.name}, Age: ${data.age}`);
}
fetchData(processData);

У цьому прикладі функція fetchData приймає функцію зворотного виклику callback, яка викликається після того, як дані будуть отримані асинхронно. Функція processData використовується як функція зворотного виклику та обробляє отримані дані.

Що робить метод slice?

Summary

Метод slice в JavaScript використовується для вилучення частини масиву або рядка, не змінюючи оригінал. Він повертає новий масив або рядок, який містить елементи або символи від зазначеного початкового індексу до кінцевого (не включаючи його).

Метод slice дозволяє створити новий масив або рядок, копіюючи елементи або символи з вихідного масиву або рядка, починаючи з вказаного початкового індексу та закінчуючи (але не включаючи) вказаним кінцевим індексом. Вихідний масив чи рядок залишаються незмінними.

array.slice(begin, end)
string.slice(begin, end)
  • begin – індекс, з якого починається вилучення.
  • end (необов'язковий) - індекс, на якому закінчується вилучення (не включаючи його).
let fruits = ['apple', 'banana', 'cherry', 'date', 'fig', 'grape'];
// Extracting a part of the array from index 1 to 3 (excluding 3)
let slicedFruits = fruits.slice(1, 3);
console.log(slicedFruits); // ['banana', 'cherry']
console.log(fruits); // ['apple', 'banana', 'cherry', 'date', 'fig', 'grape'] - the original array is not changed

let text = 'Hello, World!';
// Extracting a part of the string from index 7 to the end of the string
let slicedText = text.slice(7);
console.log(slicedText); // 'World!'
console.log(text); // 'Hello, World!' - original string is not changed

Що робить метод splice?

Summary

Метод splice в JavaScript використовується для зміни вмісту масиву шляхом видалення, заміни або додавання елементів в масив за вказаними індексами.

Метод splice дозволяє видаляти існуючі елементи, додавати нові елементи або замінювати елементи масиву. Метод змінює сам масив та повертає масив видалених елементів.

array.splice(start, deleteCount, item1, item2, ...)
  • start - індекс, з якого починається зміна масиву.
  • deleteCount — кількість елементів для видалення (якщо 0, елементи не видаляються).
  • item1, item2, ... — елементи, які будуть додані до масиву, починаючи з індексу start.
let fruits = ['apple', 'banana', 'cherry', 'date'];

// Remove two elements starting at index 1
let removed = fruits.splice(1, 2);
console.log(fruits); // ['apple', 'date']
console.log(removed); // ['banana', 'cherry']

// Add elements to the array starting at index 1
fruits.splice(1, 0, 'banana', 'cherry');
console.log(fruits); // ['apple', 'banana', 'cherry', 'date']

// Replace two elements starting at index 1
fruits.splice(1, 2, 'blackberry', 'blueberry');
console.log(fruits); // ['apple', 'blackberry', 'blueberry', 'date']

У чому різниця між Array.prototype.forEach та Array.prototype.map?

Summary

Array.prototype.forEach і Array.prototype.map - це методи JavaScript для роботи з масивами. forEach виконує функцію зворотного виклику для кожного елемента масиву, нічого не повертаючи, тоді як map створює новий масив, що містить результати виклику функції зворотного виклику для кожного елемента вихідного масиву.

Обидва Array.prototype.forEach і Array.prototype.map дозволяють виконувати функцію зворотного виклику для кожного елемента масиву. Однак вони мають різну поведінку та повертають різні результати.

Метод forEach використовується для виконання будь-якої операції для кожного елемента масиву. Він проходить по кожному елементу та викликає функцію зворотного виклику, передаючи їй поточний елемент, індекс та сам масив. forEach нічого не повертає, його основна мета - виконати якусь дію для кожного елемента.

З іншого боку, метод map створює новий масив, що містить результати виклику функції зворотного виклику кожного елемента вихідного масиву. Тобто він застосовує функцію зворотного виклику до кожного елемента вихідного масиву та повертає новий масив із результатами. Вихідний масив у своїй не змінюється.

const numbers = [1, 2, 3, 4, 5];
numbers.forEach((num, index) => {
    console.log(`Number at index ${index}: ${num}`);
});

const squares = numbers.map(num => num * num);
console.log(squares); // [1, 4, 9, 16, 25]

Що робить метод trim?

Summary

Метод trim в JavaScript використовується для обрізки пробілів з обох сторін рядка. Для обрізки тільки початку або кінця рядка можна використовувати методи trimStart/trimLeft та trimEnd/trimRight відповідно.

Метод trim видаляє пробіли та інші пробільні символи (табуляція та нові рядки) з початку і кінця рядка. Цей метод не змінює вихідний рядок, а повертає новий рядок з видаленими пробілами з обох кінців.

let str = ' Hello, World! ';
let trimmedStr = str.trim();
console.log(trimmedStr); // Outputs: 'Hello, World!'

Якщо потрібно обрізати пробіли тільки з початку рядка, використовується метод trimStart (або синонім trimLeft). Цей метод видаляє пробіли лише з початку рядка. Для обрізки пробілів тільки з кінця рядка використовується метод trimEnd (або синонім trimRight). Цей метод видаляє прогалини лише з кінця рядка.

let str = ' Hello, World! ';
let trimmedStartStr = str.trimStart();
console.log(trimmedStartStr); // Outputs: 'Hello, World! '

let str = ' Hello, World! ';
let trimmedEndStr = str.trimEnd();
console.log(trimmedEndStr); // Outputs: ' Hello, World!'

Що таке функції вищого порядку (Higher Order Functions)?

Summary

Функції вищого порядку (Higher Order Functions) у програмуванні – це функції, які можуть приймати інші функції як аргументи або повертати функції як результат.

Функції вищого порядку (Higher Order Functions) є концептом функціонального програмування. Вони можуть приймати інші функції як аргументи або повертати функції як результат. Це означає, що функції можуть бути використані як дані, так само, як і будь-які інші типи даних, такі як рядки, числа та об'єкти.

Приклади функцій вищого порядку в JavaScript включають функції map, filter, reduce, які приймають інші функції як аргументи для перетворення, фільтрації та агрегації елементів масиву.

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(num) {
    return num * 2;
});
console.log(doubled); // Outputs: [2, 4, 6, 8, 10]

Тут функція map є функцією вищого порядку, тому що вона приймає функцію як аргумент для перетворення кожного елемента масиву. Функція, передана в map, виконується кожного елемента масиву і повертає нове значення, яке потім додається в результуючий масив.

У чому сенс обертання всього вмісту JavaScript-файлу в функцію?

Summary

Обертання всього вмісту JavaScript-файлу в функцію це практика, яка називається "модульним шаблоном" або "IIFE" (Immediately Invoked Function Expression). Це дозволяє створювати локальну область видимості для змінних та функцій, ізолюючи їх від глобальної області видимості, і запобігає конфліктам імен з іншими скриптами.

Обертання всього вмісту JavaScript-файлу у функцію поширена практика у веб-розробці. Ця практика полягає в тому, що весь код скрипта обертається в анонімну функцію, яка одразу викликається.

(function() {
 // code
})();

Основна ціль цієї практики - створити локальну область видимості для змінних і функцій всередині функції, ізолюючи їх від глобальної області видимості. Це допомагає запобігти конфліктам імен між різними скриптами на сторінці та захищає код від зовнішніх впливів.

Крім ізоляції змінних, використання модульного шаблону також може забезпечити безпеку і поліпшити продуктивність, оскільки змінні, оголошені всередині функції, не доступні ззовні, і JavaScript може краще оптимізувати код.

Також це може бути корисно для організації коду, роблячи його більш читаним та легко підтримуваним, дозволяючи групувати логічно пов'язані частини коду в окремі модулі.

Що таке замикання (Closures)?

Summary

Замикання (Closures) - це особливість, яка дозволяє функції зберігати доступ до змінних із зовнішньої видимості, навіть після завершення виконання зовнішньої функції.

Замикання (Closures) в JavaScript виникає тоді, коли функція має доступ до змінних із зовнішньої області видимості навіть після завершення виконання зовнішньої функції. Іншими словами, функція "запам'ятовує" свою навколишню область видимості під час оголошення і продовжує мати доступ до цих змінних надалі, навіть якщо функція, що викликається, завершила свою роботу.

function outerFunction() {
    let outerVariable = 'I am from outer function';
    function innerFunction() {
        console.log(outerVariable);
    }
    return innerFunction;
}
const closureExample = outerFunction();
closureExample(); // "I am from outer function"

У цьому прикладі функція innerFunction є замиканням, тому що вона має доступ до змінної outerVariable із зовнішньої функції outerFunction, навіть після того, як outerFunction вже завершила своє виконання. Це тому, що замикання зберігає посилання зовнішні змінні, з якими воно було створено, у своїй лексичній області видимості.

Що таке ECMAScript?

Summary

ECMAScript (ES) – це стандартний набір правил, визначень та специфікацій, що описує мову програмування JavaScript.

ECMAScript (ES) є стандартом, який визначає мову програмування JavaScript. Він визначає синтаксис, типи даних, структури управління, оператори та інші основні елементи мови. Перша версія стандарту була випущена в 1997 році, і з того часу він продовжує розвиватися та оновлюватись. Основні зміни та нові можливості додаються до мови через нові версії стандарту ECMAScript, такі як ES6 (або ECMAScript 2015), ES7, ES8 тощо.

Наприклад, стандарт ECMAScript 2015 (ES6) привніс багато нових можливостей у мову, таких як стрілкові функції, класи, блочна область видимості змінних let та const, деструктуризація, шаблонні рядки та багато іншого. У подальших версіях стандарту були додані додаткові функції, такі як async/await для керування асинхронним кодом, оператори розповсюдження (spread operator) та рест (rest operator), покращені методи масивів та багато іншого.

ECMAScript є основою для розробки мови JavaScript, і її стандарти визначають поведінку та можливості мови. Хоча стандарт визначає синтаксис та функціональність мови, конкретні реалізації мови, такі як V8 (використовується в браузері Google Chrome) або Node.js, можуть розширювати його можливості або вносити свої зміни.

Що нового привніс у JS стандарт ES6 чи ECMAScript2015?

Summary

Стандарт ES6, або ECMAScript 2015, привніс до JavaScript стрілкові функції, блокову область видимості змінних let та const, деструктуризацію, шаблонні рядки, класи, модулі.

Однією із ключових змін було додавання стрілкових функцій, які пропонують більш короткий та зручний синтаксис для оголошення функцій.

const add = (a, b) => a + b;

В ES6 також були введені блочна область видимості змінних let і const, які пропонують більш передбачувану поведінку порівняно з var, особливо щодо області видимості всередині блоків коду.

Деструктуризація - ще одна важлива особливість ES6, яка дозволяє отримувати значення з об'єктів та масивів зручнішим способом.

const person = { name: 'John', age: 30};
const {name, age} = person;
console.log(name, age); // John 30

Інші значні зміни в ES6 включають шаблонні рядки, класи, покращені можливості роботи з об'єктами, оператор розширення (spread operator), а також нові методи масивів і рядків. Введення модульної системи дозволяє легко організовувати код у незалежні компоненти та підвищує його перевикористання.

У чому різниця між ключовими словами var, let та const?

Summary

Ключові слова var, let та const використовуються для оголошення змінних у JavaScript, але мають відмінності в області видимості та можливості перепризначення та перевизначення значень.

Відмінності Область видимості - var: Область видимості змінної, оголошеної з використанням var, обмежена найближчою функцією або глобальною областю видимості, якщо змінна оголошена поза функцією - let та const: Змінні, оголошені з використанням let та const, мають блочну область видимості, вони доступні тільки всередині блоку, в якому були оголошені

Перепризначення та перевизначення - var: Змінні, оголошені з використанням var, можуть бути перевизначені та перепризначені - let: Змінні, оголошені з використанням let, можуть бути лише перепризначені - const: Змінні, оголошені з використанням const, не можуть бути перевизначені, тобто їхнє значення не може бути змінено після присвоєння. Однак об'єкти та масиви, оголошені з використанням const, можуть змінювати свої властивості та елементи відповідно

var a = 10;
let b = 20;
const c = 30;

console.log(a); // Output: 10
console.log(b); // Output: 20
console.log(c); // Output: 30

a = 15; // Reassignment for variable var
b = 25; // Reassignment for variable let
// c = 35; // Error: const variable cannot be reassigned

console.log(a); // Output: 15
console.log(b); // Output: 25
console.log(c); // Output: 30 (value unchanged)

Що таке модулі (modules) у JavaScript?

Summary

Модулі (modules) - це механізм організації коду, що дозволяє розділити програму на незалежні компоненти, що перевикористовуються. Кожен модуль має власну область видимості, що дозволяє ізолювати змінні та функції від інших частин програми.

Модулі в JavaScript є набором функцій і змінних, обернутих в єдиний контейнер, який можна експортувати з одного файлу і імпортувати в іншому. Це дозволяє створювати незалежні компоненти, які можна перевикористовувати, спрощує підтримку та масштабування коду. Однією з ключових особливостей модулів є їхня власна область видимості, що дозволяє ізолювати змінні та функції від інших частин програми. Це допомагає запобігти конфліктам імен і робить код більш надійним і зрозумілим.

Для визначення модуля JavaScript часто використовується ключове слово export, яке дозволяє експортувати змінні або функції з модуля, щоб вони стали доступними для використання в інших модулях.

// myModule.js
export const myVariable = 10;

Потім інші модулі можуть імпортувати цю змінну за допомогою ключового слова import

// otherModule.js
import { myVariable } from './myModule.js';
console.log(myVariable); // 10

Які переваги та недоліки у використання Promise API замість зворотних викликів?

Summary

Переваги використання Promise API замість зворотних викликів - краща читаємість, керування послідовністю асинхронних операцій та легкість обробки помилок. Однак використання Promise API може призвести до збільшення складності у разі великої кількості асинхронних операцій та потребує підтримки сучасних браузерів або середовищ виконання.

Переваги використання Promise API

  • Читання коду: Promise API дозволяє писати асинхронний код у більш лінійному та читаному стилі завдяки використанню методів .then() та .catch() для обробки успішних та помилкових результатів.
  • Управління послідовністю операцій: Promise API надає зручні методи для управління послідовністю асинхронних операцій, такі як Promise.all(), Promise.race() та Promise.allSettled(), що робить процес синхронізації та обробки результатів зручнішим та ефективнішим.
  • Обробка помилок: Promise API дозволяє обробляти помилки за допомогою методу .catch(), що спрощує відстеження та обробку помилок в асинхронному коді.

Недоліки використання Promise API

  • Збільшення складності: У разі великої кількості асинхронних операцій або ланцюжків викликів Promise API може призвести до збільшення складності коду через необхідність вкладених ланцюжків .then().
  • Потрібна підтримка сучасних браузерів: Використання Promise API вимагає підтримки сучасних браузерів або середовищ виконання, що може обмежити сумісність програми з старішими версіями браузерів або середовищ виконання JavaScript.
  • Не можна скасувати або призупинити виконання: Promise API не підтримує скасування або призупинення асинхронних операцій безпосередньо, що може бути проблемою в деяких сценаріях.
// Example with callbacks
function fetchDataWithCallback(url, callback) {
    // Simulate asynchronous data loading
    setTimeout(() => {
        const data = { name: 'John', age: 30 };
        callback(null, data); // Return data to the callback
    }, 1000);
}

// Call a function with a callback
fetchDataWithCallback('https://api.example.com/data', (err, data) => {
    if (err) {
        console.error('Error:', err);
    } else {
        console.log('Data:', data);
    }
});

// Example using Promise API
function fetchDataWithPromise(url) {
    return new Promise((resolve, reject) => {
        // Simulate asynchronous data loading
        setTimeout(() => {
            const data = { name: 'John', age: 30 };
            resolve(data); // Return data via resolve
        }, 1000);
    });
}

// Calling a function with Promise API
fetchDataWithPromise('https://api.example.com/data')
    .then(data => {
        console.log('Data:', data);
    })
    .catch(error => {
        console.error('Error:', error);
    });