Old Guides - You are viewing the guides for Ember v4.2.0. VIEW v4.10.0
Edit Page

Disabling Prototype Extensions


By default, Ember.js will extend the prototypes of native JavaScript arrays to implement the Ember.Enumerable, Ember.MutableEnumerable, Ember.MutableArray and Ember.Array interfaces. This polyfills ECMAScript 5 array methods in browsers that do not implement them, adds convenience methods and properties to built-in arrays, and makes array mutations observable.

This is the extent to which Ember.js enhances native prototypes. We have carefully weighed the trade-offs involved with changing these prototypes, and recommend that most Ember.js developers use them. These extensions significantly reduce the amount of boilerplate code that must be typed.

However, we understand that there are cases where your Ember.js application may be embedded in an environment beyond your control. The most common scenarios are when authoring third-party JavaScript that is embedded directly in other pages, or when transitioning an application piecemeal to a more modern Ember.js architecture.

In those cases, where you can't or don't want to modify native prototypes, Ember.js allows you to completely disable the extensions described above.

To do so, simply set the EmberENV.EXTEND_PROTOTYPES flag to false:

ENV = {
  EmberENV: {
    EXTEND_PROTOTYPES: false
  }
}

You can configure which classes to include prototype extensions for in your application's configuration like so:

ENV = {
  EmberENV: {
    EXTEND_PROTOTYPES: {
      Array: false
    }
  }
}

Life Without Prototype Extension

In order for your application to behave correctly, you will need to manually extend or create the objects that the native objects were creating before.

Arrays

Native arrays will no longer implement the functionality needed to observe them. If you disable prototype extension and attempt to use native arrays with things like a template's {{#each}} helper, Ember.js will have no way to detect changes to the array and the template will not update as the underlying array changes.

You can manually coerce a native array into an array that implements the required interfaces using the convenience method Ember.A:

import { A } from '@ember/array';

let islands = ['Oahu', 'Kauai'];
islands.pushObject('Maui');
// => TypeError: Object Oahu,Kauai has no method `pushObject`

// Convert `islands` to an array that implements the
// Ember enumerable and array interfaces
A(islands);

islands.pushObject('Maui');
// => ['Oahu', 'Kauai', 'Maui'];

You can also use an "immutable update" style with tracked properties:

import { tracked } from '@glimmer/tracking';

class Ocean {
  @tracked islands = ['Oahu', 'Kauai'];

  addIsland(newIsland) {
    this.islands = this.islands.concat(newIsland);
  }
}

const ocean = new Ocean();
ocean.addIsland('Maui');
ocean.islands; // => ['Oahu', 'Kauai', 'Maui'];

Alternatively, you can use the community library tracked-built-ins to get a natively tracked version of Array, and use native Array methods with auto-tracking reactivity:

import { TrackedArray } from 'tracked-built-ins';

let islands = new TrackedArray(['Oahu', 'Kauai']);
islands.push('Maui');
// => ['Oahu', 'Kauai', 'Maui'];