Skip to content
Permalink
Browse files

refactor(*): faster check for a string starting with a substring

Thanks to @spamdaemon for the original PR to make this improvement.

In naive tests on Chrome I got the following results:

```
             Matches   Misses
indexOf      33ms      1494ms
lastIndexOf  11ms      11ms
```

Closes #3711
  • Loading branch information
petebacondarwin committed May 24, 2016
1 parent 7f4b356 commit a4e4feed909408ff837ac328371d0fad6ca49e9d
Showing with 21 additions and 18 deletions.
  1. +20 −17 src/ng/location.js
  2. +1 −1 src/ngSanitize/sanitize.js
@@ -48,17 +48,20 @@ function parseAppUrl(relativeUrl, locationObj) {
}
}

function startsWith(haystack, needle) {
return haystack.lastIndexOf(needle, 0) === 0;
}

/**
*
* @param {string} begin
* @param {string} whole
* @returns {string} returns text from whole after begin or undefined if it does not begin with
* expected string.
* @param {string} base
* @param {string} url
* @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
* the expected string.
*/
function beginsWith(begin, whole) {
if (whole.indexOf(begin) === 0) {
return whole.substr(begin.length);
function stripBaseUrl(base, url) {
if (startsWith(url, base)) {
return url.substr(base.length);
}
}

@@ -104,7 +107,7 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
* @private
*/
this.$$parse = function(url) {
var pathUrl = beginsWith(appBaseNoFile, url);
var pathUrl = stripBaseUrl(appBaseNoFile, url);
if (!isString(pathUrl)) {
throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
appBaseNoFile);
@@ -141,14 +144,14 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
var appUrl, prevAppUrl;
var rewrittenUrl;

if (isDefined(appUrl = beginsWith(appBase, url))) {
if (isDefined(appUrl = stripBaseUrl(appBase, url))) {
prevAppUrl = appUrl;
if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
if (isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) {
rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl);
} else {
rewrittenUrl = appBase + prevAppUrl;
}
} else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
} else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) {
rewrittenUrl = appBaseNoFile + appUrl;
} else if (appBaseNoFile === url + '/') {
rewrittenUrl = appBaseNoFile;
@@ -182,14 +185,14 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
* @private
*/
this.$$parse = function(url) {
var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url);
var withoutHashUrl;

if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {

// The rest of the url starts with a hash so we have
// got either a hashbang path or a plain hash fragment
withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl);
if (isUndefined(withoutHashUrl)) {
// There was no hashbang prefix so we just have a hash fragment
withoutHashUrl = withoutBaseUrl;
@@ -237,7 +240,7 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
var firstPathSegmentMatch;

//Get the relative path from the input URL.
if (url.indexOf(base) === 0) {
if (startsWith(url, base)) {
url = url.replace(base, '');
}

@@ -300,7 +303,7 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {

if (appBase === stripHash(url)) {
rewrittenUrl = url;
} else if ((appUrl = beginsWith(appBaseNoFile, url))) {
} else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) {
rewrittenUrl = appBase + hashPrefix + appUrl;
} else if (appBaseNoFile === url + '/') {
rewrittenUrl = appBaseNoFile;
@@ -909,7 +912,7 @@ function $LocationProvider() {
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl, newState) {

if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
if (isUndefined(stripBaseUrl(appBaseNoFile, newUrl))) {
// If we are navigating outside of the app then force a reload
$window.location.href = newUrl;
return;
@@ -489,7 +489,7 @@ function stripCustomNsAttrs(node) {
for (var i = 0, l = attrs.length; i < l; i++) {
var attrNode = attrs[i];
var attrName = attrNode.name.toLowerCase();
if (attrName === 'xmlns:ns1' || attrName.indexOf('ns1:') === 0) {
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
node.removeAttributeNode(attrNode);
i--;
l--;

0 comments on commit a4e4fee

Please sign in to comment.
You can’t perform that action at this time.