Skip to content

单字段表示多个属性

回答

shell
# 为什么要用单字段表示多属性?
减少数据传输 / 存储体积(如接口返回、本地存储);
简化状态管理(避免多字段冗余);
提升运算效率(位运算比多字段判断更快)。

# 常见实现方式?
1. 字符串编码/解码」:先编码把原始数据按照一定的规则分割成一个字符串整体,再解码使用的时候按照相同规则反解析字符串将其组合成对象
2. JSON 字符串:JSON.stringify、JSON.parse 转为字符再转对象
3. 位掩码+数学运算:利用位掩码和数学运算将多个属性(权限、状态、等级)编码到一个数字中,以及如何从该数字中解码还原出原始属性

# 最后
“如果是布尔型的组合属性(如权限、功能开关),优先用位运算,效率高且扩展方便;如果是包含字符串 / 数字的复杂属性(如筛选条件、用户配置),用字符串编码或 JSON 字符串,兼顾可读性和结构化。”

方案一:字符串编码(分隔符 / 固定格式)

js
// 1. 编码:单字符串表示“类型+范围+排序”三个属性(用|分隔)
const encodeFilter = (type, min, max, sort) => {
  return `${type}|${min}-${max}|${sort}`;
};
const filterStr = encodeFilter('product', 10, 100, 'price_desc'); // "product|10-100|price_desc"

// 2. 解码:解析单字符串为多属性
const decodeFilter = (str) => {
  const [type, range, sort] = str.split('|');
  const [min, max] = range.split('-').map(Number);
  return { type, min, max, sort };
};
const filterObj = decodeFilter(filterStr);
console.log(filterObj); // { type: 'product', min: 10, max: 100, sort: 'price_desc' }

方案二:JSON 字符串

js
// 编码:单字符串表示多属性
const userConfig = { theme: 'dark', fontSize: 16, autoSave: true };
const configStr = JSON.stringify(userConfig); // "{"theme":"dark","fontSize":16,"autoSave":true}"

// 解码:还原为多属性对象
const configObj = JSON.parse(configStr);
console.log(configObj.fontSize); // 16

方案三:位掩码+数学运算

&:按位与,操作的是位(整数),返回的是一个整数。

&&:逻辑与,操作的是布尔值(true/false),返回的是一个布尔值,并且具有短路特性(如果左边为假,右边不会执行)。

js
// 1. 定义常量
const Permission = { ADD: 1, DELETE: 2, EDIT: 4, READ: 8 };
const Status = { ENABLE: 16, DISABLE: 0 };

// 2. 编码:单数字表示多属性
const encodeUserAttr = (permissions, status, level) => {
  const permValue = Object.values(permissions).reduce((a, b) => a | b, 0);
  return permValue + status + level * 100;
};

// 3. 示例:将 增+查权限、启用状态、2级会员 属性编码得到最终编码结果为255
const userAttr = encodeUserAttr([Permission.ADD, Permission.READ], Status.ENABLE, 2);
console.log(userAttr); // 1+8+16 + 2*100 = 225(单字段)

// 4. 解码
const decodeUserAttr = (num) => {
  const level = Math.floor(num / 100); // 会员等级 2
  const rest = num % 100; // 权限+状态 25
  const status = rest & Status.ENABLE ? '启用' : '禁用';
  // & 是按位与运算符,常用于检查某个二进制位是否为 1。
  // Status.ENABLE 应该是一个位掩码(如 0b01 或 0b10),它只在其对应的状态位上为 1,其他位为 0。
  // rest & Status.ENABLE 的结果:
  // 如果 rest 中对应 Status.ENABLE 的位是 1,则结果非零(真),表示“启用”;
  // 如果该位是 0,则结果为零(假),表示“禁用”。
  const permissions = [];
  if (rest & Permission.ADD) permissions.push('');
  if (rest & Permission.DELETE) permissions.push('');
  if (rest & Permission.EDIT) permissions.push('');
  if (rest & Permission.READ) permissions.push('');
  return { level, status, permissions };
};

// 5. 示例:225 解码为  { level:2, status:'启用', permissions:['增','查'] }
console.log(decodeUserAttr(225));
// { level:2, status:'启用', permissions:['增','查'] }

什么是按位掩码与数学运算

shell
# 十进制转二进制
将十进制数不断除以 2,记录每次的余数,直到商为 0,然后将余数从下往上排列。

# 以 5 转二进制 101 为例
步骤 计算 余数
1    5 ÷ 2 2   1
2    2 ÷ 2 1   0
3    1 ÷ 2 0   1

# 按位与 & 运算
const status = rest & Status.ENABLE ? '启用' : '禁用';
const status = 25 & 16 ? '启用' : '禁用';

25 二进制 10011
步骤 计算  余数
1    25 ÷ 2    12   1
2    12 ÷ 2    6   0
3    6 ÷ 2    3   0
4    3 ÷ 2    1   1
5    1 ÷ 2    0   1

16 二进制 00001
步骤 计算  余数
1    16 ÷ 2    8   0
2    8 ÷ 2    4   0
3    4 ÷ 2    2   0
4    2 ÷ 2    1   0
5    1 ÷ 2    0   1

25 & 16 的结果是 10011 00001 位数相等的结果为 00001 转为 10 进制结果为 1
也就是 25 & 16 等于 1


// 示例1:5 & 3
// 5 的二进制:0101
// 3 的二进制:0011
// 按位与结果:0001 (十进制1)
console.log(5 & 3); // 输出: 1

// 示例2:12 & 5
// 12的二进制:1100
// 5 的二进制:0101
// 按位与结果:0100 (十进制4)
console.log(12 & 5); // 输出: 4