{"id":942,"date":"2012-10-27T13:29:29","date_gmt":"2012-10-27T13:29:29","guid":{"rendered":"http:\/\/www.sitekickr.com\/blog\/?p=942"},"modified":"2012-11-07T22:48:44","modified_gmt":"2012-11-07T22:48:44","slug":"javascript-strings-prototype","status":"publish","type":"post","link":"https:\/\/www.sitekickr.com\/blog\/javascript-strings-prototype\/","title":{"rendered":"Fun with JavaScript Strings and Prototype"},"content":{"rendered":"<p>My recent post titled <a href=\"\/blog\/javascript-numbers-prototype\/\">Fun with JavaScript Numbers and Prototype<\/a> got a lot more attention than I thought it would. So, I considered it cruel not to give String objects equal treatment.<\/p>\n<p>More than a few reader&#39;s of my previous post on numbers pointed out that some of the functions I created were little more than semantically different from built-in functions. I agree, and that&#39;s okay. For me, adding functions to a built-in object&#39;s prototype doesn&#39;t serve to create cutting-edge string manipulation routines, but rather to reduce the complexity of my code, by encapsulating commonly used algorithms into an easy-to-call function.<\/p>\n<p>Using a built-in object&#39;s prototype not only allows us to use a more intuitive syntax:<\/p>\n<p><code>newString = myString.awesomeFunction();<br \/>\n\t<\/code><\/p>\n<p>But also helps us to maintain <a href=\"http:\/\/en.wikipedia.org\/wiki\/Cohesion_%28computer_science%29\" target=\"_blank\">functional cohesion<\/a> within our objects. Meaning, we don&#39;t need to scramble a bunch of unrelated functions together into a giant utility class, i.e.<\/p>\n<p><code>newString = util.awesomeFunction(myString);<br \/>\n\t<\/code><\/p>\n<p>Not only does this lengthen our code, but it&#39;s disorganized. A function that does string manipulation has no business in a class (object) that calculates the perimeter of a rectangle.<\/p>\n<p>Furthermore, it wouldn&#39;t make sense to create another class (object) to house string manipulation methods when we already have the perfect one built in &#8211; the String object.<\/p>\n<p>So, let&#39;s have some fun with String&#39;s prototype!<\/p>\n<p>&nbsp;<\/p>\n<h3>Occurrences in a string<\/h3>\n<p>Sure, this one&#39;s easy. Just perform a RegEx match. But, to someone else reading your code, it might not be immediately apparent what you&#39;re trying to do.<\/p>\n<pre class=\"objectBox inline objectBox-text \" role=\"presentation\"><code>myString = &#39;this is my string&#39;;\r\nnumTimes = myString.match(\/is\/g).length;<\/code><\/pre>\n<p>Drop that into a function on String&#39;s prototype and you&#39;ve got more readable code at the very least:<\/p>\n<p><code>String.prototype.occurrences = function(substring) {<br \/>\n\t&nbsp; var occRegEx = new RegExp(substring, &#39;g&#39;);<br \/>\n\t&nbsp; return this.match(occRegEx).length;<br \/>\n\t}<\/p>\n<p>\t&gt; myString.occurrences(&#39;is&#39;);<br \/>\n\t&nbsp; <span style=\"color:#00f;\">2<\/span><br \/>\n\t<\/code><\/p>\n<p>Okay, that was not awesome. Hey, I&#39;m just getting started!<\/p>\n<p>&nbsp;<\/p>\n<h3>Logging<\/h3>\n<p>What&#39;s the scariest thing about JavaScript? It runs in the user&#39;s browser, you have no eyes and ears! One of my favorite post-launch debugging strategies on the server side is having an email sent to me any time there&#39;s an error on the site. Easily accomplished with server-side code, right. Not so easy on the client side. But, not impossible.<strong> I&#39;m admittedly going to break the rule of cohesion here. A logging function really doesn&#39;t belong in company of string manipulation functions. <\/strong>This concept is just too cool to pass up though.<\/p>\n<p>Wouldn&#39;t it be great to just drop in a statement like:<\/p>\n<p><code>myString.serverLog();<br \/>\n\t<\/code><\/p>\n<p>and have it append to a server-side error log, along with a few context identifiers? Let&#39;s make it happen:<\/p>\n<p><code>String.prototype.serverLog = function() {<br \/>\n\t&nbsp; \/* you can find implementations of an xmlHttpPost AJAX call anywhere and everywhere on the web<br \/>\n\t&nbsp;&nbsp;&nbsp;&nbsp; I won&#39;t waste your time recreating it here. *\/<br \/>\n\t&nbsp; var logString = this + &#39; | &#39; + (new Date()).toString() + &#39; | &#39; + location.href;<br \/>\n\t&nbsp; xmlHttpPost(&#39;\/log.php?s=&#39; + logString);<br \/>\n\t}<br \/>\n\t<\/code><\/p>\n<p>Put that in the context of a try\/catch block and it becomes a lot more useful:<\/p>\n<pre style=\"position: fixed; left: -1000px;\">    try {\r\n     x = y;\r\n    }\r\n    catch(e) {\r\n     if(typeof arguments !== &#39;undefined&#39;) {\r\n       console.log(arguments.callee.toString().match(\/function\\s+([^\\s\\(]+)\/)[0])\r\n     }\r\n     console.log(e.toString());\r\n    }\r\n<\/pre>\n<p><code>try {<br \/>\n\t&nbsp; x = something;<br \/>\n\t}<br \/>\n\tcatch(e) {<br \/>\n\t&nbsp; var soThisHappened = &#39;&#39;;<br \/>\n\t&nbsp; soThisHappened += e.toString();<br \/>\n\t&nbsp; if(typeof arguments !== &#39;undefined&#39;) {<br \/>\n\t&nbsp;&nbsp;&nbsp; \/\/ if we&#39;re inside a function, snag the function name<br \/>\n\t&nbsp;&nbsp;&nbsp; soThisHappened += arguments.callee.toString().match(\/function\\s+([^\\s\\(]+)\/)[0]);<br \/>\n\t&nbsp; }<br \/>\n\t&nbsp; soThisHappened.serverLog();<br \/>\n\t}<\/p>\n<p>\t<\/code><\/p>\n<h3>Markup<\/h3>\n<p>How many times have you written or seen JavaScript like this, myself included:<\/p>\n<p><code>if(document.form1.name.value === &#39;&#39;) {<br \/>\n\t&nbsp; document.write(&#39;&lt;b&gt;Oh no, you forgot your name?&lt;\/b&gt;&#39;);<br \/>\n\t}<\/code><\/p>\n<p>\tAll those gurus are always telling you to keep JavaScript out of your HTML, so shouldn&#39;t the reverse apply? But, I really want to just spit out a boldface message. Here&#39;s a cleaner way:<\/p>\n<p><code>String.prototype.bold = function() {<br \/>\n\t&nbsp; return &#39;&lt;b&gt;&#39; + this + &#39;&lt;\/b&gt;&#39;;<br \/>\n\t}<\/p>\n<p>\tdocument.write(&#39;Oh no, you forgot your name?&#39;.bold());<br \/>\n\t<\/code><\/p>\n<p>&nbsp;<\/p>\n<h3>Language<\/h3>\n<p>The previous couple functions walk the line of cohesion and usefulness, maybe even step over it. So, let&#39;s get back to the core of what a string is: typically a word or group of words in a given language. The String prototype is the perfect home for language functions.<\/p>\n<p><code>String.prototype.isQuestion = function() {<br \/>\n\t&nbsp;&nbsp; var questionIdentifiers = [&#39;when&#39;, &#39;where&#39;, &#39;why&#39;, &#39;what&#39;, &#39;who&#39;, &#39;how&#39;, &#39;can&#39;];<\/p>\n<p>\t&nbsp;&nbsp; \/\/ does the string contain a question mark?<br \/>\n\t&nbsp;&nbsp; if(this.indexOf(&#39;?&#39;) !== -1) {<br \/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br \/>\n\t&nbsp;&nbsp; }<br \/>\n\t&nbsp;&nbsp; <br \/>\n\t&nbsp;&nbsp; \/\/ search keyword may indicate a question without explicitly specifying the question mark<br \/>\n\t&nbsp;&nbsp; for(var i = 0; i &lt; questionIdentifiers.length; i++) {<br \/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(this.indexOf(questionIdentifiers[i]) !== -1) {<br \/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br \/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>\n\t&nbsp;&nbsp; }<\/p>\n<p>\t&nbsp;&nbsp; return false;<br \/>\n\t}<\/code><\/p>\n<p>We could use our new function to refine a user&#39;s search by conditionally returning results more likely to answer questions, than to provide generic information:<\/p>\n<p><code>if(searchPhrase.isQuestion()){<br \/>\n\t&nbsp; \/\/ favor search results in the knowledge base<br \/>\n\t}<br \/>\n\telse {<br \/>\n\t&nbsp; \/\/ favor search results in the product store<br \/>\n\t}<\/code><\/p>\n<p>&nbsp;<\/p>\n<h3>Is it a Word?<\/h3>\n<p>There&#39;s an unfair bias towards the <em>numeric <\/em>in computer science. Of course, it all stems from the fact that the CPU speaks <em>numeric<\/em>, while we humans speak <em>string<\/em>. You can follow a gradual trend towards natural language-based syntax in popular development languages such as ColdFusion and Python. But, there really isn&#39;t much happening in terms of native support for natural language processing. I&#39;m a little surprised by this, because I feel as if half of the applications that I develop require at least a very primitive form of natural language processing (search, form validation, speech recognition, etc.)<\/p>\n<p>Many languages offer <tt>isNumeric()<\/tt>, <tt>isDate()<\/tt>, or<tt> isArray() <\/tt>functions. But, how about <tt>isWord()<\/tt>, <tt>isVerb()<\/tt>, <tt>isCommand()<\/tt>?<\/p>\n<p><tt>isCommand()<\/tt> could be used in searching algorithms (similar to <tt>isQuestion()<\/tt>) above. If a command is detected, the user might know more or less what they are looking for, otherwise, they may need a deeper level of assistance.<\/p>\n<p><tt>isWord()<\/tt> could be used in form validation. If a user submits a field, validate that it&#39;s actually a word (or group of words). The definition of a word is subjective, but we&#39;ll make a few assumptions:<\/p>\n<ol>\n<li>A word does not contain spaces<\/li>\n<li>A word can contain only letters or the hyphen<\/li>\n<li>A word is less than 25 characters in length<\/li>\n<\/ol>\n<p><code>String.prototype.isWord = function() {<br \/>\n\t&nbsp; if(this.length &gt; 25) {<br \/>\n\t&nbsp;&nbsp;&nbsp; return false;<br \/>\n\t&nbsp; }<br \/>\n\t&nbsp; return \/^[A-Za-z\\-]*$\/.test(this);<br \/>\n\t}<\/code><\/p>\n<p>A user submits a form which has fields for first and last name, so you validate against our new isWord() function:<\/p>\n<p><code>if(!firstname.isWord()) {<br \/>\n\t&nbsp; alert(&#39;Is that really your name?&#39;);<br \/>\n\t}<\/code><\/p>\n<p>Well, maybe it isn&#39;t. Many folks out there have a two-word first name. So, let&#39;s expand our String prototype to help out here:<\/p>\n<p><code>String.prototype.words = function() {<br \/>\n\t&nbsp; return this.split(&#39; &#39;);<br \/>\n\t}<\/code><\/p>\n<p>Now we are armed with the basics necessary to properly validate a first name:<\/p>\n<p><code>for each(item in firstname.words()) {<br \/>\n\t&nbsp; if(!item.isWord()) {<br \/>\n\t&nbsp;&nbsp;&nbsp; alert(&#39;Is that really your first name?&#39;);<br \/>\n\t&nbsp;&nbsp;&nbsp; break;<br \/>\n\t&nbsp; }<br \/>\n\t}<br \/>\n\t<\/code><\/p>\n<p>&nbsp;<\/p>\n<p><!--nextpage--><\/p>\n<h3>Functions floating around the web<\/h3>\n<p>This part is more for reference. The following functions are so common, that I thought my post would be incomplete without offering them. They are not my own.<\/p>\n<p><code>String.prototype.capitalize = function() {<br \/>\n\t&nbsp; &nbsp; return this.replace(\/(&amp;)?([a-z])([a-z]{2,})(;)?\/ig, function (all, prefix, letter, word, suffix) {<br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; if (prefix &amp;&amp; suffix) {<br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return all;<br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n\t&nbsp; &nbsp; &nbsp; &nbsp; return letter.toUpperCase() + word.toLowerCase();<br \/>\n\t&nbsp; &nbsp; });<span class=\"Apple-tab-span\" style=\"white-space: pre; \"> <br \/>\n\t<\/span>}<\/code><\/p>\n<p><code>String.prototype.ltrim = function() { <br \/>\n\t&nbsp;&nbsp;&nbsp; \/\/ remove &quot;padding&quot; before the string<br \/>\n\t&nbsp;&nbsp;&nbsp; return this.replace(\/^\\s+\/, &#39;&#39;); <br \/>\n\t};<br \/>\n\t<\/code><\/p>\n<p><code>String.prototype.rtrim = function() {<br \/>\n\t&nbsp;&nbsp;&nbsp; \/\/ remove &quot;padding&quot; after the string<br \/>\n\t&nbsp;&nbsp;&nbsp; return this.replace(\/\\s+$\/,&#39;&#39;);<br \/>\n\t};<br \/>\n\t<\/code><\/p>\n<p><code>String.prototype.repeat = function( num )<br \/>\n\t{<br \/>\n\t&nbsp;&nbsp;&nbsp; \/\/ repeat a string <em>num<\/em> times<br \/>\n\t&nbsp;&nbsp;&nbsp; return new Array( num + 1 ).join( this );<br \/>\n\t}<br \/>\n\t<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>My recent post titled Fun with JavaScript Numbers and Prototype got a lot more attention than I thought it would. So, I considered it cruel&hellip;<\/p>\n","protected":false},"author":1,"featured_media":1010,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"amp_status":""},"categories":[5],"tags":[218,199,227],"_links":{"self":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/942"}],"collection":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/comments?post=942"}],"version-history":[{"count":20,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/942\/revisions"}],"predecessor-version":[{"id":1087,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/942\/revisions\/1087"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/media\/1010"}],"wp:attachment":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/media?parent=942"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/categories?post=942"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/tags?post=942"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}