Eduard
Eduard

Reputation: 9165

Freezing the elements of an array

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.

Upvotes: 0

Views: 793

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074485

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.

Upvotes: 4

Related Questions