1
\$\begingroup\$

I'm a Java guy in a JavaScript world, and I'm used to certain things about Java Strings that just aren't there for in JavaScript. Some of this I can accept, for example Strings are freaking everywhere for some reason.

Now I realize this could be simply implemented with return this.indexOf(needle) === 0; (or, even more JavaScripty, return this.indexOf(needle); but the flaw in that is that it will search the entire (potentially somewhat lengthy, you never know) String in its endeavor to find the index, when I really only care if its non-zero.

My current solution, which passes my testing so far, is thus. I'm open to all feedback, but I'm most interested in

  • Breaks from conventional JavaScript (the bad stuff, the smells)
  • Corner-cases this fails for
  • To a reasonable extent, style

I've also decided to tack it onto the class itself (I have to admit, I like being able to do that!) Is that bad practice?

String.prototype.startsWith = function(needle) {
    // This was dubious, if only because most times "" gets passed
    // it's by mistake... but decided since it was technically correct
    // that it should stay
    if(needle === "") return true;
    // but in all other cases, falsey values were considered false
    if(!needle) return false;

    // our mission, to find the needle in the haystack - is this tacky?
    var haystack = this;

    // primarily here to prevent AIOOBEs in the loop - is this the most
    // JavaScripty way to preempt that error?
    if(needle.length > haystack.length) {
        return false;
    }
    // I've read that 'i' is not scoped to this for loop, and that I would
    // need let instead. I must support older browsers, is there a better
    // way to do this?
    for(var i = 0; i < needle.length; i++) {
        if(haystack.charAt(i) !== needle.charAt(i)) {
            return false;
        }
    }
    return true;

}
\$\endgroup\$
1
\$\begingroup\$

I'd suggest using String.prototype.slice to implement startsWith like this:

String.prototype.startsWith = function(str) {
  return this.slice(0, str.length) === str
}

For type-checking (not needed in this example, but generally speaking) you might want to use the typeof operator:

if (typeof str !== 'string') return false

You can also use the instanceof operator if the value your dealing w/ isn't a literal or you need to check every value in the prototype chain, e.g. str instanceof String

haystack = this? Super-tacky ;p, but each to their own

Not sure if JS has an AIOOBE equivalent but 'blah'.charAt(6) === '' which tells me that check isn't needed.

And I'd suggest using Array iterators rather than for loops in the future:

var haystackStartsWithNeedle = needle.split('').
  forEach((v,i) => v === haystack[i]).find(v => v)

You might want to check babeljs.io. It transpiles new & flavoured JS to code most browsers can use.

\$\endgroup\$
4
  • \$\begingroup\$ Modifying native prototypes is generally a bad idea, especially if you're overwriting existing methods or prototypes which could be confused w/ others (Promise vs Promise-like object for example) \$\endgroup\$ – Ashton Six Feb 3 '16 at 12:59
  • \$\begingroup\$ About the AIOOBE you're right that it wouldn't error out, and would still work correctly - but if my needle is bigger than my haystack, I know the result is false. And isn't needle and haystack the common terms to use when searching? I thought it was - if there's better names, I'm open to that. With regards to modifying native prototypes, is this just because if everyone modifies native prototypes, you lose assurance that you're dealing with the original code? I'm not overwriting an existing method (otherwise I wouldn't write one!) and I'm not sure what you mean about "confused w/ others". \$\endgroup\$ – corsiKa Feb 4 '16 at 16:14
  • \$\begingroup\$ Yes, you know the result is false - but the check only adds complexity & optimization is best left to the optimizer (unless dramatic or big O related). You can totally use needle & haystack, just seems kinda weird to me - personal taste. Modifying native prototypes can go wrong in a few different situations - particularly name collision when multiple people working on a codebase are creating custom methods. W/ promises specifically there's a lot of different implementations & b/c JS is dynamically typed you can't be certain which objects have which methods until runtime. \$\endgroup\$ – Ashton Six Feb 4 '16 at 20:04
  • \$\begingroup\$ Example I just found: b/c MooTools (a popular library) modifies the Array prototype we've not been able to implement Array.prototype.contains in the language itself - esdiscuss.org/topic/… \$\endgroup\$ – Ashton Six Feb 7 '16 at 16:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.