How to protect a JS array against truncation
Tags: javascript, webdev
Published: 2022-08-29 10:51:00

_Disclaimer: There are several options how to truncate an array in JS but here I would like to talk only about one of them._

If you want to truncate an array in JS there is a trick with the **length** property:

```js
const numbers = [1, 2, 3, 4, 5];

if (numbers.length > 3) {
  numbers.length = 3;
}

console.log(numbers); // [1, 2, 3]
console.log(numbers.length); // 3
```

Sometimes I like to set a small theoretical JS problems for myself to keep fit. 
So let's do a small exercise together and try to find a solution how to protect an array and disable its mutation by length property manipulation.

<u>**1st solution:**</u>

A length property of an array by default has following attributes:

**enumerable === false**
**configurable === false**
**writable === true**

When a property attribute _configurable_ is _false_ just a few changes to a property is allowed. One of them is to change attribute _writable_ to _false_. 
A combination of attributes _configurable===false_ and _writable === false_ makes impossible to delete or modify a property by any possible ways.

```js
const numbers = [1, 2, 3, 4, 5];
Object.defineProperty(numbers,'length',{writable:false})

if (numbers.length > 3) {
  numbers.length = 3;
}

console.log(numbers); // [1, 2, 3, 4, 5]
console.log(numbers.length); // 5

Object.defineProperty(numbers,'length',{value:3})
// Uncaught TypeError: Cannot redefine property: length
```  

The goal is achieved! An array mutation by property **length** is disabled.

<u>**2st solution (kind of exotic):**</u>

Let's check what [ES specification](https://tc39.es/ecma262/#sec-arraysetlength) says about array length manipulation :

> For each own property key P of A that is an array index, whose numeric value is greater than or equal to newLen, in descending numeric index order, do:

To put it simply, a truncation of an array starts from a tail of an array (in descending numeric index order). In our first code snippet element 5 will be deleted first and then element 4.

Now it is time to remember that in JS <u>an array is an object</u> (exotic one but still an object) where <u>an index is a key of a property and an element is a value of a property. </u>
**So when an array is truncated it means that array's properties whose keys greater or equal than a new length are deleted.** 

> Let deleteSucceeded be ! A._[[Delete]]_(P).

Here _A_ is an array, _[[Delete]]_ is an abstract method to delete property from an object, _P_ is a property key. 

So let's combine together. 
In our example with _numbers_ array a property with key === 4 would de deleted first. But there is a way to prevent deletion of a property from an object - set attribute _configurable_ to _false_!

Experiment:
```js
const numbers = [1, 2, 3, 4, 5];
Object.defineProperty(numbers,'4',{configurable:false})

if (numbers.length > 3) {
  numbers.length = 3;
}

console.log(numbers); // [1, 2, 3, 4, 5]
console.log(numbers.length); // 5
```

The array is not mutated!
According the specification ([see](https://tc39.es/ecma262/#sec-arraysetlength) step 17) an array truncation due to length property reduction stops when a first unsuccessful property deletion is met. 
So if we protect from deleting just last array element then we achieve our goal - prevent array mutation. 

**P.S.** _[[Delete]]_ abstract method which is used to defined array truncation is also used to define logic of **delete** operator. So when you reduce length of an array you literally delete properties from it. As a side effect, pop() method also will be disabled for such array.

**P.S.S.** I have never used that in my work and hope never will=) But I enjoy feeling that I understand a little bit how internal gears of JS are designed.