diff --git a/packages/ember-runtime/lib/mixins/array.js b/packages/ember-runtime/lib/mixins/array.js index c46cbe100e7..08024c7ea7d 100644 --- a/packages/ember-runtime/lib/mixins/array.js +++ b/packages/ember-runtime/lib/mixins/array.js @@ -7,6 +7,8 @@ // HELPERS // import { symbol } from 'ember-utils'; + +import { peekMeta } from 'ember-metal'; import Ember, { // ES6TODO: Ember.A get, computed, @@ -135,20 +137,21 @@ export function arrayContentDidChange(array, startIdx, removeAmt, addAmt) { sendEvent(array, '@array:change', [array, startIdx, removeAmt, addAmt]); - let length = get(array, 'length'); - let cachedFirst = cacheFor(array, 'firstObject'); - let cachedLast = cacheFor(array, 'lastObject'); - - if (objectAt(array, 0) !== cachedFirst) { - propertyWillChange(array, 'firstObject'); - propertyDidChange(array, 'firstObject'); - } + let meta = peekMeta(array); + let cache = meta && meta.readableCache(); - if (objectAt(array, length - 1) !== cachedLast) { - propertyWillChange(array, 'lastObject'); - propertyDidChange(array, 'lastObject'); + if (cache) { + if (cache.firstObject !== undefined && + objectAt(array, 0) !== cacheFor.get(cache, 'firstObject')) { + propertyWillChange(array, 'firstObject'); + propertyDidChange(array, 'firstObject'); + } + if (cache.lastObject !== undefined && + objectAt(array, get(array, 'length') - 1) !== cacheFor.get(cache, 'lastObject')) { + propertyWillChange(array, 'lastObject'); + propertyDidChange(array, 'lastObject'); + } } - return array; } diff --git a/packages/ember-runtime/tests/suites/mutable_array/replace.js b/packages/ember-runtime/tests/suites/mutable_array/replace.js index 051736f5795..b9f17b08484 100644 --- a/packages/ember-runtime/tests/suites/mutable_array/replace.js +++ b/packages/ember-runtime/tests/suites/mutable_array/replace.js @@ -22,6 +22,23 @@ suite.test('[].replace(0,0,\'X\') => [\'X\'] + notify', function() { equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once'); }); +suite.test('[].replace(0,0,"X") => ["X"] + avoid calling objectAt and notifying fistObject/lastObject when not in cache', function() { + var obj, exp, observer; + var called = 0; + exp = this.newFixture(1); + obj = this.newObject([]); + obj.objectAt = function() { + called++; + }; + observer = this.newObserver(obj, 'firstObject', 'lastObject'); + + obj.replace(0, 0, exp); + + equal(called, 0, 'should NOT have called objectAt upon replace when firstObject/lastObject are not cached'); + equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject since not cached'); + equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject since not cached'); +}); + suite.test('[A,B,C,D].replace(1,2,X) => [A,X,D] + notify', function() { let before = this.newFixture(4); let replace = this.newFixture(1);