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

fix(Angular.js): fix isArrayLike for extends #4855

Closed

Conversation

jackcviers
Copy link
Contributor

Add spec for isArrayLike
Change element detection to use Node.hasChildren
Add Arguments detection
Prepares a single boolean return instead of several if return boolean
statements

  1. Convert if-return boolean statements to single boolean return.
  2. Assign boolean conditionals to break up the long boolean return
    and avoid repeated calls.
  3. Declare variables at the beginning of function to avoid hoisting errors.
    Break long lines into indented and aligned expressions.

#4751

@petebacondarwin
Copy link
Contributor

What is the performance impact of doing all the calculations up front rather than short-circuiting quick cases?

@petebacondarwin
Copy link
Contributor

+1 for the unit test.

@jackcviers
Copy link
Contributor Author

@petebacondarwin None of the cases involved require traversal. The return and boolean statements are set up to short-circuit with ORs as much as possible. If you'd like me to create a performance spec I can do that.

Interestingly, IE8 on win 7 is returning a bunch of

TypeError: Object doesn't support this property or method 

errors in the Travis Build. I can download an IE8 vm and investigate, but if you know if the build was failing before this pr, that would be immensely helpful. I didn't get any errors using --browsers Chrome,Firefox. I dual boot Win 8.1, so I can test in IE10 on that side. If it is related to this change, I suspect it is a missing native forEach on the object in question.

@petebacondarwin
Copy link
Contributor

You call all these functions before you begin to short-circuit:

isDefined(obj)
isArray(obj)
isString(obj)
isObject(obj)

Plus these may also get called:

obj.hasChildNodes()
obj.hasOwnProperty('length')
obj.hasOwnProperty('callee')

@petebacondarwin
Copy link
Contributor

BTW IE8 doesn't always have hasOwnProperty - http://stackoverflow.com/a/8159434/287070

@jackcviers
Copy link
Contributor Author

No problem, I can polyfill.
On Nov 9, 2013 9:54 AM, "Pete Bacon Darwin" notifications@github.com
wrote:

BTW IE8 doesn't always have hasOwnProperty -
http://stackoverflow.com/a/8159434/287070


Reply to this email directly or view it on GitHubhttps://github.com//pull/4855#issuecomment-28129990
.

@jackcviers
Copy link
Contributor Author

Haven't worked in it in over two years.
On Nov 9, 2013 10:04 AM, "Jack Viers" jackcviers@gmail.com wrote:

No problem, I can polyfill.
On Nov 9, 2013 9:54 AM, "Pete Bacon Darwin" notifications@github.com
wrote:

BTW IE8 doesn't always have hasOwnProperty -
http://stackoverflow.com/a/8159434/287070


Reply to this email directly or view it on GitHubhttps://github.com//pull/4855#issuecomment-28129990
.

@jackcviers
Copy link
Contributor Author

http://jsperf.com/angular-isarraylike-performance

The current implementation is faster. But it doesn't identify arguments objects correctly.

Want me to plug an early return into that setup as well?

@jackcviers
Copy link
Contributor Author

Added the early exit impl test.
http://jsperf.com/angular-isarraylike-performance

I am seeing that the new impl is 59% slower than the current broken implementation, and the early exit version of the new impl is 44% slower than the current broken implementation.

I don't know that the project preferences are, but the removal of the if-return boolean doesn't seem necessary for a 15% increase in correct performance.

I am adding the hasOwnProperty polyfill, assuming there isn't one already in exsitence.

) ||
isSomeOtherObj
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like these long expressions are actually much harder to mentally parse/understand, and you also lose the locality of reference benefit (everything is evaluated at the head, but used a full page down). And these may be a big part of the performance degrading.

@jackcviers
Copy link
Contributor Author

@caitp @petebacondarwin I have added a hasOwnProperty(obj, propertyName) that delegates to the native one if the object has the native method or calls the Object.prototype.hasOwnProperty in the scope of the object passed with the propertyName if it doesn't. Should fix the IE<9 issue.

I have also reworded the specifications and specification local variable names to make them clearer to understand.

@caitp
Copy link
Contributor

caitp commented Nov 11, 2013

@jackcviers: You can use Object.prototype.hasOwnProperty.call() instead of obj.hasOwnProperty() to work around issues with IE < 9 host objects. No polyfill is necessary

@jackcviers
Copy link
Contributor Author

@caitp
| @jackcviers: You can use Object.prototype.hasOwnProperty.call() instead of obj.hasOwnProperty() to work around issues with IE < 9 host objects. No polyfill is necessary.

Yeah, I'm testing to see if obj.hasOwnProperty is defined, and if not calling Object.prototype.hasOwnProperty.apply(obj, [propertyName]). However, Travis indicates massive failures. @petebacondarwin what do I need to add to run my local thru IE10? I get missing plugin notifications when trying --browsers Chrome,IE_10 I have local ie vms as well and there is an available karma ie vms plugin, should I include that?

@caitp
Copy link
Contributor

caitp commented Nov 11, 2013

http://jsfiddle.net/tUNnY/ — Try it in IE10, Chrome, Safari, Firefox, etc.

Caitlin Potter
snowball@defpixel.com

On Nov 10, 2013, at 9:49 PM, Jack Viers notifications@github.com wrote:

@caitp
| @jackcviers: You can use Object.prototype.hasOwnProperty.call() instead of obj.hasOwnProperty() to work around issues with IE < 9 host objects. No polyfill is necessary.

Yeah, I'm testing to see if obj.hasOwnProperty is defined, and if not calling Object.prototype.hasOwnProperty.apply(obj, [propertyName]). However, Travis indicates massive failures. @petebacondarwin what do I need to add to run my local thru IE10? I get missing plugin notifications when trying --browsers Chrome,IE_10


Reply to this email directly or view it on GitHub.

@jackcviers
Copy link
Contributor Author

@caitp Yeah, that still fails on Travis. Need to get a local hook into Karma for these tests for IE vms so that I can see what is going on. I have tried exporting IE_BIN. Local when I try with grunt test --browsers Chrome,IE (or IE_10) that it is missing a plugin. Anyone on the project that can point me in the correct direction, help is welcome.

@caitp
Copy link
Contributor

caitp commented Nov 11, 2013

@jackcviers you need the karma-ie-launcher module installed, it isn't an angular.js package dependency so you have to manually install it.

npm install karma-ie-launcher

@caitp
Copy link
Contributor

caitp commented Nov 11, 2013

My variation on your patch is passing CI: https://travis-ci.org/caitp/angular.js/builds/13790008 --- Feel free to review it.

It requires a change to angular.forEach because isArrayLike ends up returning false for Host objects where it otherwise wouldn't, and Host objects don't provide hasOwnProperty on IE8. Might be able to fix it so that it doesn't return true for non-POJO objects with length === 0 property.

diff

@jackcviers
Copy link
Contributor Author

I think that's fine, @caitp. I will pull your change over tomorrow. Thanks
for your help.
On Nov 10, 2013 10:56 PM, "Caitlin Potter" notifications@github.com wrote:

My variation on your patch is passing CI:
https://travis-ci.org/caitp/angular.js/builds/13790008 --- Feel free to
review it.

It requires a change to angular.forEach because isArrayLike ends up
returning false for Host objects where it otherwise wouldn't, and Host
objects don't provide hasOwnProperty on IE8.


Reply to this email directly or view it on GitHubhttps://github.com//pull/4855#issuecomment-28174075
.

Add spec for isArrayLike
Add Arguments detection
Convert NodeList detection to use hasChildren
Prepares a single boolean return instead of several if return boolean
statements

1. Convert if-return boolean statements to single boolean return.

2. Assign boolean conditionals to break up the long boolean return
and avoid repeated calls.

3. Declare variables at the beginning of function to avoid hoisting errors.
Break long lines into indented and aligned expressions.
@jackcviers
Copy link
Contributor Author

@caitp @petebacondarwin This now passes on Travis. @caitp thanks for your help. The version you gave me didn't pass on my local for some reason. This one does. Please let me know if you have any questions.

@caitp
Copy link
Contributor

caitp commented Dec 4, 2013

@jackcviers any chance we could get this patch cleaned up a bit? I was a bit surprised to learn this morning that it hasn't been merged yet. Lets make it merge-able!

@jackcviers
Copy link
Contributor Author

Sure, I am on it.
On Dec 4, 2013 9:13 AM, "Caitlin Potter" notifications@github.com wrote:

@jackcviers https://github.com/jackcviers any chance we could get this
patch cleaned up a bit? I was a bit surprised to learn this morning that it
hasn't been merged yet. Lets make it merge-able!


Reply to this email directly or view it on GitHubhttps://github.com//pull/4855#issuecomment-29812353
.

@caitp
Copy link
Contributor

caitp commented Dec 4, 2013

Actually in hindsight, this probably isn't what I was surprised to learn today, it seems that we are calling hasOwnProperty on an object which does not necessarily implement it somewhere in the core code, although it's difficult to know that for sure without seeing the code causing the error.

But just the same, if this does offer some improvements, it would be nice to squish this patch down as small as possible and try to get some decent performance out of it.

@jeffbcross jeffbcross force-pushed the master branch 2 times, most recently from cad9560 to f294244 Compare October 2, 2014 22:08
@petebacondarwin petebacondarwin modified the milestones: Backlog, 1.4.x Sep 14, 2015
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this pull request Oct 23, 2015
petebacondarwin added a commit to petebacondarwin/angular.js that referenced this pull request Oct 25, 2015
petebacondarwin added a commit to petebacondarwin/angular.js that referenced this pull request Oct 25, 2015
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this pull request Oct 25, 2015
petebacondarwin pushed a commit that referenced this pull request Oct 26, 2015
kkirsche added a commit to kkirsche/kibana that referenced this pull request Feb 21, 2016
<a name="1.4.9"></a>
# 1.4.9 implicit-superannuation (2016-01-21)

## Bug Fixes

- **Animation**
  - ensure that animate promises resolve when the document is hidden
  ([9a60408c](angular/angular.js@9a60408))
  - do not trigger animations if the document is hidden
  ([09f6061a](angular/angular.js@09f6061),
   [elastic#12842](angular/angular.js#12842), [elastic#13776](angular/angular.js#13776))
  - only copy over the animation options once
  ([2fc954d3](angular/angular.js@2fc954d),
   [elastic#13722](angular/angular.js#13722), [elastic#13578](angular/angular.js#13578))
  - allow event listeners on document in IE
  ([5ba4419e](angular/angular.js@5ba4419),
   [elastic#13548](angular/angular.js#13548), [elastic#13696](angular/angular.js#13696))
  - allow removing classes that are added by a running animation
  ([6c4581fc](angular/angular.js@6c4581f),
   [elastic#13339](angular/angular.js#13339), [elastic#13380](angular/angular.js#13380), [elastic#13414](angular/angular.js#13414), [elastic#13472](angular/angular.js#13472), [elastic#13678](angular/angular.js#13678))
  - do not use `event.timeStamp` anymore for time tracking
  ([620a20d1](angular/angular.js@620a20d),
   [elastic#13494](angular/angular.js#13494), [elastic#13495](angular/angular.js#13495))
  - ignore children without animation data when closing them
  ([be01cebf](angular/angular.js@be01ceb),
   [elastic#11992](angular/angular.js#11992), [elastic#13424](angular/angular.js#13424))
  - do not alter the provided options data
  ([7a81e6fe](angular/angular.js@7a81e6f),
   [elastic#13040](angular/angular.js#13040), [elastic#13175](angular/angular.js#13175))
  - correctly handle `$animate.pin()` host elements
  ([a985adfd](angular/angular.js@a985adf),
   [elastic#13783](angular/angular.js#13783))
  - allow animations when pinned element is parent element
  ([4cb8ac61](angular/angular.js@4cb8ac6),
   [elastic#13466](angular/angular.js#13466))
  - allow enabled children to animate on disabled parents
  ([6d85f24e](angular/angular.js@6d85f24),
   [elastic#13179](angular/angular.js#13179), [elastic#13695](angular/angular.js#13695))
  - correctly access `minErr`
  ([0c1b54f0](angular/angular.js@0c1b54f))
  - ensure animate runner is the same with and without animations
  ([937942f5](angular/angular.js@937942f),
   [elastic#13205](angular/angular.js#13205), [elastic#13347](angular/angular.js#13347))
  - remove animation end event listeners on close
  ([d9157849](angular/angular.js@d915784),
   [elastic#13672](angular/angular.js#13672))
  - consider options.delay value for closing timeout
  ([592bf516](angular/angular.js@592bf51),
   [elastic#13355](angular/angular.js#13355), [elastic#13363](angular/angular.js#13363))
- **$controller:** allow identifiers containing `$`
  ([2563ff7b](angular/angular.js@2563ff7),
   [elastic#13736](angular/angular.js#13736))
- **$http:** throw if url passed is not a string
  ([c5bf9dae](angular/angular.js@c5bf9da),
   [elastic#12925](angular/angular.js#12925), [elastic#13444](angular/angular.js#13444))
- **$parse:** handle interceptors with `undefined` expressions
  ([7bb2414b](angular/angular.js@7bb2414))
- **$resource:** don't allow using promises as `timeout` and log a warning
  ([47486524](angular/angular.js@4748652))
- **formatNumber:** cope with large and small number corner cases
  ([9c49eb13](angular/angular.js@9c49eb1),
   [elastic#13394](angular/angular.js#13394), [elastic#8674](angular/angular.js#8674), [elastic#12709](angular/angular.js#12709), [elastic#8705](angular/angular.js#8705), [elastic#12707](angular/angular.js#12707), [elastic#10246](angular/angular.js#10246), [elastic#10252](angular/angular.js#10252))
- **input:**
  - fix URL validation being too strict
  ([6610ae81](angular/angular.js@6610ae8),
   [elastic#13528](angular/angular.js#13528), [elastic#13544](angular/angular.js#13544))
  - add missing chars to URL validation regex
  ([2995b54a](angular/angular.js@2995b54),
   [elastic#13379](angular/angular.js#13379), [elastic#13460](angular/angular.js#13460))
- **isArrayLike:** recognize empty instances of an Array subclass
  ([323f9ab7](angular/angular.js@323f9ab),
   [elastic#13560](angular/angular.js#13560), [elastic#13708](angular/angular.js#13708))
- **ngInclude:** do not compile template if original scope is destroyed
  ([9590bcf0](angular/angular.js@9590bcf))
- **ngOptions:**
  - don't skip `optgroup` elements with `value === ''`
  ([85e392f3](angular/angular.js@85e392f),
   [elastic#13487](angular/angular.js#13487), [elastic#13489](angular/angular.js#13489))
  - don't `$dirty` multiple select after compilation
  ([f163c905](angular/angular.js@f163c90),
   [elastic#13211](angular/angular.js#13211), [elastic#13326](angular/angular.js#13326))
- **select:** re-define `ngModelCtrl.$render` in the `select` directive's postLink function
  ([529b2507](angular/angular.js@529b250),
   [elastic#13583](angular/angular.js#13583), [elastic#13583](angular/angular.js#13583), [elastic#13663](angular/angular.js#13663))

## Minor Features

- **ngLocale:** add support for standalone months
  ([54c4041e](angular/angular.js@54c4041),
   [elastic#3744](angular/angular.js#3744), [elastic#10247](angular/angular.js#10247), [elastic#12642](angular/angular.js#12642), [elastic#12844](angular/angular.js#12844))
- **ngMock:** add support for `$animate.closeAndFlush()`
  ([512c0811](angular/angular.js@512c081))

## Performance Improvements

- **ngAnimate:** speed up `areAnimationsAllowed` check
  ([2d3303dd](angular/angular.js@2d3303d))

## Breaking Changes

While we do not deem the following to be a real breaking change we are highlighting it here in the
changelog to ensure that it does not surprise anyone.

- **$resource:** due to [47486524](angular/angular.js@4748652),

**Possible breaking change** for users who updated their code to provide a `timeout`
promise for a `$resource` request in version v1.4.8.

Up to v1.4.7 (included), using a promise as a timeout in `$resource`, would silently
fail (i.e. have no effect).

In v1.4.8, using a promise as timeout would have the (buggy) behaviour described
in angular/angular.js#12657 (comment).
(I.e. it will work as expected for the first time you resolve the promise and will
cancel all subsequent requests after that - one has to re-create the resource
class. This was not documented.)

With this change, using a promise as timeout in v1.4.9 onwards is not allowed.
It will log a warning and ignore the timeout value.

If you need support for cancellable `$resource` actions, you should upgrade to
version 1.5 or higher.

<a name="1.4.8"></a>
# 1.4.8 ice-manipulation (2015-11-19)

## Bug Fixes

- **$animate:** ensure leave animation calls `close` callback
  ([6bd6dbff](angular/angular.js@6bd6dbf),
   [elastic#12278](angular/angular.js#12278), [elastic#12096](angular/angular.js#12096), [elastic#13054](angular/angular.js#13054))
- **$cacheFactory:** check key exists before decreasing cache size count
  ([2a5a52a7](angular/angular.js@2a5a52a),
   [elastic#12321](angular/angular.js#12321), [elastic#12329](angular/angular.js#12329))
- **$compile:**
  - bind all directive controllers correctly when using `bindToController`
  ([5d8861fb](angular/angular.js@5d8861f),
   [elastic#11343](angular/angular.js#11343), [elastic#11345](angular/angular.js#11345))
  - evaluate against the correct scope with bindToController on new scope
  ([b9f7c453](angular/angular.js@b9f7c45),
   [elastic#13021](angular/angular.js#13021), [elastic#13025](angular/angular.js#13025))
  - fix scoping of transclusion directives inside replace directive
  ([74da0340](angular/angular.js@74da034),
   [elastic#12975](angular/angular.js#12975), [elastic#12936](angular/angular.js#12936), [elastic#13244](angular/angular.js#13244))
- **$http:** apply `transformResponse` even when `data` is empty
  ([c6909464](angular/angular.js@c690946),
   [elastic#12976](angular/angular.js#12976), [elastic#12979](angular/angular.js#12979))
- **$location:** ensure `$locationChangeSuccess` fires even if URL ends with `#`
  ([6f8ddb6d](angular/angular.js@6f8ddb6),
   [elastic#12175](angular/angular.js#12175), [elastic#13251](angular/angular.js#13251))
- **$parse:** evaluate once simple expressions only once
  ([e4036824](angular/angular.js@e403682),
   [elastic#12983](angular/angular.js#12983), [elastic#13002](angular/angular.js#13002))
- **$resource:** allow XHR request to be cancelled via a timeout promise
  ([7170f9d9](angular/angular.js@7170f9d),
   [elastic#12657](angular/angular.js#12657), [elastic#12675](angular/angular.js#12675), [elastic#10890](angular/angular.js#10890), [elastic#9332](angular/angular.js#9332))
- **$rootScope:** prevent IE9 memory leak when destroying scopes
  ([87b0055c](angular/angular.js@87b0055),
   [elastic#10706](angular/angular.js#10706), [elastic#11786](angular/angular.js#11786))
- **Angular.js:** fix `isArrayLike` for unusual cases
  ([70edec94](angular/angular.js@70edec9),
   [elastic#10186](angular/angular.js#10186), [elastic#8000](angular/angular.js#8000), [elastic#4855](angular/angular.js#4855), [elastic#4751](angular/angular.js#4751), [elastic#10272](angular/angular.js#10272))
- **isArrayLike:** handle jQuery objects of length 0
  ([d3da55c4](angular/angular.js@d3da55c))
- **jqLite:**
  - deregister special `mouseenter` / `mouseleave` events correctly
  ([22f66025](angular/angular.js@22f6602),
   [elastic#12795](angular/angular.js#12795), [elastic#12799](angular/angular.js#12799))
  - ensure mouseenter works with svg elements on IE
  ([c1f34e8e](angular/angular.js@c1f34e8),
   [elastic#10259](angular/angular.js#10259), [elastic#10276](angular/angular.js#10276))
- **limitTo:** start at 0 if `begin` is negative and exceeds input length
  ([4fc40bc9](angular/angular.js@4fc40bc),
   [elastic#12775](angular/angular.js#12775), [elastic#12781](angular/angular.js#12781))
- **merge:**
  - ensure that jqlite->jqlite and DOM->DOM
  ([2f8db1bf](angular/angular.js@2f8db1b))
  - clone elements instead of treating them like simple objects
  ([838cf4be](angular/angular.js@838cf4b),
   [elastic#12286](angular/angular.js#12286))
- **ngAria:** don't add tabindex to radio and checkbox inputs
  ([59f1f4e1](angular/angular.js@59f1f4e),
   [elastic#12492](angular/angular.js#12492), [elastic#13095](angular/angular.js#13095))
- **ngInput:** change URL_REGEXP to better match RFC3987
  ([cb51116d](angular/angular.js@cb51116),
   [elastic#11341](angular/angular.js#11341), [elastic#11381](angular/angular.js#11381))
- **ngMock:** reset cache before every test
  ([91b7cd9b](angular/angular.js@91b7cd9),
   [elastic#13013](angular/angular.js#13013))
- **ngOptions:**
  - skip comments and empty options when looking for options
  ([0f58334b](angular/angular.js@0f58334),
   [elastic#12190](angular/angular.js#12190), [elastic#13029](angular/angular.js#13029), [elastic#13033](angular/angular.js#13033))
  - override select option registration to allow compilation of empty option
  ([7b2ecf42](angular/angular.js@7b2ecf4),
   [elastic#11685](angular/angular.js#11685), [elastic#12972](angular/angular.js#12972), [elastic#12968](angular/angular.js#12968), [elastic#13012](angular/angular.js#13012))

## Performance Improvements

- **$compile:** use static jquery data method to avoid creating new instances
  ([55ad192e](angular/angular.js@55ad192))
- **copy:**
  - avoid regex in `isTypedArray`
  ([19fab4a1](angular/angular.js@19fab4a))
  - only validate/clear if the user specifies a destination
  ([d1293540](angular/angular.js@d129354),
   [elastic#12068](angular/angular.js#12068))
- **merge:** remove unnecessary wrapping of jqLite element
  ([ce6a96b0](angular/angular.js@ce6a96b),
   [elastic#13236](angular/angular.js#13236))

## Breaking Changes

Signed-off-by: Kevin Kirsche <Kev.Kirsche@gmail.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants