Представь: ты строишь страницу каталога с фильтрами — категория, ценовой диапазон, сортировка, страница. Состояние фильтров нужно сохранять в URL (?category=phones&price_min=5000&page=2), чтобы пользователь мог поделиться ссылкой. Вручную конкатенировать строки URL надёжно? Нет. URL API — правильный инструмент.
Ручная сборка URL через конкатенацию строк ломается на пробелах, кириллице и спецсимволах. URL API автоматически экранирует значения, корректно обрабатывает все части URL и позволяет изменять отдельные компоненты не затрагивая остальные.
const url = new URL('https://shop.example.com/catalog?category=phones&page=2#reviews')
console.log(url.href) // 'https://shop.example.com/catalog?category=phones&page=2#reviews'
console.log(url.origin) // 'https://shop.example.com'
console.log(url.protocol) // 'https:'
console.log(url.host) // 'shop.example.com'
console.log(url.hostname) // 'shop.example.com' (без порта)
console.log(url.port) // '' (80/443 не показываются)
console.log(url.pathname) // '/catalog'
console.log(url.search) // '?category=phones&page=2'
console.log(url.hash) // '#reviews'Все свойства URL доступны для записи:
const url = new URL('https://example.com/old-path')
url.pathname = '/new-path'
url.search = '?version=2'
url.hash = '#section-1'
console.log(url.href)
// 'https://example.com/new-path?version=2#section-1'const url = new URL('https://api.example.com/products?sort=price&order=asc')
const params = url.searchParams // объект URLSearchParams
// Чтение
console.log(params.get('sort')) // 'price'
console.log(params.get('order')) // 'asc'
console.log(params.get('missing')) // null
console.log(params.has('sort')) // true
// Изменение
params.set('sort', 'name') // заменить значение
params.append('tag', 'sale') // добавить параметр (может дублироваться)
params.delete('order') // удалить параметр
console.log(url.href)
// 'https://api.example.com/products?sort=name&tag=sale'
// Итерация по параметрам
for (const [key, value] of params) {
console.log(key, '=', value)
}
// Все значения для одного ключа (если append использовался несколько раз)
const tags = params.getAll('tag') // ['sale']URL умеет резолвить относительные пути:
const base = new URL('https://example.com/api/v1/')
const endpoint = new URL('./users', base)
console.log(endpoint.href) // 'https://example.com/api/v1/users'
const absolute = new URL('/admin', base)
console.log(absolute.href) // 'https://example.com/admin'// Создать ссылку для скачивания файла
function downloadFile(content, filename, type = 'text/plain') {
const blob = new Blob([content], { type })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
a.click()
// Обязательно освобождаем память после использования
URL.revokeObjectURL(url)
}
downloadFile('{ "data": 42 }', 'result.json', 'application/json')function buildProductsURL(baseURL, filters) {
const url = new URL(baseURL)
if (filters.category) url.searchParams.set('category', filters.category)
if (filters.minPrice) url.searchParams.set('price_gte', filters.minPrice)
if (filters.maxPrice) url.searchParams.set('price_lte', filters.maxPrice)
if (filters.page) url.searchParams.set('page', filters.page)
if (filters.sort) url.searchParams.set('sort', filters.sort)
return url.href
}
const apiURL = buildProductsURL('https://api.example.com/products', {
category: 'smartphones',
minPrice: 10000,
maxPrice: 80000,
page: 2,
sort: 'price_asc',
})
console.log(apiURL)
// 'https://api.example.com/products?category=smartphones&price_gte=10000&price_lte=80000&page=2&sort=price_asc'1. Строить URL конкатенацией строк — спецсимволы ломают запрос
// ПЛОХО — пробелы и кириллица в параметрах ломают URL
const city = 'Нижний Новгород'
const url = '/api/weather?city=' + city // /api/weather?city=Нижний Новгород — сломано!
// ХОРОШО — URLSearchParams экранирует автоматически
const params = new URLSearchParams({ city })
const url2 = '/api/weather?' + params.toString()
// /api/weather?city=%D0%9D%D0%B8%D0%B6%D0%BD%D0%B8%D0%B9+%D0%9D%D0%BE%D0%B2%D0%B3%D0%BE%D1%80%D0%BE%D0%B42. Передавать относительный URL без базы в new URL()
// ПЛОХО — без базы относительный URL выбросит TypeError
const url = new URL('/api/users') // TypeError: Failed to construct 'URL': Invalid URL
// ХОРОШО — передай базу вторым аргументом
const url2 = new URL('/api/users', 'https://example.com')
console.log(url2.href) // 'https://example.com/api/users'
// Или используй window.location.origin в браузере
const url3 = new URL('/api/users', window.location.origin)3. Забыть revokeObjectURL — утечка памяти при работе с Blob
// ПЛОХО — URL объект в памяти до перезагрузки страницы
const blob = new Blob([data], { type: 'text/csv' })
const url = URL.createObjectURL(blob)
link.href = url
link.click()
// URL.revokeObjectURL(url) забыт — утечка памяти!
// ХОРОШО — освобождаем сразу после использования
link.click()
URL.revokeObjectURL(url) // освобождаем памятьnew URL() для парсинга текущего адреса и навигацииURL и URLSearchParams для безопасной сборки запросов с параметрамиURL.createObjectURL(blob) для генерации ссылки без серверного запросаURL API: парсинг URL, работа с URLSearchParams, построение API-запросов с фильтрами
// URL API доступен в современных sandbox-окружениях
// Используем реальный класс URL
// --- Демо 1: Разбор URL на части ---
console.log('=== Парсинг URL ===')
const shopUrl = new URL('https://shop.example.com:8080/catalog/phones?brand=apple&sort=price&page=3#specs')
console.log('href: ', shopUrl.href)
console.log('origin: ', shopUrl.origin) // 'https://shop.example.com:8080'
console.log('protocol: ', shopUrl.protocol) // 'https:'
console.log('host: ', shopUrl.host) // 'shop.example.com:8080'
console.log('hostname: ', shopUrl.hostname) // 'shop.example.com'
console.log('port: ', shopUrl.port) // '8080'
console.log('pathname: ', shopUrl.pathname) // '/catalog/phones'
console.log('search: ', shopUrl.search) // '?brand=apple&sort=price&page=3'
console.log('hash: ', shopUrl.hash) // '#specs'
// --- Демо 2: URLSearchParams ---
console.log('\n=== URLSearchParams ===')
const params = shopUrl.searchParams
console.log('brand:', params.get('brand')) // 'apple'
console.log('sort:', params.get('sort')) // 'price'
console.log('page:', params.get('page')) // '3'
console.log('has(brand):', params.has('brand')) // true
console.log('has(color):', params.has('color')) // false
console.log('get(missing):', params.get('missing')) // null
// Изменение параметров
params.set('page', '4')
params.append('tag', 'sale')
params.append('tag', 'new')
params.delete('sort')
console.log('\nПосле изменений:')
console.log('page:', params.get('page')) // '4'
console.log('tag (все):', params.getAll('tag')) // ['sale', 'new']
console.log('sort:', params.get('sort')) // null — удалён
console.log('URL после:', shopUrl.href)
// Итерация
console.log('\nВсе параметры:')
for (const [key, value] of shopUrl.searchParams) {
console.log(` ${key} = ${value}`)
}
// --- Демо 3: Построение API URL ---
console.log('\n=== Построение URL для API ===')
function buildApiURL(base, filters) {
const url = new URL(base)
const validFilters = {
category: v => url.searchParams.set('category', v),
minPrice: v => url.searchParams.set('price_gte', v),
maxPrice: v => url.searchParams.set('price_lte', v),
inStock: v => url.searchParams.set('in_stock', v ? '1' : '0'),
page: v => url.searchParams.set('page', v),
limit: v => url.searchParams.set('limit', v),
sort: v => url.searchParams.set('sort', v),
tags: v => v.forEach(t => url.searchParams.append('tags', t)),
}
for (const [key, value] of Object.entries(filters)) {
if (value !== undefined && value !== null && validFilters[key]) {
validFilters[key](value)
}
}
return url
}
const apiUrl = buildApiURL('https://api.myshop.ru/v2/products', {
category: 'laptops',
minPrice: 50000,
maxPrice: 150000,
inStock: true,
page: 1,
limit: 20,
sort: 'price_asc',
tags: ['gaming', 'sale'],
})
console.log('API URL:')
console.log(apiUrl.href)
console.log('\nПараметры по отдельности:')
for (const [k, v] of apiUrl.searchParams) {
console.log(` ${k}: ${v}`)
}
// --- Демо 4: Относительные URL ---
console.log('\n=== Относительные URL ===')
const base = new URL('https://api.example.com/v2/users/123/')
const relative1 = new URL('./profile', base)
const relative2 = new URL('../posts', base)
const relative3 = new URL('/admin/settings', base)
console.log('base: ', base.href)
console.log('./profile: ', relative1.href) // ...users/123/profile
console.log('../posts: ', relative2.href) // ...v2/users/posts
console.log('/admin: ', relative3.href) // ...example.com/admin/settingsПредставь: ты строишь страницу каталога с фильтрами — категория, ценовой диапазон, сортировка, страница. Состояние фильтров нужно сохранять в URL (?category=phones&price_min=5000&page=2), чтобы пользователь мог поделиться ссылкой. Вручную конкатенировать строки URL надёжно? Нет. URL API — правильный инструмент.
Ручная сборка URL через конкатенацию строк ломается на пробелах, кириллице и спецсимволах. URL API автоматически экранирует значения, корректно обрабатывает все части URL и позволяет изменять отдельные компоненты не затрагивая остальные.
const url = new URL('https://shop.example.com/catalog?category=phones&page=2#reviews')
console.log(url.href) // 'https://shop.example.com/catalog?category=phones&page=2#reviews'
console.log(url.origin) // 'https://shop.example.com'
console.log(url.protocol) // 'https:'
console.log(url.host) // 'shop.example.com'
console.log(url.hostname) // 'shop.example.com' (без порта)
console.log(url.port) // '' (80/443 не показываются)
console.log(url.pathname) // '/catalog'
console.log(url.search) // '?category=phones&page=2'
console.log(url.hash) // '#reviews'Все свойства URL доступны для записи:
const url = new URL('https://example.com/old-path')
url.pathname = '/new-path'
url.search = '?version=2'
url.hash = '#section-1'
console.log(url.href)
// 'https://example.com/new-path?version=2#section-1'const url = new URL('https://api.example.com/products?sort=price&order=asc')
const params = url.searchParams // объект URLSearchParams
// Чтение
console.log(params.get('sort')) // 'price'
console.log(params.get('order')) // 'asc'
console.log(params.get('missing')) // null
console.log(params.has('sort')) // true
// Изменение
params.set('sort', 'name') // заменить значение
params.append('tag', 'sale') // добавить параметр (может дублироваться)
params.delete('order') // удалить параметр
console.log(url.href)
// 'https://api.example.com/products?sort=name&tag=sale'
// Итерация по параметрам
for (const [key, value] of params) {
console.log(key, '=', value)
}
// Все значения для одного ключа (если append использовался несколько раз)
const tags = params.getAll('tag') // ['sale']URL умеет резолвить относительные пути:
const base = new URL('https://example.com/api/v1/')
const endpoint = new URL('./users', base)
console.log(endpoint.href) // 'https://example.com/api/v1/users'
const absolute = new URL('/admin', base)
console.log(absolute.href) // 'https://example.com/admin'// Создать ссылку для скачивания файла
function downloadFile(content, filename, type = 'text/plain') {
const blob = new Blob([content], { type })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
a.click()
// Обязательно освобождаем память после использования
URL.revokeObjectURL(url)
}
downloadFile('{ "data": 42 }', 'result.json', 'application/json')function buildProductsURL(baseURL, filters) {
const url = new URL(baseURL)
if (filters.category) url.searchParams.set('category', filters.category)
if (filters.minPrice) url.searchParams.set('price_gte', filters.minPrice)
if (filters.maxPrice) url.searchParams.set('price_lte', filters.maxPrice)
if (filters.page) url.searchParams.set('page', filters.page)
if (filters.sort) url.searchParams.set('sort', filters.sort)
return url.href
}
const apiURL = buildProductsURL('https://api.example.com/products', {
category: 'smartphones',
minPrice: 10000,
maxPrice: 80000,
page: 2,
sort: 'price_asc',
})
console.log(apiURL)
// 'https://api.example.com/products?category=smartphones&price_gte=10000&price_lte=80000&page=2&sort=price_asc'1. Строить URL конкатенацией строк — спецсимволы ломают запрос
// ПЛОХО — пробелы и кириллица в параметрах ломают URL
const city = 'Нижний Новгород'
const url = '/api/weather?city=' + city // /api/weather?city=Нижний Новгород — сломано!
// ХОРОШО — URLSearchParams экранирует автоматически
const params = new URLSearchParams({ city })
const url2 = '/api/weather?' + params.toString()
// /api/weather?city=%D0%9D%D0%B8%D0%B6%D0%BD%D0%B8%D0%B9+%D0%9D%D0%BE%D0%B2%D0%B3%D0%BE%D1%80%D0%BE%D0%B42. Передавать относительный URL без базы в new URL()
// ПЛОХО — без базы относительный URL выбросит TypeError
const url = new URL('/api/users') // TypeError: Failed to construct 'URL': Invalid URL
// ХОРОШО — передай базу вторым аргументом
const url2 = new URL('/api/users', 'https://example.com')
console.log(url2.href) // 'https://example.com/api/users'
// Или используй window.location.origin в браузере
const url3 = new URL('/api/users', window.location.origin)3. Забыть revokeObjectURL — утечка памяти при работе с Blob
// ПЛОХО — URL объект в памяти до перезагрузки страницы
const blob = new Blob([data], { type: 'text/csv' })
const url = URL.createObjectURL(blob)
link.href = url
link.click()
// URL.revokeObjectURL(url) забыт — утечка памяти!
// ХОРОШО — освобождаем сразу после использования
link.click()
URL.revokeObjectURL(url) // освобождаем памятьnew URL() для парсинга текущего адреса и навигацииURL и URLSearchParams для безопасной сборки запросов с параметрамиURL.createObjectURL(blob) для генерации ссылки без серверного запросаURL API: парсинг URL, работа с URLSearchParams, построение API-запросов с фильтрами
// URL API доступен в современных sandbox-окружениях
// Используем реальный класс URL
// --- Демо 1: Разбор URL на части ---
console.log('=== Парсинг URL ===')
const shopUrl = new URL('https://shop.example.com:8080/catalog/phones?brand=apple&sort=price&page=3#specs')
console.log('href: ', shopUrl.href)
console.log('origin: ', shopUrl.origin) // 'https://shop.example.com:8080'
console.log('protocol: ', shopUrl.protocol) // 'https:'
console.log('host: ', shopUrl.host) // 'shop.example.com:8080'
console.log('hostname: ', shopUrl.hostname) // 'shop.example.com'
console.log('port: ', shopUrl.port) // '8080'
console.log('pathname: ', shopUrl.pathname) // '/catalog/phones'
console.log('search: ', shopUrl.search) // '?brand=apple&sort=price&page=3'
console.log('hash: ', shopUrl.hash) // '#specs'
// --- Демо 2: URLSearchParams ---
console.log('\n=== URLSearchParams ===')
const params = shopUrl.searchParams
console.log('brand:', params.get('brand')) // 'apple'
console.log('sort:', params.get('sort')) // 'price'
console.log('page:', params.get('page')) // '3'
console.log('has(brand):', params.has('brand')) // true
console.log('has(color):', params.has('color')) // false
console.log('get(missing):', params.get('missing')) // null
// Изменение параметров
params.set('page', '4')
params.append('tag', 'sale')
params.append('tag', 'new')
params.delete('sort')
console.log('\nПосле изменений:')
console.log('page:', params.get('page')) // '4'
console.log('tag (все):', params.getAll('tag')) // ['sale', 'new']
console.log('sort:', params.get('sort')) // null — удалён
console.log('URL после:', shopUrl.href)
// Итерация
console.log('\nВсе параметры:')
for (const [key, value] of shopUrl.searchParams) {
console.log(` ${key} = ${value}`)
}
// --- Демо 3: Построение API URL ---
console.log('\n=== Построение URL для API ===')
function buildApiURL(base, filters) {
const url = new URL(base)
const validFilters = {
category: v => url.searchParams.set('category', v),
minPrice: v => url.searchParams.set('price_gte', v),
maxPrice: v => url.searchParams.set('price_lte', v),
inStock: v => url.searchParams.set('in_stock', v ? '1' : '0'),
page: v => url.searchParams.set('page', v),
limit: v => url.searchParams.set('limit', v),
sort: v => url.searchParams.set('sort', v),
tags: v => v.forEach(t => url.searchParams.append('tags', t)),
}
for (const [key, value] of Object.entries(filters)) {
if (value !== undefined && value !== null && validFilters[key]) {
validFilters[key](value)
}
}
return url
}
const apiUrl = buildApiURL('https://api.myshop.ru/v2/products', {
category: 'laptops',
minPrice: 50000,
maxPrice: 150000,
inStock: true,
page: 1,
limit: 20,
sort: 'price_asc',
tags: ['gaming', 'sale'],
})
console.log('API URL:')
console.log(apiUrl.href)
console.log('\nПараметры по отдельности:')
for (const [k, v] of apiUrl.searchParams) {
console.log(` ${k}: ${v}`)
}
// --- Демо 4: Относительные URL ---
console.log('\n=== Относительные URL ===')
const base = new URL('https://api.example.com/v2/users/123/')
const relative1 = new URL('./profile', base)
const relative2 = new URL('../posts', base)
const relative3 = new URL('/admin/settings', base)
console.log('base: ', base.href)
console.log('./profile: ', relative1.href) // ...users/123/profile
console.log('../posts: ', relative2.href) // ...v2/users/posts
console.log('/admin: ', relative3.href) // ...example.com/admin/settingsНапиши функцию parseQueryString(urlString) которая принимает URL-строку и возвращает объект с параметрами запроса (ключ: значение). Если у ключа несколько значений (через append) — значение должно быть массивом. Напиши функцию buildURL(base, params) которая добавляет параметры из объекта к URL и возвращает строку.
parseQueryString: итерируй url.searchParams циклом for...of. buildURL: для массивов используй append, для одиночных значений — set.