16 The JavaScript Type System
JavaScript supports a number of data types including number, string, boolean, object, and undefined (a special type with only one value: undefined). You can recover a string representation of the type of a value using the global typeof operator.
JavaScript is both dynamically typed and weakly typed. These two features of the JavaScript type system are important and distinct from one another. Not all dynamically typed languages are weakly typed, and not all weakly typed languages are dynamically typed.
Dynamic Typing
Variables are containers that store values. In JavaScript, variables are dynamically typed. This means that although values have fixed data types, variables do not. This is in contrast to statically typed languages in which variables are declared with a type and are then restricted to only hold values the declared type. In JavaScript, you do not declare a type for a variable and you can change the type of the value it holds as the program runs.
JavaScript variables are declared with the keyword let, like this:
let x; // declares x and assigns undefined let y = 3; // declares y and assigns 3 to it
Do it Yourself
Try the statements below in the JavaScript console of your browser.
let x = 100; typeof x; console.log(x); x = "I'm a string now"; typeof x; console.log(x); x = 45.3; typeof x; console.log(x); x = "$"+x; typeof x; console.log(x);
(Here’s a tip: To reset the console and make it forget all the variables you have defined in this session, just hit the refresh button on your browser.)
Connections
In older JavaScript code, you will see variables declared using the var keyword. This is outdated and it is now considered best practice to use the let keyword. One difference between the two keywords is the scope of the variable.
- If you declare with the var keyword, variables have function level scope. A var variable is either local to a function or it is global, depending on where it’s declared. This is the same as variable scope in Python.
- If you declare with the let keyword, the variable will have block level scope, like C and Java. This means the variable will be local to the code block in which it is declared. If you declare a let variable inside a while or for loop, it will not be accessible outside that loop.
You will also often see the const keyword used instead of let. You can use this keyword to declare and initialize a variable if you are planning to never change the value of that variable. Variables declared with const also have block level scope.
Do it Yourself
Here’s a loop for counting to 10. You can try it out by adding a script element to a web page, or by pasting or typing it into a JavaScript console using shift-enter to separate the lines.
for(let i = 1; i <= 10; i++)
console.log(i);
Here’s another one to try.
let n = prompt("Enter a number"); if (n < 1000) alert("that was a small number"); else alert("that was a big number");
Note that in this case the variable n contains a string, but JavaScript still allows the comparison to happen. This is because JavaScript is not only dynamically typed, but also weakly typed. More on this in the next section.
Gotcha: Scope
Actually, you can assign to a variable without declaring it in JavaScript. But when you do that, the variable becomes global, no matter where you introduced it. This is considered very bad practice (though it can be useful for debugging sometimes). Always declare your variables!
Flexible Functions
You can declare a function in JavaScript using the function keyword and return a value using the return keyword. The syntax should be quite similar to what you have seen in other languages. Because of dynamic typing, you don’t need to specify types for parameters or return values. If you do not use a return statement, the function will return the value undefined.
function function_name(comma, separated, parameter, list) { // code goes here }
Do it Yourself
Paste the following into a JavaScript console or into a script element on an HTML page and then load the page:
function sayHi(name) { return "Hello, " + name + "!"; }
Call the function from the JavaScript console, sending your name as an argument to the function. This argument will become the value of the name parameter.
sayHi("Sam");
You can send the return value of one function as an argument to another function, like this:
alert( sayHi("Sam") );
…and this:
alert( sayHi( prompt("What's your name?") ) );
JavaScript functions are also very flexible. In fact, JavaScript is so allergic to run-time errors that you don’t have to get the argument list correct in a function call. As long as you get the function name right, you can call it with any combination of arguments. If there are extra arguments, they will be discarded. If there are too few arguments, some parameters will get the value undefined.
Do it Yourself
All the function calls below are legal. Try them in the console, using the function definition from the previous DIY box.
sayHi("Sam"); sayHi(); sayHi("Sam", "Scott");
What happens in each case? Does it make sense?
You can also specify default values for parameters, and when you call a function you can pass arguments to a function by position (first argument goes to first parameter) or by keyword (i.e., parameter name). These flexible mechanism allow a function to be called in many different ways.
Do it Yourself
The function below can be called with 1, 2, or 3 parameters. Enter it in a script element of a web page or in the console, then try the function calls below. Do the return values make sense?
function spam(a, b=-1, c=-2) { return a + b + c; } spam(1); spam(1, 1); spam(1, 2, 3);
Now try the following. Do the return values still make sense?
spam(10, b=5, c=5); spam(a=2); spam(c=3, a=6);
Connections
Python functions are a lot like JavaScript functions in that they require no type specifications, support keyword arguments and default parameter values, and return the value None (similar to undefined) if there is no return statement. But unlike JavaScript, Python will raise an error if the argument list is incorrect when you call a function.
Java methods and C functions are more strict. They require parameter and return types and do not support default parameter values or keyword arguments, although Java method overloading can be used to accomplish something similar.
Weak Typing
JavaScript is also a very weakly typed language. This is not the same as being dynamically typed. Weak vs. strong typing is a matter of degree rather than an either/or. The more weakly typed a language is, the fewer syntax or run-time errors you will get for mixing types in an expression or passing the wrong type to a function. You get logic errors instead, which are much harder to debug. Weak typing gets you flexibility and convenience, but at a price.
Connections
Python is dynamically typed and strongly typed. You don’t have to declare variable types, but mixing types in an expression often causes an error. For example, concatenating an integer and a string (“age: ” + 55) will cause a run-time error.
C is statically typed but quite weakly typed. For example, you can assign a float to an integer variable with no errors or warnings (it will drop the fractional portion).
Java is also statically typed. It’s more strongly typed than C but less strongly typed than Python. Unlike C you can’t assign a float to an integer, but unlike Python, you can concatenate an integer to a string.
JavaScript is both dynamically typed and about as weakly typed as a language could possibly be.
Weak typing is a form of aggressive implicit type conversion. When asked to perform an operation on two values, JavaScript will always find a way to perform that operation. It will convert one or both of the values into a type it can work with and then compute the result. Sometimes this works in the way you would want it to, and sometimes it doesn’t.
Do it Yourself
Try the following statements in a JavaScript console, then check the type and value of the result. Is it what you expected? Which operand got converted in each case?
let a = "50" + 10; let b = "50" * 10; let c = false - 2; let d = "hi" + "there"; let e = "hi" - "there";
Compare this to another language you know. Would these statements work, or would they trigger syntax or run-time errors? For those that would work, what would the result be?
Similarly to the above, Boolean operators (i.e. the expressions that evaluate to true or false to control if statements and loops) can be used to compare any two values regardless of whether or not their types match. Again, JavaScript will convert one of the two operators and then perform the operation.
Do it Yourself
Try the following in the Chrome console. Do you get errors? Do the results make sense?
let x = "hi"; x == 45.3 x >= 45.3 x == "hi"
If a String contains a representation of a number, it can be compared to integers and floats…
Do it Yourself
Try the following in a JavaScript console:
let y = "50"; x = 50; y == x
This comparison is true, even though y is a String and x is a Number.
Because of JavaScript’s weak approach to types, there is a special Boolean operator === that returns true if two values are equal and also of the same type – this is known as being “exactly equal” or “identical“. Similarly, !== is a “not exactly equal to” operator that respects the types of the operands. See JS Comparison Operators on w3schools for more info.
Do it Yourself
Try the statements below using the same x and y variables from the last DIY box. Try to predict the result before you hit enter.
y === x; y === "50"; y !== x; y !== "50"
Explain to yourself what is happening in each case, and why each statement is either true or false.
Because of the confusion that implicit type casting can cause, it is often considered best practice to use === and !== whenever possible and try to avoid using == and !=.
Connections
Boolean Operators. JavaScript adds two new Boolean operators, === and !== for “is exactly equal to” and “is not exactly equal to”. It’s often considered best practice to use these operators instead of == and !=.
String Equality. In Java you write x.equals(“hi”). In C you use strcmp(x, “hi”) == 0. In Python, you write x == “hi”. In JavaScript use x === “hi”.
Explicit Type Conversion
Sometimes you really want to treat a String value as a Number. For example, the prompt function always returns a String, which works fine in most cases, but suppose we are asking the user to enter a number and then we want to add 1 to it. The code in the DIY box below won’t do it.
Do it Yourself
Try this in a <script> element or a Chrome console window. What is going wrong?
let y=prompt("Enter a number"); y = y + 1; alert(y);
The example in the DIY box above doesn’t do what we want because y is a string. So JavaScript treats the + operator as concatenation. In this case, we need to use explicit type conversion to force the type we want.
JavaScript has built in global parseInt and parseFloat functions for this very purpose. (Unlike alert, prompt and confirm which belong to the window object, parseInt and parseFloat are global functions. They do not belong to any object in the system. But this doesn’t usually make a difference in practice.)
Connections
In Java and C explicit conversion can often be done with a casting operator. For example (int)23.5 converts the float 23.5 to the int 23.
In Python, you can often use a type function for casting. For example int(23.5) converts the double 23.5 to the int 23 and float(“23.5”) converts the string to a float.
JavaScript is similar to Python but the conversion functions are called parseInt and parseFloat.
Do it Yourself
Here’s the fix for the last example. Verify that it works and that you understand why it works before moving on.
let y=prompt("Enter a number"); y = parseInt(y) + 1; alert(y);
But what if parseInt or parseFloat fail because the user typed something that can’t be interpreted as a number? In that case, they return the special value NaN (Not a Number) which can be detected with the global Boolean function isNaN.
Do it Yourself
Try this out in a script element or a JavaScript console. Can you explain what each line is doing?
let n; do { n = parseFloat( prompt("Enter a number") ); } while (isNaN(n)); n = n + 1; alert(n);
Gotcha: Bad Values
JavaScript always tries to do the best it can to avoid run-time errors. Weak typing is a big part of this – no matter what type you send to a built-in function or expression, JavaScript will do the necessary conversions and keep going.
Similarly, parseInt(“50.5”) will work and return 50 even though the string contains a float and not contain an integer. The expression parseInt(“50×6”) will also return 50. The parser just stops at the x and returns what it has so far. This can be either a blessing or a curse depending on what you are trying to do.
Gotcha: Assignment vs. Comparison
Even seasoned programmers make the following mistake from time to time:
let guess = 5; if (guess = 6) console.log("You guessed correctly!"); else console.log("Sorry, try again.");
In Java and Python, the above would be a syntax error because the programmer used = instead of == in the if statement. But JavaScript prints the success message. In most C-like languages, assignment (=) is an operator just like +, *, etc. This means that assignment statements return a value and can be embedded within other statements.
In the original if statement above, guess gets assigned 6, and the value 6 gets placed in the if statement. So it becomes equivalent to:
if (6) …
<exercises chapter=”types”>
- Write a script on a web page that presents the user with an arithmetic problem (for example what is 23 + -4?) and asks for the answer using a prompt. Then give them feedback using an alert box.
- Extend the script so that it generates the integers for the arithmetic problem randomly using Math.floor(Math.random() * 201) – 100. Keep presenting the question until they get it right. Then give them a success message in an alert box. For an explanation of random number generation see JS Random on W3Schools.
- Extend the script so that it asks the user if they would like another question using a confirm dialog and gives them another question if they press “OK”. If they press “Cancel”, the page should finish loading and display a goodbye message.
- Write a JavaScript program in a .js file and load it into a web page using a script element. The script file should define a function with one parameter n. If the parameter is an even integer, it should say “Hello, World!” n times in the console. Otherwise, it should say “Goodbye, World!” once. Test your function by having it execute 5 times in a loop, passing the parameters 1, 2, 3, 4, 5 as the arguments. (Note that if your function says “Hello, World!” 4 times in a row, the console will show the output only once, with a “4” next to it to save space.)
- Load whatswrong.html file from the Full Stack Example Pack, run it to see what it does, then view the page source and read the instructions in the comment header of the file. Explain exactly where the error or errors happen, and fix them.
- Further fix the code from the last question so that it recovers gracefully when the user types a bad number.
</exercises>