冻结对象不影响call绑定this,报错源于函数内修改只读属性;应将this视为只读,通过返回新对象实现变更,严格模式有助于暴露设计缺陷。

严格模式下,call 本身不会“打断冻结对象锁定的 this 上下文”——这个说法存在概念混淆。冻结对象(如 Object.freeze(obj))影响的是对象自身的可变性(属性是否可写、可配置、可枚举),而 this 的绑定发生在函数调用时,与目标对象是否被冻结无关。真正报错的,不是 call 调用本身,而是你在 call 指定的 this 上尝试执行**违反冻结规则的操作**(比如给只读属性赋值)。
先搞清哪里会报错
冻结对象后,它的属性变成只读、不可配置。如果你用 call 把一个函数的 this 绑定到该冻结对象,然后函数内部试图修改该对象的属性,就会触发 TypeError:
-
不是 call 报错:
foo.call(frozenObj)这行本身能成功执行; -
是函数体内部报错:比如函数里写了
this.prop = 123,而frozenObj.prop是只读的。
常见错误场景和对应处理方式
场景1:函数内直接赋值给 this 属性
❌ 错误写法:
"use strict";
const frozen = Object.freeze({ name: "Alice" });
function updateName() {
this.name = "Bob"; // TypeError: Cannot assign to read only property 'name'
}
updateName.call(frozen);
✅ 正确做法:
- 避免在函数中直接修改
this的属性; - 改用返回新对象的方式:
return { ...this, name: "Bob" }; - 或提前检查属性是否可写:
if (Object.getOwnPropertyDescriptor(this, 'name')?.writable) { this.name = "Bob" }。
场景2:函数内调用会修改 this 的方法(如 push、sort)
❌ 错误写法:
const arr = Object.freeze([1, 2, 3]);
function mutate() {
this.push(4); // TypeError: Can't add property 3, object is not extensible
}
mutate.call(arr);
✅ 正确做法:
- 不调用会改变原数组的方法;
- 改用非变异方法:
[...this, 4]或this.concat(4); - 若必须操作,先解冻(仅限开发调试,生产慎用):
const unfrozen = {...arr}。
关键原则:冻结 ≠ 禁止绑定,而是禁止修改
call 只负责把函数的 this 设为指定对象,它不关心那个对象是否冻结。冻结对象的目的是防止意外修改,所以处理的核心不是“绕过冻结”,而是“尊重冻结”:
- 把
this当作只读上下文来读取数据,而非可写容器; - 所有变更逻辑应放在函数外部,或通过返回新值实现;
- 如果业务逻辑确实需要修改,说明该对象不该被冻结——冻结前就要评估使用场景。
顺便提醒:严格模式在这里只是放大了问题
非严格模式下,给只读属性赋值会静默失败(不报错但也不生效);严格模式让这类错误立即暴露,反而帮你提前发现设计缺陷。这不是 bug,是 feature。











