如何在JavaScript中使对象的特定属性不可变?

3
如果我有一个像这样的对象:
let person = {
       firstName: 'Krishna',
       lastName: 'Jai',
       age: 12
}
person.firstName = 'Kumar';
console.log(person);

//output
Object { firstname: 'Kumar', lastName: 'Jai', age: 12 }

但是我不应该让任何用户更改人的名字。如何使名字不可变,以便其值无法被覆盖?


实际上,根据定义,您对象的所有属性已经具有不可变的值,因为它们是“String”类型。正确的术语应该是只读和不可配置属性。 - Redu
3个回答

12

如果您已经有一个属性,您可以修改它的描述符的 writable 属性为 false,如下所示:

Object.defineProperty(person, 'firstName', {
      writable: false,
      configurable: false
});

为防止将writable改回true等未来修改,应指定configurable: false,以防止对firstName属性进行更改。

有关属性描述符,请在此处查看更多信息

如果您正在定义之前不存在的属性,则所有描述符属性都默认为false,您只需要指定valueenumerable即可:

Object.defineProperty(person, "firstName", {
    value: "Krishna",
    enumerable: true
});

我不太确定你的意思,但是将上述内容应用于OP的对象肯定会使firstName重新配置(因此变得可写):https://jsfiddle.net/11fcmftd/ - T.J. Crowder
如果您添加 configurable: false,它将受到保护:https://jsfiddle.net/11fcmftd/1/ - T.J. Crowder

5

您有几个选择,但可能最简单的是创建一个只读、不可配置的属性:

let person = {
    lastName: 'Jai',
    age: 12
};
Object.defineProperty(person, "firstName", {
    value: "Krishna",
    enumerable: true
});

标记writableconfigurable在定义新属性时都默认为false。(你可以不把它设置为可枚举的,但……)

例如:

let person = {
    lastName: 'Jai',
    age: 12
};
Object.defineProperty(person, "firstName", {
    value: "Krishna",
    enumerable: true
});
console.log("before: ", person.firstName);
person.firstName = "Mohinder";
console.log("after setting in loose mode:", person.firstName);
function foo() {
    "use strict";
    console.log("trying to set in strict mode:");
    person.firstName = "Mohinder"; // Error
}
foo();

如果您希望在事后应用更改,则需要指定标志:

let person = {
    firstName: "Krishna",
    lastName: 'Jai',
    age: 12
};
Object.defineProperty(person, "firstName", {
    writable: false,
    configurable: false
});

例子:

let person = {
    firstName: "Krishna",
    lastName: 'Jai',
    age: 12
};
Object.defineProperty(person, "firstName", {
    writable: false,
    configurable: false
});
console.log("before: ", person.firstName);
person.firstName = "Mohinder";
console.log("after setting in loose mode:", person.firstName);
function foo() {
    "use strict";
    console.log("trying to set in strict mode:");
    person.firstName = "Mohinder"; // Error
}
foo();


1
“writable”和“configurable”这两个标志默认都为false - 是的,但仅当属性首次被定义时才是如此,对吗?如果它已经存在,则描述符将被合并。 - Max Koretskyi
这个解释得很清楚。非常感谢你。 - rgk
@T.J.Crowder,“嘿,你知道什么……” - 我不理解你的意思。 - Max Koretskyi
@Maximus:我的意思是“你说得对,我不知道。” :-) 关于可配置性:关键在于,任何人都可以重新配置为可写的只读属性实际上并不是真正的只读,这就是为什么我还将其设置为不可配置。 - T.J. Crowder
1
@Maximus:我也这么认为,谢谢。今天我学到了一个知识点,如果 configurablefalse,我仍然可以将 writabletrue 改为 false,这...太奇怪了 :-) (但在规范的第8.a.i步骤中清楚地说明了。非常离奇。) - T.J. Crowder
显示剩余7条评论

0
你可以使用getter方法并省略setter。

let person = {
       get firstName() { return 'Krishna'; },
       lastName: 'Jai',
       age: 12
    };

person.firstName = 'Kumar';
console.log(person);


我个人更喜欢这种方法。 - evolutionxbox
3
为什么需要在每次访问属性时调用函数,而实际上并没有必要这样做呢? - T.J. Crowder
因为整洁。 :P - evolutionxbox
在JS中,这不是正确的方法来完成这项工作,因为我可以轻松地通过添加一行代码来覆盖上面的代码,例如:Object.defineProperty(person, "firstName", {get : function(){ return "Nina Scholz"}});console.log(person.firstName) - Redu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接