refactor(core): remove `looseIdentical` in favor of built-in `Object.is` (#37191)
Remove `looseIdentical` implementation and instead use the ES2015 `Object.is` in its place. They behave exactly the same way except for `+0`/`-0`. `looseIdentical(+0, -0)` => `true` `Object.is(+0, -0)` => `false` Other than the difference noted above, this is not be a breaking change because: 1. `looseIdentical` is a private API 2. ES2015 is listed as a mandatory polyfill in the [browser support guide](https://angular.io/guide/browser-support#mandatory-polyfills) 3. Also note that `Ivy` already uses `Object.is` in `bindingUpdated`. PR Close #37191
This commit is contained in:
parent
92c436dd1a
commit
4456e7e4de
|
@ -6,7 +6,6 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {looseIdentical} from '../util/comparison';
|
|
||||||
import {getSymbolIterator} from '../util/symbol';
|
import {getSymbolIterator} from '../util/symbol';
|
||||||
|
|
||||||
export function devModeEqual(a: any, b: any): boolean {
|
export function devModeEqual(a: any, b: any): boolean {
|
||||||
|
@ -20,7 +19,7 @@ export function devModeEqual(a: any, b: any): boolean {
|
||||||
if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) {
|
if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return looseIdentical(a, b);
|
return Object.is(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {looseIdentical} from '../../util/comparison';
|
|
||||||
import {stringify} from '../../util/stringify';
|
import {stringify} from '../../util/stringify';
|
||||||
import {isListLikeIterable, iterateListLike} from '../change_detection_util';
|
import {isListLikeIterable, iterateListLike} from '../change_detection_util';
|
||||||
|
|
||||||
|
@ -180,7 +179,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||||
for (let index = 0; index < this.length; index++) {
|
for (let index = 0; index < this.length; index++) {
|
||||||
item = collection[index];
|
item = collection[index];
|
||||||
itemTrackBy = this._trackByFn(index, item);
|
itemTrackBy = this._trackByFn(index, item);
|
||||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
if (record === null || !Object.is(record.trackById, itemTrackBy)) {
|
||||||
record = this._mismatch(record, item, itemTrackBy, index);
|
record = this._mismatch(record, item, itemTrackBy, index);
|
||||||
mayBeDirty = true;
|
mayBeDirty = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -188,7 +187,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||||
// TODO(misko): can we limit this to duplicates only?
|
// TODO(misko): can we limit this to duplicates only?
|
||||||
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
||||||
}
|
}
|
||||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
if (!Object.is(record.item, item)) this._addIdentityChange(record, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
record = record._next;
|
record = record._next;
|
||||||
|
@ -197,7 +196,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||||
index = 0;
|
index = 0;
|
||||||
iterateListLike(collection, (item: V) => {
|
iterateListLike(collection, (item: V) => {
|
||||||
itemTrackBy = this._trackByFn(index, item);
|
itemTrackBy = this._trackByFn(index, item);
|
||||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
if (record === null || !Object.is(record.trackById, itemTrackBy)) {
|
||||||
record = this._mismatch(record, item, itemTrackBy, index);
|
record = this._mismatch(record, item, itemTrackBy, index);
|
||||||
mayBeDirty = true;
|
mayBeDirty = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -205,7 +204,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||||
// TODO(misko): can we limit this to duplicates only?
|
// TODO(misko): can we limit this to duplicates only?
|
||||||
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
||||||
}
|
}
|
||||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
if (!Object.is(record.item, item)) this._addIdentityChange(record, item);
|
||||||
}
|
}
|
||||||
record = record._next;
|
record = record._next;
|
||||||
index++;
|
index++;
|
||||||
|
@ -289,7 +288,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||||
if (record !== null) {
|
if (record !== null) {
|
||||||
// We have seen this before, we need to move it forward in the collection.
|
// We have seen this before, we need to move it forward in the collection.
|
||||||
// But first we need to check if identity changed, so we can update in view if necessary
|
// But first we need to check if identity changed, so we can update in view if necessary
|
||||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
if (!Object.is(record.item, item)) this._addIdentityChange(record, item);
|
||||||
|
|
||||||
this._moveAfter(record, previousRecord, index);
|
this._moveAfter(record, previousRecord, index);
|
||||||
} else {
|
} else {
|
||||||
|
@ -298,7 +297,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||||
if (record !== null) {
|
if (record !== null) {
|
||||||
// It is an item which we have evicted earlier: reinsert it back into the list.
|
// It is an item which we have evicted earlier: reinsert it back into the list.
|
||||||
// But first we need to check if identity changed, so we can update in view if necessary
|
// But first we need to check if identity changed, so we can update in view if necessary
|
||||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
if (!Object.is(record.item, item)) this._addIdentityChange(record, item);
|
||||||
|
|
||||||
this._reinsertAfter(record, previousRecord, index);
|
this._reinsertAfter(record, previousRecord, index);
|
||||||
} else {
|
} else {
|
||||||
|
@ -628,7 +627,7 @@ class _DuplicateItemRecordList<V> {
|
||||||
let record: IterableChangeRecord_<V>|null;
|
let record: IterableChangeRecord_<V>|null;
|
||||||
for (record = this._head; record !== null; record = record._nextDup) {
|
for (record = this._head; record !== null; record = record._nextDup) {
|
||||||
if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex!) &&
|
if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex!) &&
|
||||||
looseIdentical(record.trackById, trackById)) {
|
Object.is(record.trackById, trackById)) {
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {looseIdentical} from '../../util/comparison';
|
|
||||||
import {stringify} from '../../util/stringify';
|
import {stringify} from '../../util/stringify';
|
||||||
import {isJsObject} from '../change_detection_util';
|
import {isJsObject} from '../change_detection_util';
|
||||||
import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs';
|
import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs';
|
||||||
|
@ -229,7 +228,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
|
||||||
|
|
||||||
// Add the record or a given key to the list of changes only when the value has actually changed
|
// Add the record or a given key to the list of changes only when the value has actually changed
|
||||||
private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void {
|
private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void {
|
||||||
if (!looseIdentical(newValue, record.currentValue)) {
|
if (!Object.is(newValue, record.currentValue)) {
|
||||||
record.previousValue = record.currentValue;
|
record.previousValue = record.currentValue;
|
||||||
record.currentValue = newValue;
|
record.currentValue = newValue;
|
||||||
this._addToChanges(record);
|
this._addToChanges(record);
|
||||||
|
|
|
@ -27,7 +27,6 @@ export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn}
|
||||||
export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, BypassType as ɵBypassType, getSanitizationBypassType as ɵgetSanitizationBypassType, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue, unwrapSafeValue as ɵunwrapSafeValue} from './sanitization/bypass';
|
export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, BypassType as ɵBypassType, getSanitizationBypassType as ɵgetSanitizationBypassType, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue, unwrapSafeValue as ɵunwrapSafeValue} from './sanitization/bypass';
|
||||||
export {_sanitizeHtml as ɵ_sanitizeHtml} from './sanitization/html_sanitizer';
|
export {_sanitizeHtml as ɵ_sanitizeHtml} from './sanitization/html_sanitizer';
|
||||||
export {_sanitizeUrl as ɵ_sanitizeUrl} from './sanitization/url_sanitizer';
|
export {_sanitizeUrl as ɵ_sanitizeUrl} from './sanitization/url_sanitizer';
|
||||||
export {looseIdentical as ɵlooseIdentical,} from './util/comparison';
|
|
||||||
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
|
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
|
||||||
export {global as ɵglobal} from './util/global';
|
export {global as ɵglobal} from './util/global';
|
||||||
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
|
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
|
||||||
|
|
|
@ -8,12 +8,6 @@
|
||||||
|
|
||||||
import {areIterablesEqual, isListLikeIterable} from './iterable';
|
import {areIterablesEqual, isListLikeIterable} from './iterable';
|
||||||
|
|
||||||
|
|
||||||
// JS has NaN !== NaN
|
|
||||||
export function looseIdentical(a: any, b: any): boolean {
|
|
||||||
return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function devModeEqual(a: any, b: any): boolean {
|
export function devModeEqual(a: any, b: any): boolean {
|
||||||
const isListLikeIterableA = isListLikeIterable(a);
|
const isListLikeIterableA = isListLikeIterable(a);
|
||||||
const isListLikeIterableB = isListLikeIterable(b);
|
const isListLikeIterableB = isListLikeIterable(b);
|
||||||
|
@ -25,7 +19,7 @@ export function devModeEqual(a: any, b: any): boolean {
|
||||||
if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) {
|
if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return looseIdentical(a, b);
|
return Object.is(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {devModeEqual, WrappedValue} from '../change_detection/change_detection';
|
||||||
import {SOURCE} from '../di/injector_compatibility';
|
import {SOURCE} from '../di/injector_compatibility';
|
||||||
import {ViewEncapsulation} from '../metadata/view';
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {RendererType2} from '../render/api';
|
import {RendererType2} from '../render/api';
|
||||||
import {looseIdentical} from '../util/comparison';
|
|
||||||
import {stringify} from '../util/stringify';
|
import {stringify} from '../util/stringify';
|
||||||
|
|
||||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||||
|
@ -81,7 +80,7 @@ export function checkBinding(
|
||||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
||||||
const oldValues = view.oldValues;
|
const oldValues = view.oldValues;
|
||||||
if ((view.state & ViewState.FirstCheck) ||
|
if ((view.state & ViewState.FirstCheck) ||
|
||||||
!looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) {
|
!Object.is(oldValues[def.bindingIndex + bindingIdx], value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -941,9 +941,6 @@
|
||||||
{
|
{
|
||||||
"name": "locateHostElement"
|
"name": "locateHostElement"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "looseIdentical"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "makeMetadataCtor"
|
"name": "makeMetadataCtor"
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
import {IterableChangeRecord, IterableChanges} from '@angular/core/src/change_detection/differs/iterable_differs';
|
import {IterableChangeRecord, IterableChanges} from '@angular/core/src/change_detection/differs/iterable_differs';
|
||||||
import {KeyValueChangeRecord, KeyValueChanges} from '@angular/core/src/change_detection/differs/keyvalue_differs';
|
import {KeyValueChangeRecord, KeyValueChanges} from '@angular/core/src/change_detection/differs/keyvalue_differs';
|
||||||
|
|
||||||
import {looseIdentical} from '../../src/util/comparison';
|
|
||||||
import {stringify} from '../../src/util/stringify';
|
import {stringify} from '../../src/util/stringify';
|
||||||
|
|
||||||
export function iterableDifferToString<V>(iterableChanges: IterableChanges<V>) {
|
export function iterableDifferToString<V>(iterableChanges: IterableChanges<V>) {
|
||||||
|
@ -64,7 +63,7 @@ export function iterableChangesAsString({
|
||||||
}
|
}
|
||||||
|
|
||||||
function kvcrAsString(kvcr: KeyValueChangeRecord<string, any>) {
|
function kvcrAsString(kvcr: KeyValueChangeRecord<string, any>) {
|
||||||
return looseIdentical(kvcr.previousValue, kvcr.currentValue) ?
|
return Object.is(kvcr.previousValue, kvcr.currentValue) ?
|
||||||
stringify(kvcr.key) :
|
stringify(kvcr.key) :
|
||||||
(stringify(kvcr.key) + '[' + stringify(kvcr.previousValue) + '->' +
|
(stringify(kvcr.key) + '[' + stringify(kvcr.previousValue) + '->' +
|
||||||
stringify(kvcr.currentValue) + ']');
|
stringify(kvcr.currentValue) + ']');
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider, ɵlooseIdentical as looseIdentical} from '@angular/core';
|
import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider} from '@angular/core';
|
||||||
|
|
||||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||||
this._compareWith = fn;
|
this._compareWith = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compareWith: (o1: any, o2: any) => boolean = looseIdentical;
|
private _compareWith: (o1: any, o2: any) => boolean = Object.is;
|
||||||
|
|
||||||
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider, ɵlooseIdentical as looseIdentical} from '@angular/core';
|
import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider} from '@angular/core';
|
||||||
|
|
||||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
|
||||||
this._compareWith = fn;
|
this._compareWith = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compareWith: (o1: any, o2: any) => boolean = looseIdentical;
|
private _compareWith: (o1: any, o2: any) => boolean = Object.is;
|
||||||
|
|
||||||
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {isDevMode, ɵlooseIdentical as looseIdentical} from '@angular/core';
|
import {isDevMode} from '@angular/core';
|
||||||
|
|
||||||
import {FormArray, FormControl, FormGroup} from '../model';
|
import {FormArray, FormControl, FormGroup} from '../model';
|
||||||
import {Validators} from '../validators';
|
import {Validators} from '../validators';
|
||||||
|
@ -156,7 +156,7 @@ export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any)
|
||||||
const change = changes['model'];
|
const change = changes['model'];
|
||||||
|
|
||||||
if (change.isFirstChange()) return true;
|
if (change.isFirstChange()) return true;
|
||||||
return !looseIdentical(viewModel, change.currentValue);
|
return !Object.is(viewModel, change.currentValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BUILTIN_ACCESSORS = [
|
const BUILTIN_ACCESSORS = [
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, SimpleChanges, ɵlooseIdentical as looseIdentical} from '@angular/core';
|
import {Directive, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, SimpleChanges} from '@angular/core';
|
||||||
|
|
||||||
import {IAttributes, IAugmentedJQuery, IDirective, IInjectorService, ILinkFn, IScope, ITranscludeFunction} from '../../src/common/src/angular1';
|
import {IAttributes, IAugmentedJQuery, IDirective, IInjectorService, ILinkFn, IScope, ITranscludeFunction} from '../../src/common/src/angular1';
|
||||||
import {$SCOPE} from '../../src/common/src/constants';
|
import {$SCOPE} from '../../src/common/src/constants';
|
||||||
|
@ -206,7 +206,7 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
||||||
const newValue = this.bindingDestination[propName];
|
const newValue = this.bindingDestination[propName];
|
||||||
const oldValue = twoWayBoundLastValues[idx];
|
const oldValue = twoWayBoundLastValues[idx];
|
||||||
|
|
||||||
if (!looseIdentical(newValue, oldValue)) {
|
if (!Object.is(newValue, oldValue)) {
|
||||||
const outputName = propertyToOutputMap[propName];
|
const outputName = propertyToOutputMap[propName];
|
||||||
const eventEmitter: EventEmitter<any> = (this as any)[outputName];
|
const eventEmitter: EventEmitter<any> = (this as any)[outputName];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue