Take the 2017 Ember Community Survey!

Disabling Prototype Extensions Edit Page


By default, Ember.js will extend the prototypes of native JavaScript objects in the following ways:

This is the extent to which Ember.js enhances native prototypes. We have carefully weighed the tradeoffs 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:

config/environment.js
1
2
3
4
5
ENV = {
  EmberENV: {
    EXTEND_PROTOTYPES: false
  }
}

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

config/environment.js
1
2
3
4
5
6
7
8
ENV = {
  EmberENV: {
    EXTEND_PROTOTYPES: {
      String: false,
      Array: true
    }
  }
}

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.

Additionally, if you try to set the model of an Ember.ArrayController to a plain native array, it will raise an exception since it no longer implements the Ember.Array interface.

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

1
2
3
4
5
6
7
8
9
10
var islands = ['Oahu', 'Kauai'];
islands.includes('Oahu');
//=> TypeError: Object Oahu,Kauai has no method 'includes'

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

islands.includes('Oahu');
//=> true

Strings

Strings will no longer have the convenience methods described in the Ember.String API reference. Instead, you can use the similarly-named methods of the Ember.String object and pass the string to use as the first parameter:

1
2
3
4
5
"my_cool_class".camelize();
//=> TypeError: Object my_cool_class has no method 'camelize'

Ember.String.camelize("my_cool_class");
//=> "myCoolClass"

Functions

The Object Model section of the Guides describes how to write computed properties, observers, and bindings without prototype extensions. Below you can learn about how to convert existing code to the format now encouraged.

To annotate computed properties, use the Ember.computed() method to wrap the function:

1
2
3
4
5
6
7
8
9
10
// This won't work:
fullName: function() {
  return `${this.get('firstName')} ${this.get('lastName')}`;
}.property('firstName', 'lastName')


// Instead, do this:
fullName: Ember.computed('firstName', 'lastName', function() {
  return `${this.get('firstName')} ${this.get('lastName')}`;
})

Observers are annotated using Ember.observer():

1
2
3
4
5
6
7
8
9
10
// This won't work:
fullNameDidChange: function() {
  console.log('Full name changed');
}.observes('fullName')


// Instead, do this:
fullNameDidChange: Ember.observer('fullName', function() {
  console.log('Full name changed');
})

Evented functions are annotated using Ember.on():

1
2
3
4
5
6
7
8
9
// This won't work:
doStuffWhenInserted: function() {
  /* awesome sauce */
}.on('didInsertElement');

// Instead, do this:
doStuffWhenInserted: Ember.on('didInsertElement', function() {
  /* awesome sauce */
});