admin管理员组

文章数量:1419673

In the console, I get abc despite setting {writable:false}. Could you explain how changing metadata works?

let portfolio = {
  myFirstName: "Bob",
  myLastName: "Alice",
  myAge: 26,
  aboutMe: function() {
    return ("First Name: " + this.myFirstName + "; Last Name: " + this.myLastName + "; Age: " + this.myAge + ";");
  }
};
Object.defineProperty(portfolio, "myFirstName", { writable: false });
Object.defineProperty(portfolio, "myFirstName", { value: "abc" });
console.log(portfolio.myFirstName);

In the console, I get abc despite setting {writable:false}. Could you explain how changing metadata works?

let portfolio = {
  myFirstName: "Bob",
  myLastName: "Alice",
  myAge: 26,
  aboutMe: function() {
    return ("First Name: " + this.myFirstName + "; Last Name: " + this.myLastName + "; Age: " + this.myAge + ";");
  }
};
Object.defineProperty(portfolio, "myFirstName", { writable: false });
Object.defineProperty(portfolio, "myFirstName", { value: "abc" });
console.log(portfolio.myFirstName);

Share Improve this question edited Jan 7, 2021 at 18:58 Boann 50.1k16 gold badges124 silver badges152 bronze badges asked Jan 7, 2021 at 11:49 Darkhan10Darkhan10 796 bronze badges 3
  • Why all of this instead of just a class with getters and setters? – tadman Commented Jan 7, 2021 at 11:52
  • 4 If you do this: portfolio.myFirstName = 'abc', myFirstName will remain Bob – Vaibhav Vishal Commented Jan 7, 2021 at 11:52
  • I'd guess that Object.defineProperty doesn't honour the writable flag. I'd have to check the specs to be sure how this should work – Liam Commented Jan 7, 2021 at 11:54
Add a ment  | 

3 Answers 3

Reset to default 8

in your 2nd line Object.defineProperty(portfolio, "myFirstName", { value: "abc" }); you are defining the property again.

You are not assigning a value. You are tossing out the old property and replacing it with a brand spanking new one. (Technically incorrect, it goes through a lot of steps to evaluate and apply values to property properties, but for simple understanding, I believe this suffices as understanding, as it feels like a new one in this scenario. Please read the link if you wish to have the plex truth)

To assign a new value use portfolio.myFirstName = "value here" and you see it's write protected.

let portfolio = {
  myFirstName: "Bob",
  myLastName: "Alice",
  myAge: 26,
  aboutMe: function() {
    return ("First Name: " + this.myFirstName + "; Last Name: " + this.myLastName + "; Age: " + this.myAge + ";");
  }
};
Object.defineProperty(portfolio, "myFirstName", { writable: false });
portfolio.myFirstName = "Alice";
console.log(portfolio.myFirstName);

to prevent the workaround, call Object.freeze() on the object after modifying its property. This will also have other side effects like not being able to edit the values of other properties.

let portfolio = {
  myFirstName: "Bob",
  myLastName: "Alice",
  myAge: 26,
  aboutMe: function() {
    return ("First Name: " + this.myFirstName + "; Last Name: " + this.myLastName + "; Age: " + this.myAge + ";");
  }
};
Object.defineProperty(portfolio, "myFirstName", { writable: false });
Object.freeze(portfolio);
Object.defineProperty(portfolio, "myFirstName", {value:"abc"});
console.log(portfolio.myFirstName);

writable: false only has an effect on Object.defineProperty if configurable is also set to false. See step 7 of the ValidateAndApplyPropertyDescriptor algorithm:

Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
  If current.[[Configurable]] is false and current.[[Writable]] is false, then
    If Desc.[[Writable]] is present and Desc.[[Writable]] is true, return false.
    If Desc.[[Value]] is present and SameValue(Desc.[[Value]], current.[[Value]]) is false, return false.
    Return true.

That's likely because as long as a property is configurable, nothing stops you from changing the value of writable back to true, e.g.

Object.defineProperty(
  portfolio,
  "myFirstName",
  {value: "abc", writable: true}
);

Note that any property declared as part of an object literal automatically has {writable: true, configurable: true, enumerable: true}.

Examples

Can't assign because writable and configurable are both false:

var obj = {};
Object.defineProperty(obj, 'test', {
  value: 42,
  configurable: false,
  writable: false,
  enumerable: true,
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 21});
console.log(obj);

Can assign a value because writable or configurable are true:

var obj = {};
Object.defineProperty(obj, 'test', {
  value: 42,
  configurable: true,
  writable: false,
  enumerable: true
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 21});
console.log(obj);


var obj = {};
Object.defineProperty(obj, 'test', {
  value: 42,
  configurable: false,
  writable: true,
  enumerable: true
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 21});
console.log(obj);

Lastly, if writable and configurable are both false but if the new value is the same as the current value, no error will be thrown since no change is actually being made to the property:

var obj = {};
Object.defineProperty(obj, 'test', {
  value: 42,
  configurable: false,
  writable: false,
  enumerable: true,
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 42});
console.log(obj);


Setting writable: false will work as expected for normal assignments (foo.bar = 42) because such assignments go through OrdinarySetWithOwnDescriptor which check the writable value of an existing property descriptor first.

You're working around that access restriction to hard-set the property. That only impacts the portfolio.myFirstName mutator.

You really can't block access to defineProperty like that. It's too low-level. That's probably a good thing, though, since it is dependable.

本文标签: How does changing metadata in JavaScript workStack Overflow