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
items 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 5unshift 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( )); // 666The 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 trueThe 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 truetest 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 −1The 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 5The 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'