Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 28661d1

Browse files
HeberLZcaitp
authored andcommitted
fix($parse): support dirty-checking objects with null prototype
Objects created with `Object.create(null);` do not have a `valueOf` method unless they supply one themselves. To accomodate these, Object.prototype.valueOf is used when the type of the value is "object", and the `valueOf` property is not a function (E.G. it's not in the object at all). Closes #9568
1 parent 7f4d24c commit 28661d1

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

src/ng/parse.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,12 @@ function getterFn(path, options, fullExp) {
946946
return fn;
947947
}
948948

949+
var objectValueOf = Object.prototype.valueOf;
950+
951+
function getValueOf(value) {
952+
return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
953+
}
954+
949955
///////////////////////////////////
950956

951957
/**
@@ -1092,7 +1098,7 @@ function $ParseProvider() {
10921098
// attempt to convert the value to a primitive type
10931099
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
10941100
// be cheaply dirty-checked
1095-
newValue = newValue.valueOf();
1101+
newValue = getValueOf(newValue);
10961102

10971103
if (typeof newValue === 'object') {
10981104
// objects/arrays are not supported - deep-watching them would be too expensive
@@ -1119,7 +1125,7 @@ function $ParseProvider() {
11191125
var newInputValue = inputExpressions(scope);
11201126
if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {
11211127
lastResult = parsedExpression(scope);
1122-
oldInputValue = newInputValue && newInputValue.valueOf();
1128+
oldInputValue = newInputValue && getValueOf(newInputValue);
11231129
}
11241130
return lastResult;
11251131
}, listener, objectEquality);
@@ -1136,7 +1142,7 @@ function $ParseProvider() {
11361142
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
11371143
var newInputValue = inputExpressions[i](scope);
11381144
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
1139-
oldInputValueOfValues[i] = newInputValue && newInputValue.valueOf();
1145+
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
11401146
}
11411147
}
11421148

test/ng/parseSpec.js

+26
Original file line numberDiff line numberDiff line change
@@ -1482,6 +1482,32 @@ describe('parser', function() {
14821482
expect(watcherCalls).toBe(1);
14831483
}));
14841484

1485+
it("should always reevaluate filters with non-primitive input created with null prototype",
1486+
inject(function($parse) {
1487+
var filterCalls = 0;
1488+
$filterProvider.register('foo', valueFn(function(input) {
1489+
filterCalls++;
1490+
return input;
1491+
}));
1492+
1493+
var parsed = $parse('obj | foo');
1494+
var obj = scope.obj = Object.create(null);
1495+
1496+
var watcherCalls = 0;
1497+
scope.$watch(parsed, function(input) {
1498+
expect(input).toBe(obj);
1499+
watcherCalls++;
1500+
});
1501+
1502+
scope.$digest();
1503+
expect(filterCalls).toBe(2);
1504+
expect(watcherCalls).toBe(1);
1505+
1506+
scope.$digest();
1507+
expect(filterCalls).toBe(3);
1508+
expect(watcherCalls).toBe(1);
1509+
}));
1510+
14851511
it("should not reevaluate filters with non-primitive input that does support valueOf()",
14861512
inject(function($parse) {
14871513
var filterCalls = 0;

0 commit comments

Comments
 (0)