0

I have an array of certain primitive elements:

const array = [1, 2, 3]

I want to be able to temporarily freeze an element of this array and prevent it from being modified. But the moment the element can be allowed to be modified, there should be a way to unfreeze the value.

Is there a way to do so?

Off-topic for those who flag this question as duplicate:

The question is about freezing the elements of an array, not the entire array. It is not a duplicate of a question regarding freezing the entire array.

Eduard
  • 8,437
  • 10
  • 42
  • 64
  • Does [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#Description) meet your requirements? – zero298 Aug 30 '18 at 15:56
  • My thoughts would be to make a custom object that holds the primitive value and a boolean value of locked, if it's locked, don't allow change, if it is not locked, allow change. – Ryan Wilson Aug 30 '18 at 15:56
  • 3
    Possible duplicate of ['Freezing' Arrays in Javascript?](https://stackoverflow.com/questions/7509894/freezing-arrays-in-javascript) – Leo Aug 30 '18 at 15:56
  • Prevent certain values from being modified by whom? A programmer? A user? I'm not sure what the end goal is here. You'd probably want to look at objects. – chevybow Aug 30 '18 at 15:56
  • @zero298 I tried doing it this way: `const arr = [1, Object.freeze(2), 3]`, it did not work. – Eduard Aug 30 '18 at 15:57
  • 2
    @Leo this is not a duplicate, he doesn't want to freeze the entire array. – Ryan Wilson Aug 30 '18 at 15:58
  • Even if `Object.freeze(2)` would work, what would be the point of "freezing" a constant value? O.o – Andreas Aug 30 '18 at 15:59
  • 1
    @Andreas Only the array definition is constant. You can still push and pop values of it in JS. You just can't reassign to `array`, so `array = [5];` will error. – zero298 Aug 30 '18 at 16:00
  • @zero298 I'm totally aware of that... TO is calling `Object.freeze()` on the number `2` which doesn't make any sense - as explicitly mentioned in my comment... – Andreas Aug 30 '18 at 16:02
  • @Andreas Oh, my bad, I see what you mean. Yes I don't know what the purpose of that is. – zero298 Aug 30 '18 at 16:04
  • @Andreas Nobody ever said it makes sense to do so. I was suggested to try this method. I tried. It did not work. I relayed this information to the advisor. Period. – Eduard Aug 30 '18 at 16:05

1 Answers1

4

You don't want to freeze the value, you want to freeze the property that holds the value (the "1" property of the array, in your case). You'd use Object.defineProperty to redefine the property without the writable flag. To make it writable again, you redefine it with writable: true:

const array = [1, 2, 3];
console.log("A", array.join(", ")); // 1, 2, 3

// Freeze it
Object.defineProperty(array, "1", {
  value: array[1],
  writable: false, // For emphasis (this is the default)
  enumerable: true,
  configurable: true
});
console.log("B1", array.join(", ")); // 1, 2, 3
array[1] = 42;                       // <== Doesn't change it
console.log("B2", array.join(", ")); // 1, 2, 3 (still)

// Thaw it
Object.defineProperty(array, "1", {
  value: array[1],
  writable: true,
  enumerable: true,
  configurable: true
});
console.log("C1", array.join(", ")); // 1, 2, 3
array[1] = 42;                       // <== Changes it
console.log("C2", array.join(", ")); // 1, 42, 3 (changed!)

That assignment would be an exception if the code doing the assignment were running in strict mode:

"use strict";
const array = [1, 2, 3];
console.log("A", array.join(", ")); // 1, 2, 3

// Freeze it
Object.defineProperty(array, "1", {
  value: array[1],
  writable: false, // For emphasis (this is the default)
  enumerable: true,
  configurable: true
});
console.log("B1", array.join(", ")); // 1, 2, 3
array[1] = 42;                       // <== Doesn't change it
console.log("B2", array.join(", ")); // 1, 2, 3 (still)

// Thaw it
Object.defineProperty(array, "1", {
  value: array[1],
  writable: true,
  enumerable: true,
  configurable: true
});
console.log("C1", array.join(", ")); // 1, 2, 3
array[1] = 42;                       // <== Changes it
console.log("C2", array.join(", ")); // 1, 42, 3 (changed!)

But note that if your code can redefine it to make it writable, anyone else's code can, too.

Alternately, give it a getter and setter, make it non-configurable (so no one else can redefie it), and maintain a flag:

const array = [1, 2, 3];
let elementValue = array[1];
let writable = true;
Object.defineProperty(array, "1", {
  get: function() {
    return elementValue;
  },
  set: function(newValue) {
    if (writable) {
      elementValue = newValue;
    }
  },
  enumerable: true,
  configurable: false // Again, emphasis
});

console.log("A", array.join(", ")); // 1, 2, 3
array[1] = 42;
console.log("B", array.join(", ")); // 1, 42, 3 -- it changed
writable = false;
array[1] = 67;
console.log("C", array.join(", ")); // 1, 42, 3 -- didn't change
writable = true;
array[1] = 94;
console.log("D", array.join(", ")); // 1, 94, 3 -- changed

Naturally, you'd hide some of that and just expose the array itself.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875