'Freezing' Arrays in Javascript?
Asked Answered
C

4

72

Since the ECMA-262 specifications Javascript has gained the Object.freeze() method, which allows for objects, whose properties can not be changed, added or removed.

var obj = {'a':1, 'b:2'};
Object.freeze(obj);
Object.isFrozen(obj);       // returns true
obj.a = 10;                 // new assignment has no affect
obj.a;                      // returns 1

So far so good.

I am wondering, whether freeze() should also work on Arrays.

var arr = [1, 2];
Object.freeze(arr);
Object.isFrozen(arr);      // returns true
arr[0] = 10;
arr;                       // returns [10, 2] ... ouch!

Maybe I am wrong, but I was under the impression, that Array inherits from Object.

typeof obj                 // "object"
typeof arr                 // "object"

Any ideas, pointers, enlightenments would be highly appreciated.

Chopper answered 22/9, 2011 at 5:24 Comment(6)
I would expect it to freeze the array as well. Which browser(s) exhibit(s) said behavior?Mastoiditis
Doesn't work in FF6: var a = []; Object.freeze(a); a.push('foo'); // TypeError: a.push("foo") is not extensibleDianndianna
It looks like it's an implementation bug. 'Freezing arrays' works in Firefox (6.0.2), Chrome (14.0.835.186) - but not on the previous version. It does not work on Safari (5.1).Chopper
@trembl: it won't work on browsers that were created before the new standard rolled out.Dianndianna
@salman-a Which is why it's funny, that it does not work in Safari 5.1 (release date: 20 July, 2011), and neither in the latest Webkit nightly ( Version 5.1 (6534.50, r95673) ). I am only curious why Chrome, which is also build onto of Webkit doesn't suffer from this... Maybe because of the V8 Javascript engine?Chopper
Just tried the array example in Chrome today, the output is [1, 2] and NOT [10, 2]. Looks like the array accessor doesn't throw an error, but it works.Indemnity
C
59

Yes, freeze should work for Arrays, the behavior you are experiencing is clearly an implementation bug.

This bug might be related to the fact that array objects implement a custom [[DefineOwnProperty]] internal method (the magic that makes the length property work).

I just tested it on two implementations and it works properly (Chrome 16.0.888, and Firefox Aurora 8.02a).

About your second question, well, array objects inherit from Array.prototype which inherits from Object.prototype, for example, you can access non shadowed methods from Object.prototype directly on array objects:

['a'].hasOwnProperty('0'); // true

But this isn't related about how the typeof works, this operator will return 'object' for any object intance, regardless its kind, and for the null value, which people has always complained about.

The rest of possible return values of the typeof operator, correspond to the primitive types of the language, Number, String, Boolean, Symbol and Undefined.

Circumvallate answered 22/9, 2011 at 5:38 Comment(0)
F
12

Yes, it is applicable to arrays too.

const arr = [1,2,3,4];
Object.freeze(arr);
Object.isFrozen(arr)// true
arr.push(5) // you will get a type error
arr.pop() // you will get a type error
Friction answered 13/7, 2020 at 11:17 Comment(0)
T
-1

I ran into a situation where I wanted to use a key as a reference, and still return the key.

I wanted PERMISSION to be a simple array, not an object that has a duplicated key/value. e.g.

const PERMISSION = ['A1','A2','A3'];

used in another object like so

export const PERMS = Object.freeze({
    [PERMISSION.A1]: { ... },
    [PERMISSION.A2]: { ... },
});

for that to work (and not give undefined), I came up with this:

const arr = ["A1", "A2", "A3"];
const PERMISSION = Object.freeze(arr.reduce((ac, a) => ({ ...ac, [a]: a }), {}));

console.log(PERMISSION);
// {'A1':'A1','A2':'A2','A3':'A3'}

console.log(Object.isFrozen(PERMISSION));
// true

console.log(PERMISSION.A1)
// A1

A downside is arr is still mutable

const arr = ["A1", "A2", "A3"];
arr.push("A4");
console.log(arr);
// ['A1', 'A2', 'A3', 'A4']

console.log(arr.A1)
// undefined

one solution would be to remove arr and put the array directly in the Object.freeze

const PERMISSION = Object.freeze([
    "A1", "A2", "A3"
].reduce((ac, a) => ({ ...ac, [a]: a }), {}));

not necessarily the best solution, but a solution that makes an array immutable and fit my use case, hopefully it helps someone else who searches the same question.

Trichina answered 25/4, 2023 at 20:14 Comment(0)
E
-9

Instead of freeze, use spread operator to copy things without modifying them (if you are using a transpiler, of course):

const second = {
  ...first,
  test: 20
}
Evildoer answered 18/3, 2019 at 18:29 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.