← JavaScript/Побитовые операторы#147 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Побитовые операторы

Представь: ты проектируешь систему прав доступа для CMS. У пользователя может быть любая комбинация прав: читать, писать, удалять, публиковать, администрировать. Хранить каждое право отдельным boolean — 5 полей в базе данных. Или хранить всё в одном числе через битовые флаги. Именно так устроена система прав Unix, права файлов S3 и многие другие системы.

Что решает этот механизм

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

На основе предыдущих уроков

  • числа — числа в JavaScript внутри хранятся как 64-bit float, но для побитовых операций преобразуются в 32-bit int
  • операторы — побитовые операторы — это особая группа операторов со своим приоритетом
  • Как числа хранятся в памяти

    32-битное целое число хранится как последовательность нулей и единиц:

    // Число 5 в двоичном виде (32 бита):
    // 00000000 00000000 00000000 00000101
    //                                ^^^
    //                                5 = 4 + 1 = 2² + 2⁰
    
    console.log((5).toString(2))   // '101'
    console.log((13).toString(2))  // '1101'  (8+4+1)
    console.log((255).toString(2)) // '11111111'

    Операторы

    & (AND) — оба бита должны быть 1

    //   0101  (5)
    // & 0011  (3)
    // = 0001  (1)
    console.log(5 & 3)   // 1
    console.log(12 & 10) // 8  (1100 & 1010 = 1000)

    | (OR) — хотя бы один бит равен 1

    //   0101  (5)
    // | 0011  (3)
    // = 0111  (7)
    console.log(5 | 3)   // 7
    console.log(4 | 2)   // 6  (100 | 010 = 110)

    ^ (XOR) — биты различаются

    //   0101  (5)
    // ^ 0011  (3)
    // = 0110  (6)
    console.log(5 ^ 3)  // 6
    console.log(5 ^ 5)  // 0 — XOR с самим собой всегда 0

    ~ (NOT) — инвертирует все биты

    // ~n = -(n + 1) для 32-битных целых
    console.log(~5)   // -6
    console.log(~0)   // -1
    console.log(~-1)  // 0

    << (left shift) — сдвиг влево

    // Сдвиг на N позиций = умножение на 2^N
    console.log(1 << 0)   // 1   (00001)
    console.log(1 << 1)   // 2   (00010)
    console.log(1 << 2)   // 4   (00100)
    console.log(1 << 3)   // 8   (01000)
    console.log(5 << 1)   // 10  (0101 → 1010)

    >> (right shift) — сдвиг вправо (с сохранением знака)

    // Сдвиг на N позиций = деление на 2^N (целочисленное)
    console.log(8 >> 1)   // 4
    console.log(8 >> 2)   // 2
    console.log(-8 >> 1)  // -4 (знак сохраняется)

    >>> (unsigned right shift) — беззнаковый сдвиг вправо

    console.log(8 >>> 1)    // 4
    console.log(-8 >>> 1)   // 2147483644 (знаковый бит становится обычным битом)

    Практика: флаги и права доступа

    Битовые маски — эффективный способ хранить набор булевых флагов в одном числе:

    // Определяем флаги как степени двойки (каждый занимает свой бит)
    const READ  = 1   // 001
    const WRITE = 2   // 010
    const EXEC  = 4   // 100
    const ADMIN = 8   // 1000
    
    // Создаём маску прав (комбинируем через |)
    let permissions = READ | WRITE   // 011 = 3 (чтение и запись)
    
    // Проверка флага (& с флагом → ненулевое значение если флаг установлен)
    console.log((permissions & READ)  !== 0)  // true  — есть право на чтение
    console.log((permissions & EXEC)  !== 0)  // false — нет права на исполнение
    
    // Установка флага (|)
    permissions = permissions | EXEC  // добавить EXEC → 111 = 7
    
    // Снятие флага (& ~flag)
    permissions = permissions & ~WRITE  // убрать WRITE → 101 = 5
    
    // Переключение флага (^)
    permissions = permissions ^ ADMIN   // добавить ADMIN → 1101 = 13
    permissions = permissions ^ ADMIN   // убрать ADMIN  → 0101 = 5

    Полезные трюки

    // Math.floor через | 0 (только для чисел в диапазоне 32-bit int)
    console.log(3.7 | 0)     // 3
    console.log(-3.7 | 0)    // -3 (усечение, не floor!)
    console.log(3.7 | 0)     // 3
    // Аналог: Math.trunc(3.7)
    
    // Проверка чётности (последний бит: 0 = чётное, 1 = нечётное)
    const isEven = n => (n & 1) === 0
    const isOdd  = n => (n & 1) !== 0
    console.log(isEven(42))  // true
    console.log(isOdd(7))    // true
    
    // Быстрое умножение/деление на степень двойки
    console.log(3 << 3)  // 24 = 3 * 8 = 3 * 2³
    console.log(64 >> 2) // 16 = 64 / 4 = 64 / 2²
    
    // Проверка степени двойки
    const isPowerOf2 = n => n > 0 && (n & (n - 1)) === 0
    console.log(isPowerOf2(16))  // true
    console.log(isPowerOf2(12))  // false

    Реальный пример: chmod-подобная система прав

    В Unix каждый файл имеет три группы прав (owner, group, other) по три бита (rwx):

    // chmod 755 → owner: rwx (7), group: r-x (5), other: r-x (5)
    //            7 = 111₂, 5 = 101₂
    
    const OWNER_R = 0o400  // 100 000 000
    const OWNER_W = 0o200  // 010 000 000
    const OWNER_X = 0o100  // 001 000 000
    const GROUP_R = 0o040  // 000 100 000
    const GROUP_X = 0o010  // 000 001 000
    const OTHER_R = 0o004  // 000 000 100
    const OTHER_X = 0o001  // 000 000 001
    
    const mode755 = OWNER_R | OWNER_W | OWNER_X | GROUP_R | GROUP_X | OTHER_R | OTHER_X
    console.log(mode755.toString(8))  // 755
    
    // Проверяем права
    console.log((mode755 & OWNER_W) !== 0)  // true — владелец может писать
    console.log((mode755 & GROUP_R) !== 0)  // true — группа может читать

    Типичные ошибки

    1. Использовать | 0 для floor отрицательных чисел

    // ПЛОХО — | 0 это Math.trunc, а не Math.floor!
    console.log(-3.7 | 0)    // -3, а не -4!
    console.log(Math.floor(-3.7))  // -4 (правильно)
    
    // ХОРОШО — используй Math.floor или Math.trunc явно
    const truncated = Math.trunc(3.7)   // 3
    const floored   = Math.floor(3.7)   // 3
    // Для отрицательных разница критична

    2. Применять побитовые операции к числам больше 32-bit

    // ПЛОХО — число обрезается до 32 бит!
    const bigNum = 2 ** 33  // 8589934592 — больше 32 бит
    console.log(bigNum | 0)  // 0 — потеряли всё!
    
    // ХОРОШО — проверяй диапазон, используй только для маленьких целых
    // Безопасный диапазон: -2147483648 до 2147483647 (2^31 - 1)
    const safeMax = 2 ** 31 - 1   // 2147483647
    console.log(safeMax | 0)       // 2147483647 — безопасно

    3. Забыть добавить скобки — приоритет & ниже, чем ===

    // ПЛОХО — & имеет низкий приоритет, сначала выполнится сравнение
    if (permissions & READ === 0) { ... }   // читается как: permissions & (READ === 0) → неправильно!
    
    // ХОРОШО — скобки обязательны
    if ((permissions & READ) !== 0) { ... }  // сначала &, потом сравнение

    В реальных проектах

  • Права Unix/Linux: chmod 755 — это битовая маска, rwxr-xr-x закодировано в 9 битах
  • AWS S3 permissions: ACL разрешения на объекты хранятся как битовые флаги
  • Игровые движки: состояние игры (прыгает / бежит / стреляет) — один int вместо нескольких boolean
  • CSS в JavaScript: React Native и некоторые CSS-in-JS библиотеки внутри используют битовые флаги для dirty-tracking стилей
  • Примеры

    Система прав доступа на битовых флагах: проверка, установка, снятие и переключение прав

    // Система прав доступа на битовых флагах
    // Каждое право — отдельный бит (степень двойки)
    
    const Permissions = {
      NONE:         0,     // 0000 0000
      READ:         1,     // 0000 0001
      WRITE:        2,     // 0000 0010
      DELETE:       4,     // 0000 0100
      SHARE:        8,     // 0000 1000
      ADMIN:        16,    // 0001 0000
    }
    
    // Вспомогательные функции
    function hasPermission(mask, flag) {
      return (mask & flag) !== 0
    }
    
    function addPermission(mask, flag) {
      return mask | flag
    }
    
    function removePermission(mask, flag) {
      return mask & ~flag
    }
    
    function togglePermission(mask, flag) {
      return mask ^ flag
    }
    
    // Читаемое представление маски прав
    function describePermissions(mask) {
      const names = Object.entries(Permissions)
        .filter(([name, flag]) => flag !== 0 && hasPermission(mask, flag))
        .map(([name]) => name)
      return names.length > 0 ? names.join(' | ') : 'NONE'
    }
    
    // ===== Демонстрация =====
    console.log('=== Система прав доступа ===')
    
    // Пользователь с базовыми правами
    let userPerms = Permissions.READ | Permissions.WRITE
    console.log('Изначальные права:', describePermissions(userPerms))
    // READ | WRITE
    
    console.log('Может читать?', hasPermission(userPerms, Permissions.READ))    // true
    console.log('Может удалять?', hasPermission(userPerms, Permissions.DELETE)) // false
    
    // Добавить право на удаление
    userPerms = addPermission(userPerms, Permissions.DELETE)
    console.log('\nПосле добавления DELETE:', describePermissions(userPerms))
    // READ | WRITE | DELETE
    
    // Снять право на запись
    userPerms = removePermission(userPerms, Permissions.WRITE)
    console.log('После удаления WRITE:', describePermissions(userPerms))
    // READ | DELETE
    
    // Переключить SHARE (вкл/выкл)
    userPerms = togglePermission(userPerms, Permissions.SHARE)
    console.log('После включения SHARE:', describePermissions(userPerms))
    // READ | DELETE | SHARE
    
    userPerms = togglePermission(userPerms, Permissions.SHARE)
    console.log('После выключения SHARE:', describePermissions(userPerms))
    // READ | DELETE
    
    // ===== Роли пользователей =====
    console.log('\n=== Роли с предустановленными правами ===')
    
    const Roles = {
      GUEST:  Permissions.READ,
      USER:   Permissions.READ | Permissions.WRITE,
      EDITOR: Permissions.READ | Permissions.WRITE | Permissions.DELETE | Permissions.SHARE,
      ADMIN:  Permissions.READ | Permissions.WRITE | Permissions.DELETE | Permissions.SHARE | Permissions.ADMIN,
    }
    
    const users = [
      { name: 'Гость',      role: Roles.GUEST },
      { name: 'Иван',       role: Roles.USER },
      { name: 'Редактор',   role: Roles.EDITOR },
      { name: 'Дмитрий',    role: Roles.ADMIN },
    ]
    
    const actions = [
      { name: 'читать',    flag: Permissions.READ },
      { name: 'писать',    flag: Permissions.WRITE },
      { name: 'удалять',   flag: Permissions.DELETE },
      { name: 'делиться',  flag: Permissions.SHARE },
      { name: 'управлять', flag: Permissions.ADMIN },
    ]
    
    for (const user of users) {
      const allowed = actions
        .filter(a => hasPermission(user.role, a.flag))
        .map(a => a.name)
      console.log(`${user.name.padEnd(12)}: ${allowed.join(', ')}`)
    }
    
    // ===== Трюки =====
    console.log('\n=== Побитовые трюки ===')
    
    // Проверка чётности
    const numbers = [0, 1, 2, 7, 12, 15, 100, 101]
    for (const n of numbers) {
      console.log(`${n}: ${(n & 1) === 0 ? 'чётное' : 'нечётное'}`)
    }
    
    // Быстрое Math.trunc через | 0
    console.log('\n3.9 | 0 =', 3.9 | 0)     // 3
    console.log('-3.9 | 0 =', -3.9 | 0)   // -3
    
    // Проверка степени двойки
    const checkPow2 = n => n > 0 && (n & (n - 1)) === 0
    const testNums = [1, 2, 3, 4, 8, 12, 16, 31, 32, 64]
    console.log('\nСтепени двойки:')
    for (const n of testNums) {
      if (checkPow2(n)) console.log(` ${n} = 2^${Math.log2(n)}`)
    }

    Побитовые операторы

    Представь: ты проектируешь систему прав доступа для CMS. У пользователя может быть любая комбинация прав: читать, писать, удалять, публиковать, администрировать. Хранить каждое право отдельным boolean — 5 полей в базе данных. Или хранить всё в одном числе через битовые флаги. Именно так устроена система прав Unix, права файлов S3 и многие другие системы.

    Что решает этот механизм

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

    На основе предыдущих уроков

  • числа — числа в JavaScript внутри хранятся как 64-bit float, но для побитовых операций преобразуются в 32-bit int
  • операторы — побитовые операторы — это особая группа операторов со своим приоритетом
  • Как числа хранятся в памяти

    32-битное целое число хранится как последовательность нулей и единиц:

    // Число 5 в двоичном виде (32 бита):
    // 00000000 00000000 00000000 00000101
    //                                ^^^
    //                                5 = 4 + 1 = 2² + 2⁰
    
    console.log((5).toString(2))   // '101'
    console.log((13).toString(2))  // '1101'  (8+4+1)
    console.log((255).toString(2)) // '11111111'

    Операторы

    & (AND) — оба бита должны быть 1

    //   0101  (5)
    // & 0011  (3)
    // = 0001  (1)
    console.log(5 & 3)   // 1
    console.log(12 & 10) // 8  (1100 & 1010 = 1000)

    | (OR) — хотя бы один бит равен 1

    //   0101  (5)
    // | 0011  (3)
    // = 0111  (7)
    console.log(5 | 3)   // 7
    console.log(4 | 2)   // 6  (100 | 010 = 110)

    ^ (XOR) — биты различаются

    //   0101  (5)
    // ^ 0011  (3)
    // = 0110  (6)
    console.log(5 ^ 3)  // 6
    console.log(5 ^ 5)  // 0 — XOR с самим собой всегда 0

    ~ (NOT) — инвертирует все биты

    // ~n = -(n + 1) для 32-битных целых
    console.log(~5)   // -6
    console.log(~0)   // -1
    console.log(~-1)  // 0

    << (left shift) — сдвиг влево

    // Сдвиг на N позиций = умножение на 2^N
    console.log(1 << 0)   // 1   (00001)
    console.log(1 << 1)   // 2   (00010)
    console.log(1 << 2)   // 4   (00100)
    console.log(1 << 3)   // 8   (01000)
    console.log(5 << 1)   // 10  (0101 → 1010)

    >> (right shift) — сдвиг вправо (с сохранением знака)

    // Сдвиг на N позиций = деление на 2^N (целочисленное)
    console.log(8 >> 1)   // 4
    console.log(8 >> 2)   // 2
    console.log(-8 >> 1)  // -4 (знак сохраняется)

    >>> (unsigned right shift) — беззнаковый сдвиг вправо

    console.log(8 >>> 1)    // 4
    console.log(-8 >>> 1)   // 2147483644 (знаковый бит становится обычным битом)

    Практика: флаги и права доступа

    Битовые маски — эффективный способ хранить набор булевых флагов в одном числе:

    // Определяем флаги как степени двойки (каждый занимает свой бит)
    const READ  = 1   // 001
    const WRITE = 2   // 010
    const EXEC  = 4   // 100
    const ADMIN = 8   // 1000
    
    // Создаём маску прав (комбинируем через |)
    let permissions = READ | WRITE   // 011 = 3 (чтение и запись)
    
    // Проверка флага (& с флагом → ненулевое значение если флаг установлен)
    console.log((permissions & READ)  !== 0)  // true  — есть право на чтение
    console.log((permissions & EXEC)  !== 0)  // false — нет права на исполнение
    
    // Установка флага (|)
    permissions = permissions | EXEC  // добавить EXEC → 111 = 7
    
    // Снятие флага (& ~flag)
    permissions = permissions & ~WRITE  // убрать WRITE → 101 = 5
    
    // Переключение флага (^)
    permissions = permissions ^ ADMIN   // добавить ADMIN → 1101 = 13
    permissions = permissions ^ ADMIN   // убрать ADMIN  → 0101 = 5

    Полезные трюки

    // Math.floor через | 0 (только для чисел в диапазоне 32-bit int)
    console.log(3.7 | 0)     // 3
    console.log(-3.7 | 0)    // -3 (усечение, не floor!)
    console.log(3.7 | 0)     // 3
    // Аналог: Math.trunc(3.7)
    
    // Проверка чётности (последний бит: 0 = чётное, 1 = нечётное)
    const isEven = n => (n & 1) === 0
    const isOdd  = n => (n & 1) !== 0
    console.log(isEven(42))  // true
    console.log(isOdd(7))    // true
    
    // Быстрое умножение/деление на степень двойки
    console.log(3 << 3)  // 24 = 3 * 8 = 3 * 2³
    console.log(64 >> 2) // 16 = 64 / 4 = 64 / 2²
    
    // Проверка степени двойки
    const isPowerOf2 = n => n > 0 && (n & (n - 1)) === 0
    console.log(isPowerOf2(16))  // true
    console.log(isPowerOf2(12))  // false

    Реальный пример: chmod-подобная система прав

    В Unix каждый файл имеет три группы прав (owner, group, other) по три бита (rwx):

    // chmod 755 → owner: rwx (7), group: r-x (5), other: r-x (5)
    //            7 = 111₂, 5 = 101₂
    
    const OWNER_R = 0o400  // 100 000 000
    const OWNER_W = 0o200  // 010 000 000
    const OWNER_X = 0o100  // 001 000 000
    const GROUP_R = 0o040  // 000 100 000
    const GROUP_X = 0o010  // 000 001 000
    const OTHER_R = 0o004  // 000 000 100
    const OTHER_X = 0o001  // 000 000 001
    
    const mode755 = OWNER_R | OWNER_W | OWNER_X | GROUP_R | GROUP_X | OTHER_R | OTHER_X
    console.log(mode755.toString(8))  // 755
    
    // Проверяем права
    console.log((mode755 & OWNER_W) !== 0)  // true — владелец может писать
    console.log((mode755 & GROUP_R) !== 0)  // true — группа может читать

    Типичные ошибки

    1. Использовать | 0 для floor отрицательных чисел

    // ПЛОХО — | 0 это Math.trunc, а не Math.floor!
    console.log(-3.7 | 0)    // -3, а не -4!
    console.log(Math.floor(-3.7))  // -4 (правильно)
    
    // ХОРОШО — используй Math.floor или Math.trunc явно
    const truncated = Math.trunc(3.7)   // 3
    const floored   = Math.floor(3.7)   // 3
    // Для отрицательных разница критична

    2. Применять побитовые операции к числам больше 32-bit

    // ПЛОХО — число обрезается до 32 бит!
    const bigNum = 2 ** 33  // 8589934592 — больше 32 бит
    console.log(bigNum | 0)  // 0 — потеряли всё!
    
    // ХОРОШО — проверяй диапазон, используй только для маленьких целых
    // Безопасный диапазон: -2147483648 до 2147483647 (2^31 - 1)
    const safeMax = 2 ** 31 - 1   // 2147483647
    console.log(safeMax | 0)       // 2147483647 — безопасно

    3. Забыть добавить скобки — приоритет & ниже, чем ===

    // ПЛОХО — & имеет низкий приоритет, сначала выполнится сравнение
    if (permissions & READ === 0) { ... }   // читается как: permissions & (READ === 0) → неправильно!
    
    // ХОРОШО — скобки обязательны
    if ((permissions & READ) !== 0) { ... }  // сначала &, потом сравнение

    В реальных проектах

  • Права Unix/Linux: chmod 755 — это битовая маска, rwxr-xr-x закодировано в 9 битах
  • AWS S3 permissions: ACL разрешения на объекты хранятся как битовые флаги
  • Игровые движки: состояние игры (прыгает / бежит / стреляет) — один int вместо нескольких boolean
  • CSS в JavaScript: React Native и некоторые CSS-in-JS библиотеки внутри используют битовые флаги для dirty-tracking стилей
  • Примеры

    Система прав доступа на битовых флагах: проверка, установка, снятие и переключение прав

    // Система прав доступа на битовых флагах
    // Каждое право — отдельный бит (степень двойки)
    
    const Permissions = {
      NONE:         0,     // 0000 0000
      READ:         1,     // 0000 0001
      WRITE:        2,     // 0000 0010
      DELETE:       4,     // 0000 0100
      SHARE:        8,     // 0000 1000
      ADMIN:        16,    // 0001 0000
    }
    
    // Вспомогательные функции
    function hasPermission(mask, flag) {
      return (mask & flag) !== 0
    }
    
    function addPermission(mask, flag) {
      return mask | flag
    }
    
    function removePermission(mask, flag) {
      return mask & ~flag
    }
    
    function togglePermission(mask, flag) {
      return mask ^ flag
    }
    
    // Читаемое представление маски прав
    function describePermissions(mask) {
      const names = Object.entries(Permissions)
        .filter(([name, flag]) => flag !== 0 && hasPermission(mask, flag))
        .map(([name]) => name)
      return names.length > 0 ? names.join(' | ') : 'NONE'
    }
    
    // ===== Демонстрация =====
    console.log('=== Система прав доступа ===')
    
    // Пользователь с базовыми правами
    let userPerms = Permissions.READ | Permissions.WRITE
    console.log('Изначальные права:', describePermissions(userPerms))
    // READ | WRITE
    
    console.log('Может читать?', hasPermission(userPerms, Permissions.READ))    // true
    console.log('Может удалять?', hasPermission(userPerms, Permissions.DELETE)) // false
    
    // Добавить право на удаление
    userPerms = addPermission(userPerms, Permissions.DELETE)
    console.log('\nПосле добавления DELETE:', describePermissions(userPerms))
    // READ | WRITE | DELETE
    
    // Снять право на запись
    userPerms = removePermission(userPerms, Permissions.WRITE)
    console.log('После удаления WRITE:', describePermissions(userPerms))
    // READ | DELETE
    
    // Переключить SHARE (вкл/выкл)
    userPerms = togglePermission(userPerms, Permissions.SHARE)
    console.log('После включения SHARE:', describePermissions(userPerms))
    // READ | DELETE | SHARE
    
    userPerms = togglePermission(userPerms, Permissions.SHARE)
    console.log('После выключения SHARE:', describePermissions(userPerms))
    // READ | DELETE
    
    // ===== Роли пользователей =====
    console.log('\n=== Роли с предустановленными правами ===')
    
    const Roles = {
      GUEST:  Permissions.READ,
      USER:   Permissions.READ | Permissions.WRITE,
      EDITOR: Permissions.READ | Permissions.WRITE | Permissions.DELETE | Permissions.SHARE,
      ADMIN:  Permissions.READ | Permissions.WRITE | Permissions.DELETE | Permissions.SHARE | Permissions.ADMIN,
    }
    
    const users = [
      { name: 'Гость',      role: Roles.GUEST },
      { name: 'Иван',       role: Roles.USER },
      { name: 'Редактор',   role: Roles.EDITOR },
      { name: 'Дмитрий',    role: Roles.ADMIN },
    ]
    
    const actions = [
      { name: 'читать',    flag: Permissions.READ },
      { name: 'писать',    flag: Permissions.WRITE },
      { name: 'удалять',   flag: Permissions.DELETE },
      { name: 'делиться',  flag: Permissions.SHARE },
      { name: 'управлять', flag: Permissions.ADMIN },
    ]
    
    for (const user of users) {
      const allowed = actions
        .filter(a => hasPermission(user.role, a.flag))
        .map(a => a.name)
      console.log(`${user.name.padEnd(12)}: ${allowed.join(', ')}`)
    }
    
    // ===== Трюки =====
    console.log('\n=== Побитовые трюки ===')
    
    // Проверка чётности
    const numbers = [0, 1, 2, 7, 12, 15, 100, 101]
    for (const n of numbers) {
      console.log(`${n}: ${(n & 1) === 0 ? 'чётное' : 'нечётное'}`)
    }
    
    // Быстрое Math.trunc через | 0
    console.log('\n3.9 | 0 =', 3.9 | 0)     // 3
    console.log('-3.9 | 0 =', -3.9 | 0)   // -3
    
    // Проверка степени двойки
    const checkPow2 = n => n > 0 && (n & (n - 1)) === 0
    const testNums = [1, 2, 3, 4, 8, 12, 16, 31, 32, 64]
    console.log('\nСтепени двойки:')
    for (const n of testNums) {
      if (checkPow2(n)) console.log(` ${n} = 2^${Math.log2(n)}`)
    }

    Задание

    Реализуй четыре функции для работы с битовыми флагами: hasPermission(mask, flag) — проверяет наличие флага, addPermission(mask, flag) — добавляет флаг, removePermission(mask, flag) — снимает флаг, togglePermission(mask, flag) — переключает флаг. Затем используй их для создания функции applyRoleDefaults(currentMask, roleMask) которая добавляет все права роли к текущей маске.

    Подсказка

    hasPermission: (mask & flag) !== 0. addPermission: mask | flag. removePermission: mask & ~flag. togglePermission: mask ^ flag. applyRoleDefaults: currentMask | roleMask

    Загружаем среду выполнения...
    Загружаем AI-помощника...