惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
D
DataBreaches.Net
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog RSS Feed
Recent Announcements
Recent Announcements
The Register - Security
The Register - Security
S
Secure Thoughts
Y
Y Combinator Blog
The Last Watchdog
The Last Watchdog
L
LINUX DO - 最新话题
V2EX - 技术
V2EX - 技术
腾讯CDC
GbyAI
GbyAI
G
Google Developers Blog
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
Schneier on Security
Schneier on Security
Microsoft Security Blog
Microsoft Security Blog
Jina AI
Jina AI
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
MyScale Blog
MyScale Blog
Help Net Security
Help Net Security
K
Kaspersky official blog
P
Privacy & Cybersecurity Law Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
AI
AI
MongoDB | Blog
MongoDB | Blog
Scott Helme
Scott Helme
J
Java Code Geeks
Engineering at Meta
Engineering at Meta
H
Heimdal Security Blog
H
Help Net Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
云风的 BLOG
云风的 BLOG
Microsoft Azure Blog
Microsoft Azure Blog
S
Security Affairs
TaoSecurity Blog
TaoSecurity Blog
The GitHub Blog
The GitHub Blog
Hacker News: Ask HN
Hacker News: Ask HN
Martin Fowler
Martin Fowler
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Project Zero
Project Zero
T
The Blog of Author Tim Ferriss
Last Week in AI
Last Week in AI

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Затенение в JavaScript
JastaFly · 2026-04-28 · via Все публикации подряд на Хабре

В статье о глобальной области видимости в JavaScript, мы коротко коснулись темы затенений (бурж. variable shadowing), в данной статье мы рассмотрим это явление подробнее.

В одной области видимости, не может быть переменных или аргументов с одинаковыми именами. Нарушение данного правила ведёт к ошибке:

function greetingUser(userName) {
    let userName = 'Васятка' // Uncaught SyntaxError: Identifier 'userName' has already been declared
  
    console.log(`Привет ${userName}`)
}

greetingUser('Пётр')

Оно и понятно, если есть две одинаковых переменных, как интерпретатор поймёт с какой из них ему работать дальше? Поэтому он сразу пресекает это дело, выдавая ошибку. Но если переменные были созданы в разных областях видимости, то ошибке не произойдёт, просто использоваться будет ближайшая к вызову переменная:

function greetingUser(userName, isForeign = false) {
    let greeting = 'Привет'

    if (isForeign) {
        let greeting = 'Hola' // Затеняем greeting переменную из верхней области видимости

        return (`${greeting} ${userName}!`)
    }

    return (`${greeting} ${userName}!`)
}

console.log(greetingUser('Pedro', true))

В примере выше, переменная greeting, внутри условия, затеняет свою тёску из функциональной области видимости. После этого обратиться к функциональной переменной greeting, внутри данного блока, становится невозможно.

Затенение глобальных переменных

Затенению подвержены и переменные объявленные в глобальной области видимости:

var userName = 'Пётр'

function greetingUser() {
    let userName = 'Васятка' // Затеняем глобальную переменную

    console.log(`Привет ${userName}!`) // Привет Васятка!
}

greetingUser()

Но если к простым смертным переменным, обратиться после затенения невозможно, то к глобальным можно получить доступ через свойство объекта window:

var userName = 'Пётр'

function greetingUser() {
  let userName = 'Васятка'
  let globalUser = window.userName
  
  console.log(`Привет ${userName}! И тебе тоже привет ${globalUser}!`) // Привет Васятка! И тебе тоже привет Пётр!
}

greetingUser()

В примере выше window.student, является синонимом глобальной переменной student, а не её копией. Изменения в сделанные при помощи одной формы обращений будут отображаться и в другой:

var userName = 'Пётр'

function greetingUser() {
  window.userName = 'Ивасик' // Изменяем глобальную переменную

  let userName = 'Васятка'
  let globalUser = window.userName

  console.log(`Привет ${userName}! И тебе тоже привет ${globalUser}!`) // Привет Васятка! И тебе тоже привет Ивасик!

}

greetingUser()

Но обращение к глобальной переменной, как к свойству объекта window, сработает только если она была объявлена через var. А вот если использовать для объявления let или const, то ничего не выйдет:

let userName = 'Пётр'

function greetingUser() {
  let userName = 'Васятка'
  let globalUser = window.userName

  console.log(`Привет ${userName}! И тебе тоже привет ${globalUser}!`) // Привет Васятка! И тебе тоже привет undefined!
}

greetingUser()

При вызове window.userName, ошибки не возникнет, просто значение свойства будет равно undefined. Таким образом если для объявления, использовалось ключевое слово let или const, затенение обойти не получится.

Именованные функции объявленные при помощи function, тоже станут свойствами глобального объекта window и через него к ним можно будет обращаться, обходя затенение. Подробно нюансы работы глобальной области видимости в JavaScript, мы рассматривали в одной из статей, поэтому тут не будем заострять, слишком много внимания на данной теме.

let, var и затенение

Затенение по разному работает, в зависимости от через какое ключевое слово была объявлена переменная. Переменная объявленная при помощи let, спокойно затеняет переменную объявленную через var:

function greetingUser(userName, isForeign = false) {
    var greeting = 'Привет'

    if (isForeign) {
        let greeting = 'Hola' 

        return (`${greeting} ${userName}!`)
    }

    return (`${greeting} ${userName}!`)
}

console.log(greetingUser('Pedro', true)) // Hola Pedro! - let затенило var

А вот var не может затенить let. Попытка сделать это, вызовет ошибку:

function greetingUser(userName, isForeign = false) {
    let greeting = 'Привет'

    if (isForeign) {
        var greeting = 'Hola' // Uncaught SyntaxError: Identifier 'greeting' has already been declared

        return (`${greeting} ${userName}!`)
    }

    return (`${greeting} ${userName}!`)
}

console.log(greetingUser('Pedro', true))

Так происходит потому что, var имеет функциональную область видимости и после объявления она "всплывает" на вверх из своего блок в начало функции. А там уже объявлена переменная с точно таким-же именем, что и приводит к ошибке. Подробно механику всплытия мы рассматривали в одной из предыдущих статей.

При этом этом var может затенить let, не вызывая ошибок, если они будут находится в разных функциях. Такое например возможно при использовании замыканий:

function greetingUser(userName, isForeign = false) {
    let greeting = 'Привет'

    if (isForeign) {
        return () => {
            var greeting = 'Hola' // Ошибки как в прошлый раз не будет!

            return (`${greeting} ${userName}!`)
        }
    }
  
    return () => {
        return (`${greeting} ${userName}!`)
    }
}

const greeting = greetingUser('Pedro', true)

console.log(greeting())

О замыканиях у меня кстати тоже есть подробная статья.

Плюсы и минусы использования затенения в коде

Затенение двояко влияет на код. С одной стороны она позволяет инкапсулировать переменные, а с другой может привести к путанице. Рассмотрим плюсы и минусы замыканий подробнее.

Плюсы затенения

Локализация и изоляция

Когда в начала блока или внутренней функции локальная переменная, затеняет функциональную, это сразу говорит о том что тут инкапсулирована своя логика:

function greetingUser(userName, greeting) {
    return {
        say: () => {
            console.log(`${greeting} ${userName}!`)
        },
        sayFr: () => {
            let greeting = 'Bonjour'

            console.log(`${greeting} ${userName}!`)
        },
        saySp: () => {
            let greeting = 'Hola'

            console.log(`${greeting} ${userName}!`)
        },
        sayEn: () => {
            let greeting = 'Hola'

            console.log(`${greeting} ${userName}!`)
        }
    }
}

const greeting = greetingUser('Васятка', 'Привет')

greeting.say()
greeting.sayEn()
greeting.sayFr()
greeting.saySp()

Зашита от конфликтов имён

В больших легаси функциях, со множеством вложенных циклов, колбэков и замыканий, можно спокойно добавлять новые переменные, с простыми и лаконичными именами, не опасаясь синтаксических ошибок. Ведь максимум что они сделаю, это затенят аналогичную переменную, где-то на более высоком уровене. Хотя если встречается такая мега-функция, то разумнее будет её декомпозировать, но к сожалению на это не всегда есть время поэтому затенения тут спасают.

Упрощение рефакторинга и отладки

В блоках можно затенять переменные и быстро экспериментировать с логикой, не боясь ошибок и сайдэффектов во внешнем коде.

Минусы затенения

Путаница при беглом чтении кода

При быстром чтении большого объёма кода, наличии нескольких переменных с одинаковыми именами именами, может так не слабо подпалить седалище разработчика:

function greetingUser(userName, isForeign = false) {
    let greeting = 'Привет'
    // Много, много кода
    return () => {
        var greeting = 'Hello'
        // Много, много при много кода
        if (isForeign) {
            let greeting = 'Hola'

            return (`${greeting} ${userName}!`)
        }
        // Ещё много, много при много кода
        greeting = 'Hello' // В итоге какая из переменных меняется тут?

        return (`${greeting} ${userName}!`)
    }
}

const greeting = greetingUser('Pedro', true)

console.log(greeting())

Пример выше, это сильно упрощённый псевдокод, но даже в нём нужно остановиться и потратить немало мыслетоплива, на то чтобы банально разобраться, во всех приключениях одной переменной. В недрах реальных проектов, встречаются чудища, куда страшнее.

Ухудшение читаемости при глубокой вложенности

Чем больше уровней вложенности, тем сложнее понять какая именно из переменных используется на конкретной строке:

function greetingUsers(users) {
    let greeting = 'Hello'

    if (Array.isArray(users)) {
        for (let user of users) {
            let isForeign = user[1]
            let greeting = 'Привет'

            if (isForeign) {
                let greeting = 'Hola'

                console.log(`${greeting} ${user[0]}!`)
            }

            console.log(`${greeting} ${user[0]}!`) // Какая из переменных greeting используется тут?
        }
    } else {
        console.log(`${greeting} ${users}!`) // А тут?
    }
}

const users = [
    ['Васятка', false],
    ['Pedro', true],
    ['Пётр', false]
] 

greetingUsers(users)

Неочевидность поведения

Выше мы описывали различия в работе затенения, в зависимости от того какое ключевое слово использовалось для объявления: var или let. Соответственно если в коде, затенение дополняется комбинацией, из различных способов объявление переменных, то понимание происходящего становится задачей со звёздочкой:

function greetingUser(userName, isForeign = false) {
    let greeting = 'Привет'

    return () => {
        if (isForeign) {
            var greeting = 'Hola'

            return (`${greeting} ${userName}!`)
        }

        return (`${greeting} ${userName}!`)
    }
}

const greetingForeign = greetingUser('Pedro', true)
const greeting = greetingUser('Васятка')
  
console.log(greetingForeign()) // Hola Pedro!
console.log(greeting()) // undefined Васятка!

В примере выше если аргумент isForeign равен true, то всё работает штатно и первый вывод в консоль обходится без сюрпризов. Но если значение isForeign, меняется на false, то переменная greeting, объявленная через var, не куда не девается. Она повинуясь законам всемирного тяготения JavaScript, всплывает в самый вверх внутренней функции, затеняя собой переменную greeting, из внешней функции. В этот момент движок JS, присваивает ей значение: undefined, до того момента пока не отработает строка с присвоением. Но в данном случае, присвоение находится в условии, которое не сработает, поэтому переменная greeting, так и останется со значение undefined! Это мы и увидим во втором выводе. Опять-же все подробности, почему так происходит, есть в статье о всплытии.

Поиск проблемных затенений в коде

Как мы убедились выше, затенения могут стать причиной множества неочевидных багов, поэтому лучше избегать их использования в коде. Помочь в этом может линтер: ESLint, имеющий специально обученное правило: no-shadow, ищущие проблемные затенения. Чтобы воспользоваться данным инструментом, нужно установить его в свой проект, при помощи пакетного менеджера:

npm install eslint --save-dev

Далее нужно создать конфиг. Для этого в ESLint имеется специально обученный конфигуратор, запускаемый командой:

npx eslint --init

В нём нужно будет, ответить на вопросы, о стеке и среде проекта. В итоге конфигуратор соберёт файл: eslint.config.mjs и сохранит его в корне проекта. Останется только добавить в него правило no-shadow:

import js from "@eslint/js"
import globals from "globals"
import {defineConfig} from "eslint/config"

export default defineConfig([
  {
    files: ["**/*.{js,mjs,cjs}"],
    plugins: { js },
    extends: ["js/recommended"],
    languageOptions: {
      globals: globals.browser
    },
    rules: {
      "no-shadow": "error" // Запрещаем затенения
    }
  }
])

Теперь прогоним через ESLint файл с последним примером, где как раз из-за затенения возникала ошибка:

npx eslint js/script.js

После некоторых раздумий, ESLint выдаст в консоли сообщения о найденных им проблемах:

  2:6  error  'greeting' is assigned a value but never used                         no-unused-vars
  6:8  error  'greeting' is already declared in the upper scope on line 2 column 6  no-shadow

✖ 2 problems (2 errors, 0 warnings)

В данном случае он видит 2 проблемы:

  1. Переменная let greeting, на второй строке, объявлена, но не используется из-за затенения на 6-й строке;

  2. Переменная var greeting, на 6-й строке, создаёт затенение, нарушая правило no-shadow.

Современные IDE умеют считывать правила из конфига eslint.config.mjs, используя их в своём статическом анализаторе. В VSCode для потребуется установить расширение ESLint, которое в совокупности с правилом no-shadow, будет подсвечивать проблемные затенения прямо в коде:

Работа ESLint в VSCode

Работа ESLint в VSCode

Вывод

Затенение может быть полезно в отдельных ситуациях. Например оно позволяет затенять глобальные переменные, избежать конфликтов имён и изолировать часть имплементации. Но в целом его использование в коде, нужно сводить к минимуму и быть с ним крайне аккуратным. Не что так не сбивает с толку, при чтении кода, как куча одинаковых переменных, разбросанных по разным уровням вложенности. Затенение это полезный инструмент, но небрежное обращение с ним превращает код в головоломку.