防御性设计将不确定性视为常态,通过可选链(?.)、空值合并(??)、结构类型双校验、object.freeze冻结配置、白名单控制动态键及避免动态执行等方式保障安全。

处理对象属性时,防御性设计不是为了“防出错”,而是从第一行访问开始就承认:对象可能不完整、数据可能被篡改、输入可能不可信。核心是把不确定性当作常态,用明确的检查代替侥幸。
用可选链和空值合并替代手动判断
避免写 if (obj && obj.user && obj.user.profile && obj.user.profile.name) 这类嵌套判断——它冗长、易漏、难维护。现代 JS 提供更直接的表达方式:
-
obj?.user?.profile?.name:任意一环为null或undefined,整个表达式立即返回undefined,不报错 -
obj?.settings?.theme ?? 'light':仅当左侧为null或undefined时才取默认值;0、false、空字符串等“falsy 但合法”的值会被保留 - 方法调用也支持:
service?.fetch?.(),确保函数存在且可调用
对关键输入做结构与类型双校验
来自 API、URL、localStorage 的数据不能假设结构一致。光看值是否“有”还不够,得确认它“该是什么”:
- 检查字段是否存在且属于对象自身:
obj.hasOwnProperty('id')或'id' in obj(注意in会查原型链,hasOwnProperty更严格) - 验证类型而非真假值:
typeof obj.id === 'string'、Array.isArray(obj.tags)、Number.isFinite(obj.price) - 用户输入务必清洗:
input.trim()去首尾空格,Number(input)+isNaN()判断是否为有效数字
冻结不可变配置,切断运行时篡改路径
权限列表、API 地址映射、状态枚举这些不该被修改的数据,一旦初始化就应锁定:
-
Object.freeze(config):禁止增删改属性(浅冻结,嵌套对象仍可变) - 需要深层防护时,用
structuredClone()创建副本再冻结:const safeConfig = Object.freeze(structuredClone(rawConfig)) - 对内置原型也做基础防护(尤其在加载第三方脚本前):
Object.freeze(String.prototype)、Object.freeze(Object.prototype)
拒绝动态键名的随意拼接
obj[userInput] 或 window[untrustedKey] 类写法极易引发原型污染或越权访问,必须加白名单控制:
- 只允许预定义键:
const allowedKeys = ['name', 'email', 'avatar']; if (!allowedKeys.includes(key)) throw new Error('Invalid key') - DOM 渲染避免拼接 HTML:
el.textContent = data[key]替代el.innerHTML,必要时用DOMPurify.sanitize() - 禁用
eval、Function构造函数、setTimeout字符串参数等动态执行方式
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











