Though this be madness, yet there is method in 't.
JavaScript includes a small set of standard methods that are available on the standard types.
The concat
method produces
a new array containing a shallow copy of this
array
with the
item
s appended to it. If an
item
is an array, then each of
its elements is appended individually. Also see
array
.push(
item
...)
later in this chapter.
var a = ['a', 'b', 'c']; var b = ['x', 'y', 'z']; var c = a.concat(b, true); // c is ['a', 'b', 'c', 'x', 'y', 'z', true]
The join
method makes a
string from an array
. It does this
by making a string of each of the
array
's elements, and then
concatenating them all together with a
separator
between them. The
default separator
is ','
. To join without separation,
use an empty string as the
separator
.
If you are assembling a string from a large number of pieces,
it is usually faster to put the pieces into an array and
join
them than it is to
concatenate the pieces with the +
operator:
var a = ['a', 'b', 'c']; a.push('d'), var c = a.join(''), // c is 'abcd';
The pop
and push
methods make an
array
work like a stack. The
pop
method removes and
returns the last element in this
array
. If the
array
is empty
, it returns undefined
.
var a = ['a', 'b', 'c']; var c = a.pop( ); // a is ['a', 'b'] & c is 'c'
pop
can be implemented like
this:
Array.method('pop', function ( ) { return this.splice(this.length - 1, 1)[0]; });
The push
method appends
items
to the end of an array.
Unlike the concat
method, it
modifies the array
and appends array
items whole. It returns the new length of the
array
:
var a = ['a', 'b', 'c']; var b = ['x', 'y', 'z']; var c = a.push(b, true); // a is ['a', 'b', 'c', ['x', 'y', 'z'], true] // c is 5;
push
can be implemented
like this:
Array.method('push', function ( ) { this.splice.apply( this, [this.length, 0].concat(Array.prototype.slice.apply(arguments))); return this.length; });
The reverse
method modifies
the array
by reversing the order of
the elements. It returns the
array
:
var a = ['a', 'b', 'c']; var b = a.reverse( ); // both a and b are ['c', 'b', 'a']
The shift
method removes
the first element from an array
and
returns it. If the array
is empty, it
returns undefined
. shift
is usually much slower than
pop
:
var a = ['a', 'b', 'c']; var c = a.shift( ); // a is ['b', 'c'] & c is 'a'
shift
can be implemented
like this:
Array.method('shift', function ( ) { return this.splice(0, 1)[0]; });
The slice
method makes a
shallow copy of a portion of an array
. The first element copied will be
array
[
start
]
. It will stop before
copying array
[
end
]
. The
end
parameter is optional, and
the default is array
.length
. If either parameter
is negative, array
.length
will be added to them
in an attempt to make them nonnegative. If
start
is greater than or equal to
array
.length
, the result will be a
new empty array. Do not confuse slice
with splice
. Also see
string
.slice
later in this
chapter.
var a = ['a', 'b', 'c']; var b = a.slice(0, 1); // b is ['a'] var c = a.slice(1); // c is ['b', 'c'] var d = a.slice(1, 2); // d is ['b']
The sort
method sorts the
contents of an array
in place. It
sorts arrays of numbers incorrectly:
var n = [4, 8, 15, 16, 23, 42]; n.sort( ); // n is [15, 16, 23, 4, 42, 8]
JavaScript's default comparison function assumes that the elements to be sorted are strings. It isn't clever enough to test the type of the elements before comparing them, so it converts the numbers to strings as it compares them, ensuring a shockingly incorrect result.
Fortunately, you may replace the comparison function with your
own. Your comparison function should take two parameters and
return 0
if the two
parameters are equal, a negative number if the first parameter
should come first, and a positive number if the second parameter
should come first. (Old-timers might be reminded of the FORTRAN
II arithmetic IF
statement.)
n.sort(function (a, b) { return a − b; }); // n is [4, 8, 15, 16, 23, 42];
That function will sort numbers, but it doesn't sort strings. If we want to be able to sort any array of simple values, we must work harder:
var m = ['aa', 'bb', 'a', 4, 8, 15, 16, 23, 42]; m.sort(function (a, b) { if (a === b) { return 0; } if (typeof a === typeof b) { return a < b ? −1 : 1; } return typeof a < typeof b ? −1 : 1; }); // m is [4, 8, 15, 16, 23, 42, 'a', 'aa', 'bb']
If case is not significant, your comparison function should
convert the operands to lowercase before comparing them. Also
see string
.localeCompare
later in this
chapter.
With a smarter comparison function, we can sort an array of objects. To make things easier for the general case, we will write a function that will make comparison functions:
// Function by takes a member name string and returns // a comparison function that can be used to sort an // array of objects that contain that member. var by = function (name) { return function (o, p) { var a, b; if (typeof o === 'object' && typeof p === 'object' && o && p) { a = o[name]; b = p[name]; if (a === b) { return 0; } if (typeof a === typeof b) { return a < b ? −1 : 1; } return typeof a < typeof b ? −1 : 1; } else { throw { name: 'Error', message: 'Expected an object when sorting by ' + name; }; } }; }; var s = [ {first: 'Joe', last: 'Besser'}, {first: 'Moe', last: 'Howard'}, {first: 'Joe', last: 'DeRita'}, {first: 'Shemp', last: 'Howard'}, {first: 'Larry', last: 'Fine'}, {first: 'Curly', last: 'Howard'} ]; s.sort(by('first')); // s is [ // {first: 'Curly', last: 'Howard'}, // {first: 'Joe', last: 'DeRita'}, // {first: 'Joe', last: 'Besser'}, // {first: 'Larry', last: 'Fine'}, // {first: 'Moe', last: 'Howard'}, // {first: 'Shemp', last: 'Howard'} // ]
The sort
method is not
stable, so:
s.sort(by('first')).sort(by('last'));
is not guaranteed to produce the correct sequence. If you want
to sort on multiple keys, you again need to do more work. We can
modify by
to take a second
parameter, another compare
method that will be called to break ties when the major key
produces a match:
// Function by takes a member name string and an // optional minor comparison function and returns // a comparison function that can be used to sort an // array of objects that contain that member. The // minor comparison function is used to break ties // when the o[name] and p[name] are equal. var by = function (name, minor) { return function (o, p) { var a, b; if (o && p && typeof o === 'object' && typeof p === 'object') { a = o[name]; b = p[name]; if (a === b) { return typeof minor === 'function' ? minor(o, p) : 0; } if (typeof a === typeof b) { return a < b ? −1 : 1; } return typeof a < typeof b ? −1 : 1; } else { throw { name: 'Error', message: 'Expected an object when sorting by ' + name; }; } }; }; s.sort(by('last', by('first'))); // s is [ // {first: 'Joe', last: 'Besser'}, // {first: 'Joe', last: 'DeRita'}, // {first: 'Larry', last: 'Fine'}, // {first: 'Curly', last: 'Howard'}, // {first: 'Moe', last: 'Howard'}, // {first: 'Shemp', last: 'Howard'} // ]
The splice
method removes
elements from an array
, replacing
them with new item
s. The
start
parameter is the number of
a position within the array
. The
deleteCount
parameter is the
number of elements to delete starting from that position. If
there are additional parameters, those
item
s will be inserted at the
position. It returns an array containing the deleted
elements.
The most popular use of splice
is to delete elements from an array. Do
not confuse splice
with
slice
:
var a = ['a', 'b', 'c']; var r = a.splice(1, 1, 'ache', 'bug'), // a is ['a', 'ache', 'bug', 'c'] // r is ['b']
splice
can be implemented
like this:
Array.method('splice', function (start, deleteCount) { var max = Math.max, min = Math.min, delta, element, insertCount = max(arguments.length - 2, 0), k = 0, len = this.length, new_len, result = [], shift_count; start = start || 0; if (start < 0) { start += len; } start = max(min(start, len), 0); deleteCount = max(min(typeof deleteCount === 'number' ? deleteCount : len, len − start), 0); delta = insertCount − deleteCount; new_len = len + delta; while (k < deleteCount) { element = this[start + k]; if (element !== undefined) { result[k] = element; } k += 1; } shift_count = len - start - deleteCount; if (delta < 0) { k = start + insertCount; while (shift_count) { this[k] = this[k − delta]; k += 1; shift_count −= 1; } this.length = new_len; } else if (delta > 0) { k = 1; while (shift_count) { this[new_len − k] = this[len − k]; k += 1; shift_count −= 1; } this.length = new_len; } for (k = 0; k < insertCount; k += 1) { this[start + k] = arguments[k + 2]; } return result; });
The unshift
method is like
the push
method except that
it shoves the item
s onto the front
of this array
instead of at the end.
It returns the array
's new length
:
var a = ['a', 'b', 'c']; var r = a.unshift('?', '@'), // a is ['?', '@', 'a', 'b', 'c'] // r is 5
unshift
can be implemented
like this:
Array.method('unshift', function ( ) { this.splice.apply(this, [0, 0].concat(Array.prototype.slice.apply(arguments))); return this.length; });
The apply
method invokes a
function
, passing in the object
that will be bound to this
and an optional array of arguments. The apply
method is used in the apply invocation
pattern (Chapter 4):
Function.method('bind', function (that) { // Return a function that will call this function as // though it is a method of that object. var method = this, slice = Array.prototype.slice, args = slice.apply(arguments, [1]); return function ( ) { return method.apply(that, args.concat(slice.apply(arguments, [0]))); }; }); var x = function ( ) { return this.value; }.bind({value: 666}); alert(x( )); // 666
The toExponential
method
converts this number
to a string in
the exponential form. The optional
fractionDigits
parameter controls
the number of decimal places. It should be between 0 and
20:
document.writeln(Math.PI.toExponential(0)); document.writeln(Math.PI.toExponential(2)); document.writeln(Math.PI.toExponential(7)); document.writeln(Math.PI.toExponential(16)); document.writeln(Math.PI.toExponential( )); // Produces 3e+0 3.14e+0 3.1415927e+0 3.1415926535897930e+0 3.141592653589793e+0
The toFixed
method converts
this number
to a string in the
decimal form. The optional
fractionDigits
parameter controls
the number of decimal places. It should be between 0 and 20. The
default is 0:
document.writeln(Math.PI.toFixed(0)); document.writeln(Math.PI.toFixed(2)); document.writeln(Math.PI.toFixed(7)); document.writeln(Math.PI.toFixed(16)); document.writeln(Math.PI.toFixed( )); // Produces 3 3.14 3.1415927 3.1415926535897930 3
The toPrecision
method
converts this number
to a string in
the decimal form. The optional
precision
parameter controls the
number of digits of precision. It should be between 1 and
21:
document.writeln(Math.PI.toPrecision(2)); document.writeln(Math.PI.toPrecision(7)); document.writeln(Math.PI.toPrecision(16)); document.writeln(Math.PI.toPrecision( )); // Produces 3.1 3.141593 3.141592653589793 3.141592653589793
The toString
method
converts this number
to a string. The
optional radix
parameter controls
radix, or base. It should be between 2 and 36. The default
radix
is base 10. The
radix
parameter is most commonly
used with integers, but it can be used on any number.
The most common case, number
.toString( )
, can be written
more simply as String(
number
)
:
document.writeln(Math.PI.toString(2)); document.writeln(Math.PI.toString(8)); document.writeln(Math.PI.toString(16)); document.writeln(Math.PI.toString( )); // Produces 11.001001000011111101101010100010001000010110100011 3.1103755242102643 3.243f6a8885a3 3.141592653589793
The hasOwnProperty
method
returns true
if the
object
contains a property having
the name
. The prototype chain is not
examined. This method is useless if the
name
is hasOwnProperty
:
var a = {member: true}; var b = Object.create(a); // from Chapter 3 var t = a.hasOwnProperty('member'), // t is true var u = b.hasOwnProperty('member'), // u is false var v = b.member; // v is true
The exec
method is the most
powerful (and slowest) of the methods that use regular
expressions. If it successfully matches the
regexp
and the
string
, it returns an array. The
0 element of the array will contain the substring that matched
the regexp
. The 1 element is the
text captured by group 1, the 2 element is the text captured by
group 2, and so on. If the match fails, it returns null
.
If the regexp
has a g
flag, things are a little more
complicated. The searching begins not at position 0 of the
string, but at position regexp
.lastIndex
(which is
initially zero). If the match is successful, then
regexp
.lastIndex
will be set to the
position of the first character after the match. An unsuccessful
match resets regexp
.lastIndex
to 0.
This allows you to search for several occurrences of a pattern
in a string by calling exec
in a loop. There are a couple things to watch out for. If you
exit the loop early, you must reset
regexp
.lastIndex
to 0 yourself
before entering the loop again. Also, the ^
factor matches only when
regexp
.lastIndex
is 0:
// Break a simple html text into tags and texts. // (See string.replace for the entityify method.) // For each tag or text, produce an array containing // [0] The full matched tag or text // [1] The /, if there is one // [2] The tag name // [3] The attributes, if any var text = '<html><body bgcolor=linen><p>' + 'This is <b>bold</b>!</p></body></html>'; var tags = /[^<>]+|<(/?)([A-Za-z]+)([^<>]*)>/g; var a, i; while ((a = tags.exec(text))) { for (i = 0; i < a.length; i += 1) { document.writeln(('// [' + i + '] ' + a[i]).entityify( )); } document.writeln( ); } // Result: // [0] <html> // [1] // [2] html // [3] // [0] <body bgcolor=linen> // [1] // [2] body // [3] bgcolor=linen // [0] <p> // [1] // [2] p // [3] // [0] This is // [1] undefined // [2] undefined // [3] undefined // [0] <b> // [1] // [2] b // [3] // [0] bold // [1] undefined // [2] undefined // [3] undefined // [0] </b> // [1] / // [2] b // [3] // [0] ! // [1] undefined // [2] undefined // [3] undefined // [0] </p> // [1] / // [2] p // [3] // [0] </body> // [1] / // [2] body // [3] // [0] </html> // [1] / // [2] html // [3]
The test
method is the
simplest (and fastest) of the methods that use regular
expressions. If the regexp
matches
the string
, it returns true
; otherwise, it returns
false
. Do not use the
g
flag with this
method:
var b = /&.+;/.test('frank & beans'), // b is true
test
could be implemented
as:
RegExp.method('test', function (string) { return this.exec(string) !== null; });
The charAt
method returns
the character at position pos
in this
string
. If
pos
is less than zero or greater
than or equal to string
.length
, it returns the empty
string. JavaScript does not have a character type. The result of
this method is a string:
var name = 'Curly'; var initial = name.charAt(0); // initial is 'C'
charAt
could be implemented
as:
String.method('charAt', function (pos) { return this.slice(pos, pos + 1); });
The charCodeAt
method is
the same as charAt
except
that instead of returning a string, it returns an integer
representation of the code point value of the character at
position pos
in that
string
. If
pos
is less than zero or greater
than or equal to string
.length
, it returns NaN
:
var name = 'Curly'; var initial = name.charCodeAt(0); // initial is 67
The concat
method makes a
new string by concatenating other strings together. It is rarely
used because the +
operator
is more convenient:
var s = 'C'.concat('a', 't'), // s is 'Cat'
The indexOf
method searches
for a searchString
within a
string
. If it is found, it
returns the position of the first matched character; otherwise,
it returns −1. The optional position
parameter causes the search to begin at some specified position
in the string
:
var text = 'Mississippi'; var p = text.indexOf('ss'), // p is 2 p = text.indexOf('ss', 3); // p is 5 p = text.indexOf('ss', 6); // p is −1
The lastIndexOf
method is
like the indexOf
method,
except that it searches from the end of the string instead of
the front:
var text = 'Mississippi'; var p = text.lastIndexOf('ss'), // p is 5 p = text.lastIndexOf('ss', 3); // p is 2 p = text.lastIndexOf('ss', 6); // p is 5
The localeCompare
method
compares two strings. The rules for how the strings are compared
are not specified. If this string
is
less than that
string, the result is
negative. If they are equal, the result is zero. This is similar
to the convention for the array
.sort
comparison
function:
var m = ['AAA', 'A', 'aa', 'a', 'Aa', 'aaa']; m.sort(function (a, b) { return a.localeCompare(b); }); // m (in some locale) is // ['a', 'A', 'aa', 'Aa', 'aaa', 'AAA']
The match
method matches a
string and a regular expression. How it does this depends on the
g
flag. If there is no
g
flag, then the result
of calling string
.match(
regexp
)
is the same as calling
regexp
.exec(
string
)
. However, if the
regexp
has the g
flag, then it produces an array
of all the matches but excludes the capturing groups:
var text = '<html><body bgcolor=linen><p>' + 'This is <b>bold</b>!</p></body></html>'; var tags = /[^<>]+|<(/?)([A-Za-z]+)([^<>]*)>/g; var a, i; a = text.match(tags); for (i = 0; i < a.length; i += 1) { document.writeln(('// [' + i + '] ' + a[i]).entityify( )); } // The result is // [0] <html> // [1] <body bgcolor=linen> // [2] <p> // [3] This is // [4] <b> // [5] bold // [6] </b> // [7] ! // [8] </p> // [9] </body> // [10] </html>
The replace
method does a
search and replace operation on this
string
, producing a new string.
The searchValue
argument can be a
string or a regular expression object. If it is a string,
only the first occurrence of the
searchValue
is replaced,
so:
var result = "mother_in_law".replace('_', '-'),
will produce "mother-in_law"
, which might be a
disappointment.
If searchValue
is a regular
expression and if it has the g
flag, then it will replace all occurrences. If
it does not have the g
flag,
then it will replace only the first occurrence.
The replaceValue
can be a string or
a function. If replaceValue
is a
string, the character $
has
special meaning:
// Capture 3 digits within parens var oldareacode = /((d{3}))/g; var p = '(555)666-1212'.replace(oldareacode, '$1-'), // p is '555-666-1212'
Dollar sequence |
Replacement |
---|---|
|
|
|
The matched text |
|
Capture group text |
|
The text preceding the match |
|
The text following the match |
If the replaceValue
is a function,
it will be called for each match, and the string returned by the
function will be used as the replacement text. The first
parameter passed to the function is the matched text. The second
parameter is the text of capture group 1, the next parameter is
the text of capture group 2, and so on:
String.method('entityify', function ( ) { var character = { '<' : '<', '>' : '>', '&' : '&', '"' : '"' }; // Return the string.entityify method, which // returns the result of calling the replace method. // Its replaceValue function returns the result of // looking a character up in an object. This use of // an object usually outperforms switch statements. return function ( ) { return this.replace(/[<>&"]/g, function (c) { return character[c]; }); }; }( )); alert("<&>".entityify( )); // <&>
The search
method is like
the indexOf
method, except
that it takes a regular expression object instead of a string.
It returns the position of the first character of the first
match, if there is one, or −1 if the search fails. The g
flag is ignored. There is no
position
parameter:
var text = 'and in it he says "Any damn fool could'; var pos = text.search(/["']/); // pos is 18
The slice
method makes a
new string by copying a portion of another
string
. If the
start
parameter is negative, it
adds string
.length
to it. The
end
parameter is optional, and
its default value is string
.length
. If the
end
parameter is negative, then
string
.length
is added to it. The
end
parameter is one greater than
the position of the last character. To get n
characters starting at position
p
, use
string
.slice(p, p + n)
. Also see
string
.substring
and
array
.slice
, later and earlier in
this chapter, respectively.
var text = 'and in it he says "Any damn fool could'; var a = text.slice(18); // a is '"Any damn fool could' var b = text.slice(0, 3); // b is 'and' var c = text.slice(−5); // c is 'could' var d = text.slice(19, 32); // d is 'Any damn fool'
The split
method creates an
array of strings by splitting this
string
into pieces. The optional
limit
parameter can limit the
number of pieces that will be split. The
separator
parameter can be a
string or a regular expression.
If the separator
is the empty
string, an array of single characters is produced:
var digits = '0123456789'; var a = digits.split('', 5); // a is ['0', '1', '2', '3', '4']
Otherwise, the string
is searched
for all occurrences of the separator
.
Each unit of text between the separators is copied into the
array. The g
flag is
ignored:
var ip = '192.168.1.0'; var b = ip.split('.'), // b is ['192', '168', '1', '0'] var c = '|a|b|c|'.split('|'), // c is ['', 'a', 'b', 'c', ''] var text = 'last, first ,middle'; var d = text.split(/s*,s*/); // d is [ // 'last', // 'first', // 'middle' // ]
There are some special cases to watch out for. Text from capturing groups will be included in the split:
var e = text.split(/s*(,)s*/); // e is [ // 'last', // ',', // 'first', // ',', // 'middle' // ]
Some implementations suppress empty strings in the output
array when the separator
is a regular
expression:
var f = '|a|b|c|'.split(/|/); // f is ['a', 'b', 'c'] on some systems, and // f is ['', 'a', 'b', 'c', ''] on others
The substring
method is the
same as the slice
method
except that it doesn't handle the adjustment for negative
parameters. There is no reason to use the substring
method. Use slice
instead.
The toLocaleLowerCase
method produces a new string that is made by converting this
string
to lowercase using the
rules for the locale. This is primarily for the benefit of
Turkish because in that language `I' converts to 1
, not `i'.
The toLocaleUpperCase
method produces a new string that is made by converting this
string
to uppercase using the
rules for the locale. This is primarily for the benefit of
Turkish, because in that language `i' converts to
`', not `I'.
The toLowerCase
method
produces a new string that is made by converting this
string
to lowercase.
The toUpperCase
method
produces a new string that is made by converting this
string
to uppercase.
The String.fromCharCode
function produces a string from a series of numbers.
var a = String.fromCharCode(67, 97, 116); // a is 'Cat'
3.144.205.223