0

0

js如何让原型属性变为只读

小老鼠

小老鼠

发布时间:2025-07-29 12:39:01

|

905人浏览过

|

来源于php中文网

原创

要让原型属性只读,核心方法是使用object.defineproperty()并将writable设为false;1. 使用object.defineproperty()在原型上定义属性时设置writable: false,可防止属性被重新赋值;2. 该方法通常配合configurable: false和enumerable: true使用,以锁定属性配置并控制是否可枚举;3. 在严格模式下尝试修改只读属性会抛出typeerror,非严格模式下静默失败;4. writable: false仅保护引用不被修改,若属性值为对象或数组,其内部仍可变;5. 其他机制如object.freeze()可冻结整个对象(浅冻结),object.seal()密封对象防止增删属性但允许修改值,const则确保变量引用不变但不保护对象内部可变性;这些机制按控制粒度由细到粗分别为defineproperty、seal、freeze和const,原型属性只读场景中defineproperty最精确适用。

js如何让原型属性变为只读

在JavaScript中,要让一个原型属性变为只读,核心方法是利用Object.defineProperty()。这个内置方法允许你对对象的属性进行精细控制,包括定义其是否可写。

js如何让原型属性变为只读

解决方案

要实现原型属性的只读性,你需要直接在原型对象上使用Object.defineProperty(),并将属性描述符中的writable设置为false

function MyConstructor() {
    // 构造函数逻辑
}

// 定义一个普通的可写原型属性
MyConstructor.prototype.editableProperty = "我可以被修改";

// 使用Object.defineProperty定义只读原型属性
Object.defineProperty(MyConstructor.prototype, 'readOnlyMethod', {
    value: function() {
        console.log("这是一个只读的方法,不应该被覆盖。");
    },
    writable: false,     // 关键:设置为false使其不可写
    configurable: false, // 通常也设置为false,防止属性被删除或重新配置
    enumerable: true     // 根据需要决定是否可枚举
});

Object.defineProperty(MyConstructor.prototype, 'immutableValue', {
    value: 123,
    writable: false,
    configurable: false,
    enumerable: true
});


// 实例化一个对象
const instance = new MyConstructor();

console.log("--- 尝试修改只读属性 ---");

// 尝试修改只读方法
try {
    instance.readOnlyMethod = function() {
        console.log("我尝试覆盖了只读方法!");
    };
    console.log("成功覆盖只读方法 (不应该发生)!");
} catch (e) {
    console.error("尝试覆盖只读方法失败 (预期)!", e.message); // 在严格模式下会抛出TypeError
}

// 尝试修改只读值
try {
    instance.immutableValue = 456;
    console.log("成功修改只读值 (不应该发生)!");
} catch (e) {
    console.error("尝试修改只读值失败 (预期)!", e.message);
}

// 尝试修改可写属性 (对比)
instance.editableProperty = "我已经被修改了";
console.log("可写属性修改后:", instance.editableProperty); // 输出: 我已经被修改了

console.log("\n--- 验证属性状态 ---");
console.log("只读方法调用:");
instance.readOnlyMethod(); // 仍然调用原始方法
console.log("只读值:", instance.immutableValue); // 仍然是原始值

为什么会需要让原型属性只读?这背后的考量是什么?

在实际开发中,我们选择让原型属性只读,往往是出于对代码健壮性和可维护性的深层考量。这不单单是为了遵循某种规范,更多的是一种防御性编程的体现。

js如何让原型属性变为只读

首先,它能有效防止意外修改。想象一下,你定义了一个核心工具方法在原型上,供所有实例共享。如果这个方法可以被随意覆盖,那么在大型项目中,某个不经意的赋值操作就可能破坏其原有功能,导致难以追踪的bug。将其设为只读,就像给它上了一把锁,即便有人尝试去修改,也会立即收到错误提示(在严格模式下),这比默默失败要好得多。

其次,这有助于维护API的契约性。当你的库或模块对外提供服务时,原型上的某些方法或属性就是其公共API的一部分。明确这些属性是只读的,意味着你向使用者承诺了它们的稳定性和不变性。这对于依赖你代码的第三方来说,提供了更强的信任感和可预测性。

js如何让原型属性变为只读

再者,从架构设计的角度看,只读属性可以确保某些共享的、不应变动的数据或行为保持一致性。比如,一个通用的常量或者一个不依赖实例状态的纯函数,将其放在原型上并设为只读,能够清晰地表达其“不可变”的意图,减少了潜在的副作用和理解成本。这就像给团队成员一个明确的信号:这部分是稳定的基石,不要去动它。

尝试修改只读原型属性时,JavaScript的行为有何不同?

当我们尝试去修改一个已经被Object.defineProperty()设置为writable: false的原型属性时,JavaScript的反应会根据当前的运行模式(严格模式或非严格模式)而有所不同,这其实是JavaScript语言本身一个非常有趣的“双面性”。

严格模式(Strict Mode)下,如果你尝试对一个只读属性进行赋值操作,JavaScript会毫不留情地抛出一个TypeError。这是一种非常直接且明确的错误反馈,它会立即中断当前的操作,并告诉你:“嘿,你不能修改这个属性!”这对于调试和快速发现问题非常有帮助,因为它强制你面对并解决这个不被允许的操作。在现代JavaScript开发中,我们几乎总是推荐使用严格模式,因为它能捕获更多潜在的错误,让代码更健壮。

然而,在非严格模式(Non-Strict Mode)下,同样的操作却会静默失败。这意味着你尝试赋值的代码不会报错,但属性的实际值也不会改变。这听起来可能很“宽容”,但实际上却是一个巨大的隐患。因为它不会给出任何提示,你可能会误以为修改成功了,而实际上你的程序逻辑已经偏离了预期。这种静默失败是JavaScript早期设计中的一个“坑”,经常导致难以排查的逻辑错误。这也是为什么现在大家普遍倾向于使用严格模式的原因之一。

需要特别强调的是,writable: false仅控制属性本身是否可以被重新赋值。如果这个只读属性的值是一个可变对象(比如一个数组或另一个对象),那么你仍然可以修改这个可变对象内部的属性或元素writable: false只是保护了指向这个对象的引用不被改变,而没有保护对象内容的不可变性。这是一个常见的误解,务必区分开。例如:

Text-To-Song
Text-To-Song

免费的实时语音转换器和调制器

下载
function MyClass() {}

const sharedArray = [1, 2, 3];
Object.defineProperty(MyClass.prototype, 'data', {
    value: sharedArray,
    writable: false,
    configurable: false
});

const instance1 = new MyClass();
const instance2 = new MyClass();

console.log("原始共享数组:", instance1.data); // [1, 2, 3]

// 尝试修改data属性本身 (失败)
try {
    instance1.data = [4, 5, 6];
} catch (e) {
    console.error("尝试重新赋值data属性失败 (预期):", e.message);
}

// 修改data属性所指向的数组内容 (成功,因为数组本身是可变的)
instance1.data.push(4);
console.log("修改数组内容后,instance1.data:", instance1.data); // [1, 2, 3, 4]
console.log("修改数组内容后,instance2.data:", instance2.data); // [1, 2, 3, 4] (因为是共享引用)

这段代码清晰地展示了,writable: false保护的是data这个属性引用本身,而不是sharedArray这个数组的内容。

除了Object.defineProperty,还有其他相关或相似的机制吗?它们有何区别

除了Object.defineProperty来控制单个属性的只读性,JavaScript还提供了一些更宏观的机制来限制对象的修改,它们各有侧重,适用于不同的场景。理解它们的区别至关重要。

1. Object.freeze()

Object.freeze() 是一个非常强大的方法,它能让一个对象变得“冻结”。一旦一个对象被冻结,你就不能再添加新的属性,不能删除现有属性,也不能修改现有属性的值(包括它们的writableconfigurable等描述符)。这意味着,它不仅让属性变得只读,还阻止了对象的结构变化。

  • 区别于defineProperty: Object.freeze()作用于整个对象,而不是单个属性。它相当于对对象的所有现有属性都隐式地设置了writable: falseconfigurable: false,并且将extensible设置为false(即不可扩展,不能添加新属性)。
  • 使用场景: 当你需要确保一个对象及其所有直接属性都完全不可变时,Object.freeze()是理想选择。例如,定义一个配置对象或一个常量枚举。
  • 局限性: Object.freeze()是“浅冻结”。如果冻结的对象内部包含其他对象(如数组或嵌套对象),这些内部对象本身并不会被冻结,它们仍然可以被修改。
const myFrozenObject = {
    prop1: 10,
    nested: { a: 1 }
};
Object.freeze(myFrozenObject);

// 尝试修改直接属性 (失败)
myFrozenObject.prop1 = 20; // 严格模式下抛出TypeError
console.log(myFrozenObject.prop1); // 10

// 尝试修改嵌套对象内部 (成功,因为是浅冻结)
myFrozenObject.nested.a = 2;
console.log(myFrozenObject.nested.a); // 2

2. Object.seal()

Object.seal() 方法介于definePropertyfreeze之间。它能“密封”一个对象,使其变得不可扩展(不能添加新属性),并且所有现有属性都变得不可配置(不能删除或改变它们的描述符,如writable)。但是,现有属性的值仍然可以被修改

  • 区别于defineProperty: Object.seal()也作用于整个对象,但它允许现有属性的值被修改,而defineProperty可以精确控制单个属性的writable状态。
  • 使用场景: 当你需要固定一个对象的结构(属性集合),但又允许这些属性的值随时间变化时。
  • 局限性: 同样是浅层操作,不影响嵌套对象的修改。
const mySealedObject = {
    propA: 'hello',
    propB: 100
};
Object.seal(mySealedObject);

// 尝试修改现有属性的值 (成功)
mySealedObject.propA = 'world';
console.log(mySealedObject.propA); // 'world'

// 尝试添加新属性 (失败)
mySealedObject.propC = 'new'; // 严格模式下抛出TypeError
console.log(mySealedObject.propC); // undefined

3. const 关键字

const是ES6引入的声明变量的关键字,它用于声明一个常量const声明的变量在初始化后不能被重新赋值。

  • 区别于原型属性控制: const作用于变量声明,而不是对象属性。它确保变量名指向的引用不会改变,但如果这个引用指向的是一个对象,那么这个对象的内容仍然是可变的。
  • 使用场景: 声明不应改变引用的变量,例如函数、配置对象引用等。
  • 局限性: const无法阻止对象内部属性的修改,与Object.defineProperty控制原型属性的只读性是完全不同的概念和作用域
const myConfig = {
    url: 'api.example.com',
    timeout: 5000
};

// 尝试重新赋值myConfig变量 (失败)
// myConfig = {}; // TypeError: Assignment to constant variable.

// 修改myConfig对象内部的属性 (成功)
myConfig.timeout = 10000;
console.log(myConfig.timeout); // 10000

总而言之,Object.defineProperty提供了最细粒度的控制,允许你精确地定义单个属性的读写权限。而Object.freeze()Object.seal()则提供了更粗粒度的对象整体保护,const则是在变量声明层面提供不变性。选择哪种机制,取决于你需要保护的粒度和深度。在处理原型属性的只读性时,Object.defineProperty通常是最直接且精确的选择。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

554

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

374

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

732

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

477

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

394

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

991

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

657

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

551

2023.09.20

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

27

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 3.2万人学习

XML教程
XML教程

共142课时 | 5.7万人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号