前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库writable false 严格模式与非严格模式赋值结果
JavaScript对象与原型

给 writable 为 false 的属性赋值,严格模式和非严格模式分别会怎样?

严格模式下抛 TypeError;非严格模式下赋值被悄悄丢掉,不报错,属性还是原来的值。看的是执行赋值那行代码所在的作用域是不是严格模式,和属性在哪里定义无关。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#对象与原型#流处理
先看核心答案读代码示例
理解线索

一次赋值失败,三种表现

  1. 严格模式赋值抛 TypeError,ES module 和 class 内部默认就是严格模式
  2. 非严格模式赋值表达式照常结束,值没变,最难排查
  3. Reflect.set不抛错,返回 false,适合需要显式判断成败的地方

writable: false 只管赋值。属性如果还是 configurable: true,用 defineProperty 照样能把值改掉。

核心回答

先记住这个答案

属性被定义成 writable: false 后,obj.x = 9 这样的赋值一定不会成功,区别只在失败时报不报错。严格模式下抛 TypeError;非严格模式下这句赋值被静默丢掉,代码继续往下跑,属性还是原值。决定行为的是执行赋值那行代码所处的作用域,和属性定义时是不是严格模式无关。需要在不抛错的前提下知道写没写成功,可以用 Reflect.set,它返回布尔值。

  • 严格模式:抛 TypeError,值不变
  • 非严格模式:静默失败,值不变
  • 看赋值那行代码的严格性,不看定义处
  • 原型上的只读属性也会挡住子对象赋值

同一次赋值,两种结局

下面把同一个只读属性分别放在非严格函数和严格函数里赋值,再用 Reflect.set 试一次。

严格模式、非严格模式和 Reflect.setJavaScript
const obj = {}
Object.defineProperty(obj, 'x', { value: 42, writable: false })

function sloppyWrite() {
  obj.x = 9 // 非严格模式:静默失败,不报错
  return 'sloppy x=' + obj.x
}

function strictWrite() {
  'use strict'
  try {
    obj.x = 9
  } catch (e) {
    return e.name + ' x=' + obj.x
  }
}

console.log(sloppyWrite())
console.log(strictWrite())
console.log(Reflect.set(obj, 'x', 9))
查看输出与解释
sloppy x=42
TypeError x=42
false

三种情况下 x 都还是 42,写入要么完全成功,要么完全无效,不存在写了一半的状态。 为什么会有两种表现?早期 JavaScript 的设计是尽量让脚本跑下去,很多错误都选择了静默失败。严格模式是后来加的,专门把这类静默错误变成显式异常。所以现在的建议很简单:代码统一跑在严格模式下。好在 ES module 和 class 内部默认就是严格模式,用现代构建工具写的业务代码基本都已经是严格的了。

静默失败为什么危险
比如某个 SDK 把默认超时锁成只读,业务里有一个没开严格模式的老脚本写了 config.timeout = 500。这句不报错,开发者以为改成了 500 毫秒,实际请求还是按原值超时,排查时很难想到是这行赋值根本没生效。换成严格模式,同样的代码在第一次运行时就会抛错。

原型上的只读属性也会挡住赋值

这是面试里区分度最高的一点。正常情况下给对象赋值,如果自身没有这个属性,会直接在对象上新建一个,遮住原型上的同名属性。但如果原型链上找到的同名属性是只读的,赋值就会失败,不会新建自有属性。

子对象写不进去,defineProperty 可以JavaScript
const proto = {}
Object.defineProperty(proto, 'id', { value: 1, writable: false })
const child = Object.create(proto)

child.id = 2 // 非严格模式下静默失败
console.log(child.id, Object.hasOwn(child, 'id'))

Object.defineProperty(child, 'id', { value: 2 }) // 绕开赋值,直接定义自有属性
console.log(child.id, Object.hasOwn(child, 'id'))
查看输出与解释
1 false
2 true

赋值走的是「先沿原型链找同名属性,再决定怎么写」的流程,找到只读属性就停了。defineProperty 不走这套流程,它只管在 child 自身上定义属性,所以能成功。 这个行为在实际项目里会踩到:有人对 Object.prototype 或某个公共原型做了 Object.freeze,之后所有继承它的对象都没法用赋值的方式添加同名属性,比如 toString。

回答前,多想一步

容易答错的地方

以为定义属性时开了严格模式就行
严格性看的是执行赋值的那行代码。定义属性的模块是严格的,但调用方是非严格脚本,赋值照样静默失败。
以为 writable: false 就彻底改不了了
它只挡赋值。如果属性是 configurable: true,用 Object.defineProperty(obj, 'x', { value: 9 }) 一样能把值改掉。要彻底锁住,得同时设 configurable: false。注意 defineProperty 没写 configurable 时默认就是 false,上面第一段代码里的 x 其实已经锁死了,再用 defineProperty 换成别的值会抛 TypeError。
以为只有数据属性会这样
只有 get 没有 set 的访问器属性,赋值同样会失败,规则一样:严格模式抛错,非严格模式静默失败。给不可扩展对象添加新属性也属于同一类失败。
试着用自己的话回答

面试官还会怎么问?

非严格模式下怎么知道写入失败了?

用 Reflect.set(obj, key, value),写成功返回 true,失败返回 false,不依赖异常。也可以写完后再读一次对比,但不如 Reflect.set 直接。

Object.freeze 和 writable: false 是什么关系?

Object.freeze 会把对象所有自有数据属性都设成 writable: false 和 configurable: false,再把对象设为不可扩展。所以给冻结对象的属性赋值,表现和这题完全一样。它是浅冻结,嵌套对象的属性还能改。

数组的 length 设成只读会怎样?

push 这类会修改 length 的方法会抛 TypeError,而且不管调用处是不是严格模式都会抛,因为 push 内部是按严格语义去写 length 的。直接给 length 赋值则遵循本题的严格或非严格规则。

从一道题,走向一组知识

把知识连起来

对象与原型

构造函数返回对象或原始值时,new 最后会得到什么?

构造函数里给 this 赋值同样走普通赋值规则,原型上有只读同名属性时实例字段会写不进去。

对象与原型

读取一个对象属性时,JavaScript 是按什么顺序在原型链上找的?

赋值前引擎也要沿原型链找同名属性,这正是原型上的只读属性能挡住子对象的原因。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Strict mode

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 同一次赋值,两种结局
  3. 原型上的只读属性也会挡住赋值
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑