A data type is a set of values grouped together because certain operations apply to them. Every programming language seems to have at least numbers, strings, and booleans.
JavaScript has 8 types: undefined, null, boolean, number, bigint, string, symbol, and object.
The type Undefined
has one value, undefined
. This is the value given to variables declared without initializers. It means that we don’t know, don’t care, or aren’t supposed to know the actual value.
let numberOfPets = 5 let age numberOfPets // 5 (bc you said so) age // undefined (HOW DARE YOU EVEN ASK)
The type Null
has one value, null
. Technically you can use this value for whatever you want, but conventionally people use it as a way to emphatically and explicitly mean there’s really no value.
let supervisor = "None" // The supervisor's name is "None"
let supervisor = null // There is absolutely NO supervisor, and // ...we know that for sure.
let supervisor = undefined // There might be a supervisor, we just // ...don't know or care who it is. Or we // ...are being told it is none of our // ...business.
The two boolean values are true
and false
. Operators producing boolean values are &&
(“and also”), ||
(“or else”), and !
(“not”). Try:
3 < 5 && 21 === 8 // false 1 === 2 || 13 > -5 // true !(3 <= 55) // false let sick = false let weekday = true let holiday = true let canSleepIn = sick || !weekday || holiday let x = 42 let y = -1 let bothPositive = x > 0 && y > 0 let atLeastOneNegative = x < 0 || y < 0 let exactlyOneNegative = x < 0 !== y < 0 let atLeastOneNonPositive = !bothPositive
Here are some examples of numbers:
42 // Forty-two, and example of a “whole number” or “integer” 3.15 // Three and 15 one-hundredths 1_000_000_000 // It’s fine to use underscores in your numbers 2.4434634E9 // 2.443634 times 10 to the 9th 7.538E-23 // 7.538 times 10 to the -23rd 388E12 // 388 times 10 to the 12th 0x3F82 // A hexadecimal literal (base-16), equal to 16258 0b111011010101 // A binary literal (base-2), equal to 3797 0o3571 // An octal literal (base-8), equal to 1913
Decimal, Binary, Octal, Hex
Need a review?JavaScript can represent any base from 2-36, but these four are the only ones that have a special syntax for directly writing numerals in.
- Decimal (base-10): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ..., 19, 20, 21, ..., 99, 100, ...
- Binary (base-2): 0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, ...
- Octal (base-8): 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, ..., 17, 20, ... 77, 100, ...
- Hex (base-16): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, ..., 19, 1A, 1B, ... 1F, 20, 21, ..., 99, A0, ... FE, FF, 100, ...
Two important things to know:
Learn these by example. You do not need to memorize them all. Try them in a shell yourself. Not all of these operations are commonly used, but it is good to know that they are there.
+89 // Unary plus (doesn’t really do anything, does it?) -21 // This is negation (not to be confused with subtraction) 5 + 8 // Addition 18 - 30 // Subtraction 23.5 * 12 // Multiplication 31 / 7 // Division 37 % 7 // Remainder 22.5 % 5.25 // Remainder even works on non-integers! 3 ** 5 // Exponentiation Math.E // Euler’s number, prints as 2.718281828459045 Math.PI // Half of tau, prints as 3.141592653589793 Math.SQRT1_2 // Square root of ½, prints as 0.7071067811865476 Math.SQRT2 // Square root of 2, prints as 1.4142135623730951 Math.LN2 // Natural log of 2, prints as 0.6931471805599453 Math.LN10 // Natural log of 10, prints as 2.302585092994046 Math.LOG2E // Log-base-2 of e, prints as 1.4426950408889634 Math.LOG10E // Log-base-10 of e, prints as 0.4342944819032518 Math.abs(-103) // Absolute value of -103, namely 103 Math.sqrt(100) // 10 Math.cbrt(343) // 7 Math.hypot(3, -4) // 5 Math.hypot(2, 7, 5) // 8.831760866327848 Math.random() // Random number between 0 inclusive and 1 exclusive Math.sin(1) // Sine of 1 radian Math.cos(-3) // Cosine of -3 radians Math.tan(1.57) // Tangent of 1.57 radians Math.asin(0.5) // The arcsine of 0.5 Math.acos(-1) // The arccosine of -1 Math.atan(2) // The arctangent of 2 Math.atan2(4,-3) // The angle in radians from the x-axis to the vector <-3, 4> Math.atan2(1,0) // The angle in radians from the x-axis to the vector <0, 1> Math.exp(2) // e to the 2nd power Math.log(2.718) // Natural log of 2.718 Math.log2(2048) // 11 Math.ceil(-5.9) // -5 Math.ceil(5.9) // 6 Math.floor(-5.9) // -6 Math.floor(5.9) // 5 Math.round(-5.9) // -6 Math.round(5.9) // 6 Math.round(2.5) // 3 Math.round(-2.5) // -2 (rounding at halves is toward +∞) Math.trunc(2.5) // -2 (trunc just removes the fractional part) Math.trunc(-2.5) // -2 (... regardless of sign, i.e. “rounds toward 0”) Math.max(3, -8, 2.9, -2) // 3 Math.min(3, -8, 2.9, -2) // -8 Math.min() // Infinity Math.max() // -Infinity Number.isInteger(90) // true Number.isInteger(90.3) // false Number.isInteger(90.0) // true 798 .toString(16) // '31e' 798 .toString(2) // '1100011110' 798 .toString(8) // '1436' 798 .toString(30) // 'qi' 798 .toString(36) // 'm6' // Rare operations: you need to know what a two’s complement encodings is // in order to understand these. 61 & 22 // Bitwise conjunction 894 | 202 // Bitwise disjunction 3 << 5 // Left shift 298332 >> 6 // Arithmetic right shift -53 >> 3 // Arithmetic right shift of a negative value -53 >>> 3 // Logical right shift ~34 // Bitwise complement ~(-35) // Can you see the relationship between ~ and -?
toString
examples above?
The number NaN
means not a number and results from computations like:
0 / 0 // NaN Infinity - Infinity // NaN Infinity * 0 // NaN Infinity / Infinity // NaN Math.acos(100) // NaN
Any arithmetic operation applied to NaN
results in NaN
. And guess what: NaN is not equal to anything, not even NaN itself!! To to check for NaN
, use Number.isNaN()
:
8 + NaN // NaN NaN === NaN // false (no kidding!) Number.isNaN(0 / 0) // true Number.isNaN(5 / 0) // false, it’s infinity Number.isNaN("dog") // false, a string is not the same thing as the value NaN
acos(-2)
actually has a value, so why do you think JavaScript says Math.acos(-2)
is NaN?
Only certain numbers can be represented exactly in JavaScript—the ones representable by the binary64 format of IEEE 754. All other numbers have to be approximated by the nearest representable number. This is a fact of life with computers that store values in fixed-size storage locations.
0.1 + 0.2 // 0.30000000000000004 0.3 / 0.1 // 2.9999999999999996 0.17258 * 7 // 1.2080600000000001 0.1339 * 714 // 95.60459999999999 2987347774829323985 // 2987347774829324000 2987347774829323985 + 100 // 2987347774829324000 2987347774829323985 + 2008 // 2987347774829326000 955 ** 100 // 1.0007766372740826e+298 955 ** 105 // Infinity 5.919873e-300 // 5.919873e-300 5.919873e-320 // 5.92e-320 5.919873e-324 // 5e-324 5.919873e-325 // 0
Read more about Numeric Encoding to find out which numbers are exactly representable.
Good to know:
Number.MIN_SAFE_INTEGER
and Number.MAX_SAFE_INTEGER
. When you do “counting” computations you often have to make sure you are in this range.
Useful numbers:
JavaScript Name | Exact Value | Printed as | Description |
---|---|---|---|
Number.NEGATIVE_INFINITY | $-\infty$ | -Infinity | Negative infinity |
-Number.MAX_VALUE | $-2^{1024}+2^{971}$ | -1.7976931348623157e+308 | Most negative finite number |
Number.MIN_SAFE_INTEGER | $-(2^{53}-1)$ | -9007199254740991 | The smallest integer $n$ for which $n-1$ and $n-2$ have distinct representations. |
-Number.MIN_VALUE | $-2^{-1074}$ | -5e-324 | Negative representable number closest to zero |
Number.MIN_VALUE | $2^{-1074}$ | 5e-324 | Positive representable number closest to zero |
Number.MAX_SAFE_INTEGER | $2^{53}-1$ | 9007199254740991 | The largest integer $n$ for which $n+1$ and $n+2$ have distinct representations. |
Number.MAX_VALUE | $2^{1024}-2^{971}$ | 1.7976931348623157e+308 | Most positive finite number |
Number.POSITIVE_INFINITY | $+\infty$ | Infinity | Positive infinity |
That safety thing, in more detail:
Test functions:
Each of these functions return a boolean value:
Number.isFinite(
$x$)
Number.isInteger(
$x$)
Number.isSafeInteger(
$x$)
Number.isNaN(
$x$)
A bigint is a different kind of number: it has to be an integer, but its size is technically unbounded (tho if it gets too big your memory can fill up and bad things can happen). You get the arithmentic operations +
(addition), -
, *
, /
, **
, <<
, >>
, &
, |
, ^
, ~
, and negation. You cannot use the Math
operations on them and you cannot mix bigints and numbers!
420n * -5n // -2100n 75n / 8n // 9n (bigint division rounds toward zero!) 55n ** 89n // 78033190670751819287411028871804135604662579206582642179598761630756217530788715355143091589992314168288227641651598476268958393120556138455867767333984375n // Bigints give full precision, numbers do not! 98n ** 10n // 81707280688754689024n 98 ** 10 // 81707280688754690000
2n ** 10000n
in the console. Did id compute quickly?
Intuitively, a string is a sequence of characters. In JavaScript we write them surrounded by quotes, apostrophes, or backticks:
"hello" 'hello' `hello` "She said 'I don’t think so 😎'... (╯°□°)╯︵ ┻━┻)" 'x = "4"' "Shall we 🏄♀️ or play 🏀?" "¡¡Olé 🙌!!" "Ruby was created by まつもと ゆきひろ" "E hele kāua i Kauaʻi" 'Поехали в Кауаи' "අපි කවායි වෙත යමු" '1. f3 e5 g4 ♛h4++'
Strings can be concatenated, joined, trimmed, split, and interpolated. You can even check to see is one string is included in another (either at the beginning, end, or anywhere):
" hello ".trimStart() // "hello " " hello ".trimEnd() // " hello" " hello ".trim() // "hello" "hello".replace("e", "u") // "hullo" let x = 8 'After ${x} comes ${x+1}' // "After 8 comes 9" "hello".includes("ell") // true "hello".startsWith("h") // true "hello".endsWith("w") // false const s = "dog" const t = "house" const ajin = ["Kei", "Kō", "Izumi", "Okuyama"] s + t // "doghouse" `${s}${t}` // "doghouse" s.concat(t) // "doghouse" s.repeat(3) // "dogdogdog" "♥️".repeat(8) // "♥️♥️♥️♥️♥️♥️♥️♥️" ajin.join("-👻-") // "Kei-👻-Kō-👻-Izumi-👻-Okuyama" "Mississippi".split("si") // [ 'Mis', 's', 'ppi' ] "😍😨:👍👍👠:🦆".split(":") // [ "😍😨", "👍👍👠", "🦆" ] [..."hi 🎃 🏈"] // [ "h", "i", " ", "🎃", " ", "🏈" ] [..."👋🏄♀️"] // [ "👋", "🏄", "", "♀", "" ]
WAIT WHAT HAPPENED WITH THAT LAST ONE THERE?
Internally, strings are not exactly strings of characters, but they feel like they are. To understand strings, we have to go a bit deeper and ask: What is a character?
A character is just a named, abstract symbol. Examples:
PLUS SIGN CYRILLIC SMALL LETTER TSE RECYCLING SYMBOL FOR TYPE-2 PLASTICS MUSICAL SYMBOL FERMATA BELOW
Do not confuse a character with a glyph, which is a picture of a character. Two or more characters can share the same glyph (e.g. LATIN CAPITAL LETTER A and GREEK CAPITAL LETTER ALPHA), and one character can have many glyphs (think fonts, e.g., A A A $\mathcal{A}$ $\mathscr{A}$).
Unicode is a character set. Other character sets include ASCII, Latin-1, and Windows-1252, but you should always use Unicode. A character set consists of a set of characters each of which has a unique non-negative integer code point. Traditionally, codepoints are given in hex. Here are some examples from Unicode:
25 PERCENT SIGN 2B PLUS SIGN 54 LATIN CAPITAL LETTER T 5D RIGHT SQUARE BRACKET B0 DEGREE SIGN C9 LATIN CAPITAL LETTER E WITH ACUTE 2AD LATIN LETTER BIDENTAL PERCUSSIVE 39B GREEK CAPITAL LETTER LAMDA 446 CYRILLIC SMALL LETTER TSE 543 ARMENIAN CAPITAL LETTER CHEH 5E6 HEBREW LETTER TSADI 635 ARABIC LETTER SAD 784 THAANA LETTER BAA 94A DEVANAGARI VOWEL SIGN SHORT O 9D7 BENGALI AU LENGTH MARK BEF TAMIL DIGIT NINE D93 SINHALA LETTER AIYANNA F0A TIBETAN MARK BKA- SHOG YIG MGO 11C7 HANGUL JONGSEONG NIEUN-SIOS 1293 ETHIOPIC SYLLABLE NAA 13CB CHEROKEE LETTER QUV 2023 TRIANGULAR BULLET 20A4 LIRA SIGN 2105 CARE OF 213A ROTATED CAPITAL Q 21B7 CLOCKWISE TOP SEMICIRCLE ARROW 2226 NOT PARALLEL TO 2234 THEREFORE 265E BLACK CHESS KNIGHT 1D111 MUSICAL SYMBOL FERMATA BELOW 1D122 MUSICAL SYMBOL F CLEF 1F3C4 SURFER
Unicode code points are traditionally written with U+
followed by four to six hex digits (e.g. U+00C9
, U+1D122
).
You can browse the entire Unicode character set, and see sample glyphs for each character, at the Unicode Consortium’s Code Charts. The main Unicode site contains a wealth of additional documentation, indexes, and search tools for finding characters. Sample chapters of the Unicode book, including the interesting chapter on East Asian scripts are worth reading.
Also check out the awesome charbase.com and codepoints.net.
You can build a string out of codepoints:
String.fromCodePoint(65, 66, 67) // "ABC" String.fromCodePoint(1087, 1088, 1080, 1074, 1077, 1090) // "привет" String.fromCodePoint(0x4f, 0x68, 44, 32, 128169, 0x1f4a9, 128175) // 'Oh, 💩💩💯'
You can embed code points in a string. For characters with code points in the range U+0000 to U+007F, use \x
hh; in the range U+0000 to U+FFFF use \u
hhhh. You can always use \u{
...}
no matter what the code point is. Examples:
"\x41" // "A" "\u0041" // "A" "\u{41}" // "A" "\u13cd" // "Ꮝ" "\u{13cd}" // "Ꮝ" "\u{1f451}" // "👑"
"\x28\u30ce\u25a1\u76ca\u25a1\x29\u30ce\u5f61\u253b\u2501\u253b"
look like? For each codepoint, give the name of its corresponding character.
This shows that the backslash character inside a string is special. It combines with the following character(s) to mean something else. The gory details:
Escape Sequence | Description |
---|---|
\x hh | The character with codepoint hh (exactly 2 hex digits) |
\u hhhh | The character with codepoint hhhh (exactly 4 hex digits) |
\u{ h...h} | The character with codepoint h...h (between 1 and 6 hex digits, maybe more) |
\' | The single quote character (\x27 )
|
\" | The double quote character (\x22 )
|
\n | The linefeed character (\x0a )
|
\t | The tab character (\x09 )
|
\b | The backspace character (\x08 )
|
\r | The carriage return character (\x0d )
|
\f | The formfeed character (\x0c )
|
\v | The vertical tab character (\x0b )
|
\\ | The backslash character itself (\x5c )
|
Some characters, such as combiners and modifers, are combined with adjancent characters to produce what looks like a single printed character. These multi-character sequences (when valid) make up an extended grapheme cluster. Examples:
Character Sequence | Grapheme |
---|---|
U+0052 LATIN CAPITAL LETTER R U+030A COMBINING RING ABOVE | R̊
Latin capital letter R with a ring above
|
U+0BA8 TAMIL LETTER NA U+0BBF TAMIL VOWEL SIGN I | நி
Tamil Ni
|
U+1F6B4 BICYCLIST U+1F3FE EMOJI MODIFIER FITZPATRICK TYPE-5 | 🚴🏾
Bicyclist Type-5
|
U+1F3C4 SURFER U+1F3FC EMOJI MODIFIER FITZPATRICK TYPE-3 U+200D ZERO-WIDTH JOINER U+2640 FEMALE SIGN |
🏄🏼♀
Woman Surfing Type-3 |
U+1F469 WOMAN U+200D ZERO-WIDTH JOINER U+1F52C MICROSCOPE | 👩🔬
Woman Scientist
|
U+1F1F1 REGIONAL INDICATOR SYMBOL LETTER L U+1F1E7 REGIONAL INDICATOR SYMBOL LETTER B | 🇱🇧
Flag for Lebanon
|
Cool, so multiple characters might be needed to make up a single grapheme. So what is a string? A sequence of characters, or a seqence of graphemes? As long as you are just printing strings, or concantenating, or trimming, or checking whether a string is a substring of another, you probably don’t care. But if you are doing anything like asking for the 5th character, or the substring at position 7 of length 10, then it matters. And the answer to this question is:
Neither.
What?! It’s true: JavaScript strings are sequence of code units, also known as char codes. Not characters. Not graphemes. Code units. What?! Well for the most part, code units look like characters. These all seem to make sense:
const s = "dog" const t = "house" s[0] // "d" (indexes start at 0) s[2] // "g" s[3] // undefined (no error if beyond) s[-1] // undefined s.length // 3 const m = "Mississippi" m.includes("sis") // true m.indexOf("i") // 1 m.lastIndexOf("i") // 10 m.indexOf("q") // -1 m.slice(3, 8) // "sissi" m.slice(7, 100) // "ippi" m.slice(5 -3) // "ssi" (In slice, -1, -2,... count back from end) m.slice(8) // "ppi" m.slice(5, 3) // "" (if going backwards, empty) m.slice(-5, -3) // 'si' "hello".padStart(10, "*") // '*****hello' "hello".padEnd(10, "*") // 'hello*****' "hello".padEnd(10, "-^") // 'hello-^-^-'
However, internally, a string is made up of code units, and the index positions and length computations and all that depend on this code unit sequence. So what’s a code unit? Well, if your character is in the range U+0000...U+FFFF (inclusive) it gets one code unit. But if your character has a code point beyond FFFF (these are called astral characters no kidding), it gets two code units. So:
"👍👩🔬$🇺🇾".length // 12
The details are tricky, and you can do a lot without knowing them right away. You can find details here in this document on character sets and character encodings.
If you are a little impatient, here’s the break down for this string length computation (without any technical details):
Grapheme Character Code Point Code Units Thumbs Up THUMBS UP SIGN 128077 55357, 56397 Woman Scientist WOMAN 128105 55357, 56425 ZERO WIDTH JOINER 8205 8205 MICROSCOPE 128300 55357, 56620 Dollar Sign DOLLAR SIGN 36 36 Uruguayan Flag REGIONAL INDICATOR SYMBOL LETTER U 127482 55356, 56826 REGIONAL INDICATOR SYMBOL LETTER Y 127486 55356, 56830
String indexing and length-taking are highly advanced topics
Until you have learned about Unicode, UTF encoding schemes, and Unicode normalization, you shouldn’t really be mucking around with the index positions of individual characters or taking the length of strings.
If you do find yourself interviewing for an internship position and someone asks you a question about indexing and lengths, simply ask your interviewer: “may I ignore astral characters and normalization concerns?” They will probably be impressed. Then you can go ahead and assume one code unit per character.
Certain string operations that involve arrays and regular expressions (search
, match
) will be covered later.
Other obscure operations, such as (1) those dealing with character codes, (2) useless ones like charAt
(c’mon just use []
), and (3) deprecated or non-standard ones, will not be covered at all.
Symbols are unique things. Every time you create a symbol, you get a new thing, different from all other symbols. That doesn’t happen with strings!
$ node > let x = "dog" > let y = "dog" > x === y true > let a = Symbol() > let b = Symbol() > a === b false
Symbols can have descriptions, but they aren’t good for much other than the fact that they show up when you print the symbol. Having the same description does not make two symbols equal!
$ node > let i = Symbol("dog") > let j = Symbol("dog") > i Symbol(dog) > j Symbol(dog) > i === j false
Symbols are kind of an advanced thing
They are just shown here for completeness sake.
A JavaScript object is a value made up by bundling together named properties, each of which has a value. A property is either a non-negative integer or a string or a symbol. A string property does not have to be given in quotes if it is a simple-enough word (that is, with no spaces or other non-word-like characters).
let part = { 'serial number': '367DRT2219873X-785-11P', description: 'air intake manifold', 'unit cost': 29.95 } let person = { name: "Seán O'Brien", country: 'Ireland', birth: { year: 1981, month: 3, day: 17 }, kidNames: {1: 'Ciara', 2: 'Bearach', 3: 'Máiréad', 4: 'Aisling' } }
You can always access properties with square-bracket notation. If the property name is a simple string without spaces or punctuation or other oddities, you can use dot-notation.
person.country // 'Ireland' person['country'] // 'Ireland' person.birth.year // 1981 person.birth['year'] // 1981 person['birth'].year // 1981 person['birth']['year'] // 1981 person.kidNames[4] // 'Aisling' person['kidNames'][4] // 'Aisling' const x = 'country' person[x] // 'Ireland'
Values of non-objects are ”stored directly” while the values of objects are the pointers, not the objects themselves:
This is worth repeating
The value of an object is not a property bundle, but rather a pointer to the property bundle.
The syntax for objects is pretty rich:
const seen = false const metric = 'weight' const location = { lat: 20, lon: 100} const info = { color: 'blue', // quotes for string property names not always needed "name": 'Ali', // quotes are okay even if not needed seen, // a shorthand property, same as seen:seen 21: true, // non-negative integer properties okay [metric]: 88, // a compputed property: the key is "weight" ...location, // this spreads the properties of location into this object }
The fact that object values are actually pointers has major ramifications for assignment and equality testing that you absolutely must understand:
const p = {x: 3, y: 5} // The curly braces CREATE a new object const q = {x: 3, y: 5} // The curly braces CREATE a new object const r = q // Copy the arrow in box q into the box for r p !== q // Because these two arrows point to different things q === r // Because these two arrows point to the same thing
Try diagramming these variables and objects on your own. You should end up with this:
Note that p
and q
refer to different objects, but q
and r
refer to the same object. We say q
and r
are identical as the objects they refer to have the same identity. Now you can tell that p
and q
are not identical, but might they be “equal”? The objects they refer to have the same property values but alas, no, for JavaScript objects, equality and identity are the same.
Like all useful programming languages, JavaScript has a mechanism to express collections of objects that are all related (structurally or behaviorally) to each other, i.e., to make something resembling a new data type.
Every object in JavaScript has a prototype. When looking up properties, the prototype chain is searched. Objects created with the { }
syntax get a built-in, global prototype (details below), but if you use Object.create
, you can set the new object’s prototype to whatever you want:
const protoCircle = {x: 0, y: 0, radius: 1, color: "black"} const c1 = Object.create(protoCircle) c1.x = 4 c1.color = "green" // Now c1.y === 0 and c1.radius === 1
Here $c1$ has two own properties: x
and color
. It has several inherited properties, including y
and radius
from its own prototype, and a bunch of others from its prototype’s prototype.
Prototypes are very useful when creating a bunch of objects that look or behave similarly:
An array is a kind of object with numeric properties. Fortunately, JavaScript provides a convenient syntax for them, too:
let a = [] let b = [8, false, [null, 9]] let days = ["p\u014d\u02bbakahi", "p\u014d\u02bbalua", "p\u014d\u02bbakolu", "p\u014d\u02bbah\u0101", "p\u014d\u02bbalima", "p\u014d\u02bbaono", "l\u0101pule" ]
Here are more arrays, an illustration of mixing objects and arrays (very powerful), and the use of the length property to grow and shrink arrays:
let a = [] let b = [1, 5, 25] let c = new Array() // Lame way to say [], don't write this let d = new Array(4, 5, 6) // Lame way to say [4, 5, 6], don't write this let e = new Array(10) // 10 cells, all undefined. let f = a.length let g = [1, true, [1,2], {x:5, y:6}, "Hi"] let h = {triple: {a:4, b:"dog", c:[1,null]}, 7: "stuff"} let i = [h.triple.a, h[7]] console.log(g[1]) // Prints true console.log(g[2][0]) // Prints 1 console.log(g.length) // Prints 5 g[10] = 100 // Makes g[5] through g[9] undefined console.log(g.length) // Prints 11 g.length = 2 // Now g is just [1, true]
Here are a few array operators:
let s = "A red boat" let a = s.split(" ") // a is ["A", "red", "boat"] let b = [9, 3, 2, 1, 3, 7] let c = b.slice(2, 5) // c is [2, 1, 3] let d = c.concat(a) // d is [2, 1, 3, "A", "red", "boat"] alert(d.join("**")) // Alerts 2**1**3**A**red**boat let a = [] // a is an array of length 0 let b = [3, 5] // b has length 2 b.push(2) // Now b is [3, 5, 2] b.unshift(7) // Now b is [7, 3, 5, 2] a.push(3, 10, 5) // Now a is [3, 10, 5] a.reverse() // Now a is [5, 10, 3] alert(a.pop()) // Alerts 3 and changes a to [5, 10] alert(a.shift()) // Alerts 5 and changes a to [10] b.push(a[0], 1) // b is now [7, 3, 5, 2, 10, 1] b.sort() // b is now [1, 10, 2, 3, 5, 7]
Later, when we see functions, we’ll see the fabulous array operators every
, some
, find
, findIndex
, flat
, forEach
, map
, flatMap
, filter
, reduce
, and reduceRight
.
Sometimes you want to “unpack” an array to use its values individually. Here are some examples:
let a = [2016, 12, 31] new Date(...a) // 2017-01-31T08:00:00.000Z let b = [9, 30] a.push(...b, 12) // a is now [2016, 12, 31, 9, 30, 12] [4, 3, ...b, 6] // [4, 3, 9, 30, 6]
An expression of the form ...x
is called a spread.
When you “unhook” an object from all of the variables that reference it, the object becomes garbage and will (eventually) be picked up by the JavaScript garbage collector. It’s important to make sure you don’t hold on to objects longer than necessary, otherwise memory could fill up with all of the new objects you create and the script might stop working.
Functions and Regular Expressions are objects that have special syntax. We’ll see them later.
JavaScript has a set type in its standard library. To build a set, use this syntax:
let artists = new Set(['Rilo Kiley', 'M.I.A.', 'Death Cab for Cutie', 'Jenny Lewis', 'Santigold', 'The Decemberists', 'Lily Allen', 'The Shins', 'Tegan and Sara', 'Leona Naess', 'Yeah Yeah Yeahs', 'Regina Spektor', 'Postal Service', 'Feist', 'Belle and Sebastian', 'Cold War Kids', 'Silversun Pickups', 'Peter Bjorn and John' ]) artists.add('Jaymay') artists.size // 19 artists.has('Feist') // true
The new
operator is something we will see later.
Also, we’ll see the maps later.
And when doing graphics, we’ll encounter the various typed arrays.
JavaScript has a quirky typeof operator. Give it an expression and it will give you back a string. It doesn’t always do what you expect.
typeof 101.3 // "number" typeof false // "boolean" typeof "dog" // "string" typeof {x:1, y:2} // "object" typeof undefined // "undefined" typeof null // "object" ... WTF?!?!?!? THIS IS TOTALLY STUPID typeof [1, 2, 3] // "object" typeof alert // "function"
Normally, operators expect operands of a certain type. If an operator doesn’t get operands of the right type, it does conversions to get similar arguments of the right types. This means JavaScript is weakly typed. In a strongly typed language, on the other hand, finding a value of the wrong type generates an error.
When a boolean is expected:
undefined
⟶ false
null
⟶ false
0
⟶ false
NaN
⟶ false
true
false
true
true
When a number is expected:
undefined
⟶ NaN
null
⟶ 0
false
⟶ 0
true
⟶ 1
0
0
NaN
.valueOf()
When a string is expected:
undefined
⟶ "undefined"
null
⟶ "null"
false
⟶ "false"
NaN
⟶ "NaN"
toString()
NOTE: The ==
and !=
operators
tell you whether their operands are similar-to or not-similar-to each other. Similarity is not the same as equality. These operators are crap and you should never use them. If you want to determine equality, use use ===
and !==
instead. They tell you whether $x$ and $y$ have the same value and the same type.
A value that is false or would be converted to false is called falsy. All other values are truthy. (I did not make up those names.)
Somewhat related: the values undefined
and null
are called nullish.
Sometimes you will want to do a type conversion yourself. This is a classic example:
let x = prompt("Enter a number") let y = prompt("Enter another number") alert(x + y) // Oops! Concatenation, not arithmetic addition!
There are a few ways to do the string-to-number conversion explicitly:
"5.25" * 1
"5.25" / 1
"5.25" - 0
+"5.25"
Number("5.25")
Advice: Don’t use parseInt
or parseFloat
.
In general:
+x
!!x
`${x}`
We’ve covered:
typeof