Initializing arrays in Ember


#1

I just had a hard time finding how to define an array with Ember’s new ES6 module imports. So here it is for others who might have the same question:

import { A } from '@ember/array';
...
this.set('someVar', A());             // empty Emberish array
this.set('someOtherVar', A([1,2,3]));

As explained in the doc here

A() is not needed if EmberENV.EXTEND_PROTOTYPES is true (the default value). However, it is recommended that you use A() when creating addons for ember or when you can not guarantee that EmberENV.EXTEND_PROTOTYPES will be true.


#2

I would also add that you can often stick to patterns that don’t require Ember.Array at all.

What Ember.Array gives you is ember-specific methods for mutations that cause those mutations to be easily observed (by templates or computed properties or whatever else might need to update).

But if instead of mutating your array, you just replace it, then you can use a plain array.

For example:

// before
this.someArray.pushObject(thing);

//after
this.set('someArray', [...this.someArray, thing]);

What I like about this technique is that you only need to know one Ember-specific thing, which is set, and you already needed to know set anyway. You can avoid dealing with the entire API of Ember.Enumerable, which grew up in a time before Ember was good at optimizing whole-array updates.


#3

Yeah just keep in mind that:

  1. It’s not performant to keep creating entirely new arrays just to push items to it, especially when the original array someArray has a lot of items
  2. I’m not sure if Ember observers actually perform optimizations when pushObject(s)() is used, but I assume you lose that when setting an entirely new array on someArray

#4

This is true, but it requires quite a lot more items than people often intuitively assume before it matters. And when you get to the point where you want to optimize out the array copy, you can still avoid Ember.A, at the cost of learning one new Ember API (notifyPropertyChange):

this.someArray.push(thing);
this.notifyPropertyChange('someArray');

Pretty much every realistic case I can think of is just as optimized. {{#each}} in templates works the same whether you replace the array or not, and has the same optimizations. A computed property like

computed('someArray.@each.foo', function(){ 
  expensiveWork()
})

costs the same either way, because it will invalidate the same amount and it will do the same work in either case.

It’s hard to intuit performance without measuring. If you measure, I’m confident you will see that plain array is as fast or faster than Ember.Array in almost every scenario. Browsers are optimized for native arrays.


#5

I prefer to make arrays immutable as what you’ve indicated in your example. But just to add with the performance issue (if I’m right), the component hooks will fire when you recreate those arrays. Depending on how the app has been structured, this could potentially re-fire all hooks across the component chain that’s receiving the array prop. If those hooks does some intensive operations, that might be your performance issue.