Ты вызываешь [1, 2, 3].map() — но откуда у массива этот метод? В массиве его нет. JavaScript ищет map в Array.prototype — специальном объекте, который является прототипом для всех массивов. Именно там живут map, filter, reduce и ещё 30+ методов.
prototype связаныКогда создаётся массив, его [[Prototype]] указывает на Array.prototype — объект со всеми методами массивов:
const nums = [1, 2, 3]
// Где живёт метод map?
console.log(nums.hasOwnProperty('map')) // false — не в самом массиве
console.log(Array.prototype.hasOwnProperty('map')) // true — в прототипе!
// Цепочка: nums → Array.prototype → Object.prototype → null
console.log(Object.getPrototypeOf(nums) === Array.prototype) // true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype) // true// Массив
// [] → Array.prototype → Object.prototype → null
// Строка
// '' → String.prototype → Object.prototype → null
// Функция
// function() {} → Function.prototype → Object.prototype → null
// Object.prototype — вершина всех цепочек
console.log(Object.prototype.__proto__) // nullhasOwnProperty проверяет только собственные свойства объекта, оператор in — весь прототип:
const order = { id: 42, total: 1500 }
console.log('id' in order) // true
console.log('toString' in order) // true — из Object.prototype
console.log(order.hasOwnProperty('id')) // true
console.log(order.hasOwnProperty('toString')) // false — не собственное!
// Современный способ (безопаснее — не сломается если hasOwnProperty переопределён):
console.log(Object.hasOwn(order, 'id')) // true
console.log(Object.hasOwn(order, 'toString')) // falsetypeof не всегда помогает (typeof null === 'object'). Метод Object.prototype.toString возвращает точный тип:
function getType(value) {
return Object.prototype.toString.call(value)
}
console.log(getType([])) // '[object Array]'
console.log(getType({})) // '[object Object]'
console.log(getType(null)) // '[object Null]'
console.log(getType(undefined)) // '[object Undefined]'
console.log(getType(42)) // '[object Number]'
console.log(getType('строка')) // '[object String]'
console.log(getType(/\d+/)) // '[object RegExp]'
console.log(getType(new Date())) // '[object Date]'Полифил — реализация стандартного метода для старых сред:
// Полифил для Array.prototype.flat
if (!Array.prototype.flat) {
Array.prototype.flat = function(depth = 1) {
if (depth <= 0) return this.slice()
return this.reduce(function(acc, item) {
if (Array.isArray(item)) {
acc.push(...item.flat(depth - 1))
} else {
acc.push(item)
}
return acc
}, [])
}
}
const nested = [1, [2, [3, [4]]]]
console.log(nested.flat()) // [1, 2, [3, [4]]]
console.log(nested.flat(2)) // [1, 2, 3, [4]]Изменение встроенных прототипов — опасная практика:
// Плохо — изменяем глобальный прототип:
Array.prototype.last = function() { return this[this.length - 1] }
// Хорошо — используем обычную функцию:
function last(arr) { return arr[arr.length - 1] }
// Или только для полифилов со строгой проверкой:
if (!Array.prototype.at) {
Array.prototype.at = function(index) {
if (index < 0) return this[this.length + index]
return this[index]
}
}1. Потеря hasOwnProperty — объект создан через Object.create(null):
// Словарь без прототипа — нет метода hasOwnProperty!
const dict = Object.create(null)
dict.name = 'Иван'
dict.hasOwnProperty('name') // TypeError: dict.hasOwnProperty is not a function
// Правильно: использовать Object.hasOwn или вызывать из прототипа:
Object.hasOwn(dict, 'name') // true — безопасно
Object.prototype.hasOwnProperty.call(dict, 'name') // true — старый способ2. for...in итерирует унаследованные свойства при изменении прототипа:
Array.prototype.myMethod = function() {}
const arr = [1, 2, 3]
for (const key in arr) {
console.log(key) // '0', '1', '2', 'myMethod' — лишнее попало!
}
// Правильно: всегда фильтруй через hasOwn:
for (const key in arr) {
if (Object.hasOwn(arr, key)) console.log(key) // только '0', '1', '2'
}3. Полифил без проверки на существование — перезапишет нативный метод:
// Плохо: перезаписывает нативный Array.prototype.flat если он есть
Array.prototype.flat = function() { /* своя реализация */ }
// Хорошо: добавляем только если метода нет
if (!Array.prototype.flat) {
Array.prototype.flat = function() { /* полифил */ }
}Object.prototype.toString.call(value) для точного определения типаЦепочка прототипов массива, hasOwnProperty vs in, Object.prototype.toString для определения типа
// 1. Исследуем цепочку прототипов массива
const cart = ['молоко', 'хлеб', 'яйца']
console.log('--- Цепочка прототипов ---')
console.log(Object.getPrototypeOf(cart) === Array.prototype) // true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype)) // null — конец цепочки
// 2. hasOwnProperty vs in
console.log('\n--- Собственные свойства vs прототип ---')
const product = { name: 'Чай', price: 120 }
console.log(product.hasOwnProperty('name')) // true
console.log('name' in product) // true
console.log(product.hasOwnProperty('toString')) // false — не собственное
console.log('toString' in product) // true — есть в цепочке!
console.log('map' in product) // false — map только у Array
// Безопасная итерация только по своим ключам:
for (const key in product) {
if (Object.hasOwn(product, key)) {
console.log(`${key}: ${product[key]}`)
}
}
// name: Чай
// price: 120
// 3. Object.prototype.toString — точное определение типа
console.log('\n--- Определение типа ---')
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1) // 'Array', 'Date', etc.
}
const items = [
['Алиса', 'String'],
[42, 'Number'],
[[], 'Array'],
[{}, 'Object'],
[null, 'Null'],
[undefined, 'Undefined'],
[new Date(), 'Date'],
[/\d+/, 'RegExp'],
]
items.forEach(([value, expected]) => {
const actual = getType(value)
const status = actual === expected ? 'OK' : 'ОШИБКА'
console.log(`getType(${JSON.stringify(value)}) = '${actual}' (${status})`)
})Ты вызываешь [1, 2, 3].map() — но откуда у массива этот метод? В массиве его нет. JavaScript ищет map в Array.prototype — специальном объекте, который является прототипом для всех массивов. Именно там живут map, filter, reduce и ещё 30+ методов.
prototype связаныКогда создаётся массив, его [[Prototype]] указывает на Array.prototype — объект со всеми методами массивов:
const nums = [1, 2, 3]
// Где живёт метод map?
console.log(nums.hasOwnProperty('map')) // false — не в самом массиве
console.log(Array.prototype.hasOwnProperty('map')) // true — в прототипе!
// Цепочка: nums → Array.prototype → Object.prototype → null
console.log(Object.getPrototypeOf(nums) === Array.prototype) // true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype) // true// Массив
// [] → Array.prototype → Object.prototype → null
// Строка
// '' → String.prototype → Object.prototype → null
// Функция
// function() {} → Function.prototype → Object.prototype → null
// Object.prototype — вершина всех цепочек
console.log(Object.prototype.__proto__) // nullhasOwnProperty проверяет только собственные свойства объекта, оператор in — весь прототип:
const order = { id: 42, total: 1500 }
console.log('id' in order) // true
console.log('toString' in order) // true — из Object.prototype
console.log(order.hasOwnProperty('id')) // true
console.log(order.hasOwnProperty('toString')) // false — не собственное!
// Современный способ (безопаснее — не сломается если hasOwnProperty переопределён):
console.log(Object.hasOwn(order, 'id')) // true
console.log(Object.hasOwn(order, 'toString')) // falsetypeof не всегда помогает (typeof null === 'object'). Метод Object.prototype.toString возвращает точный тип:
function getType(value) {
return Object.prototype.toString.call(value)
}
console.log(getType([])) // '[object Array]'
console.log(getType({})) // '[object Object]'
console.log(getType(null)) // '[object Null]'
console.log(getType(undefined)) // '[object Undefined]'
console.log(getType(42)) // '[object Number]'
console.log(getType('строка')) // '[object String]'
console.log(getType(/\d+/)) // '[object RegExp]'
console.log(getType(new Date())) // '[object Date]'Полифил — реализация стандартного метода для старых сред:
// Полифил для Array.prototype.flat
if (!Array.prototype.flat) {
Array.prototype.flat = function(depth = 1) {
if (depth <= 0) return this.slice()
return this.reduce(function(acc, item) {
if (Array.isArray(item)) {
acc.push(...item.flat(depth - 1))
} else {
acc.push(item)
}
return acc
}, [])
}
}
const nested = [1, [2, [3, [4]]]]
console.log(nested.flat()) // [1, 2, [3, [4]]]
console.log(nested.flat(2)) // [1, 2, 3, [4]]Изменение встроенных прототипов — опасная практика:
// Плохо — изменяем глобальный прототип:
Array.prototype.last = function() { return this[this.length - 1] }
// Хорошо — используем обычную функцию:
function last(arr) { return arr[arr.length - 1] }
// Или только для полифилов со строгой проверкой:
if (!Array.prototype.at) {
Array.prototype.at = function(index) {
if (index < 0) return this[this.length + index]
return this[index]
}
}1. Потеря hasOwnProperty — объект создан через Object.create(null):
// Словарь без прототипа — нет метода hasOwnProperty!
const dict = Object.create(null)
dict.name = 'Иван'
dict.hasOwnProperty('name') // TypeError: dict.hasOwnProperty is not a function
// Правильно: использовать Object.hasOwn или вызывать из прототипа:
Object.hasOwn(dict, 'name') // true — безопасно
Object.prototype.hasOwnProperty.call(dict, 'name') // true — старый способ2. for...in итерирует унаследованные свойства при изменении прототипа:
Array.prototype.myMethod = function() {}
const arr = [1, 2, 3]
for (const key in arr) {
console.log(key) // '0', '1', '2', 'myMethod' — лишнее попало!
}
// Правильно: всегда фильтруй через hasOwn:
for (const key in arr) {
if (Object.hasOwn(arr, key)) console.log(key) // только '0', '1', '2'
}3. Полифил без проверки на существование — перезапишет нативный метод:
// Плохо: перезаписывает нативный Array.prototype.flat если он есть
Array.prototype.flat = function() { /* своя реализация */ }
// Хорошо: добавляем только если метода нет
if (!Array.prototype.flat) {
Array.prototype.flat = function() { /* полифил */ }
}Object.prototype.toString.call(value) для точного определения типаЦепочка прототипов массива, hasOwnProperty vs in, Object.prototype.toString для определения типа
// 1. Исследуем цепочку прототипов массива
const cart = ['молоко', 'хлеб', 'яйца']
console.log('--- Цепочка прототипов ---')
console.log(Object.getPrototypeOf(cart) === Array.prototype) // true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype)) // null — конец цепочки
// 2. hasOwnProperty vs in
console.log('\n--- Собственные свойства vs прототип ---')
const product = { name: 'Чай', price: 120 }
console.log(product.hasOwnProperty('name')) // true
console.log('name' in product) // true
console.log(product.hasOwnProperty('toString')) // false — не собственное
console.log('toString' in product) // true — есть в цепочке!
console.log('map' in product) // false — map только у Array
// Безопасная итерация только по своим ключам:
for (const key in product) {
if (Object.hasOwn(product, key)) {
console.log(`${key}: ${product[key]}`)
}
}
// name: Чай
// price: 120
// 3. Object.prototype.toString — точное определение типа
console.log('\n--- Определение типа ---')
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1) // 'Array', 'Date', etc.
}
const items = [
['Алиса', 'String'],
[42, 'Number'],
[[], 'Array'],
[{}, 'Object'],
[null, 'Null'],
[undefined, 'Undefined'],
[new Date(), 'Date'],
[/\d+/, 'RegExp'],
]
items.forEach(([value, expected]) => {
const actual = getType(value)
const status = actual === expected ? 'OK' : 'ОШИБКА'
console.log(`getType(${JSON.stringify(value)}) = '${actual}' (${status})`)
})В аналитическом сервисе нужен полифил Array.prototype.groupBy(fn), который группирует элементы массива по результату функции fn. Метод должен возвращать объект, где ключи — результаты fn, а значения — массивы элементов. Перед определением обязательно проверь, не существует ли метод уже.
fn(item) даёт ключ группы. Если groups[key] ещё не создан — инициализируй его как []. Затем groups[key].push(item). return this.reduce(..., {}) — начальное значение аккумулятора — пустой объект {}.