Graduate Program KB

Types and Coercion in JavaScript


Primitive Types

  • The primitive types are:
    • undefined
    • string
    • number
    • boolean
    • object
      • array: use array.isArray() to test if an array is an array.
      • function
      • null: A historical bug... important to know that if you're testing for null, you may be getting an object type that is actually null.
    • symbol
    • bigint (future)
  • In JavaScript variables don't have types, their values have types... because JS is a dynamically typed language.
  • typeof returns strings! Great for checking to see if a value is what you expect.
  • undefined is different to undeclared... undeclared means it has never been created in any scope whereas undefined means that the variable has no value.

Special values

  • NaN:
    • it has a type of number!!!
    • represents an invalid number.
    • Can occur when trying to convert a string that can't be converted to number to a number.
    • Or subtracting a string from a number.
    • NaN don't have the identity property, NaN is the only value that is never equal to itself.
    • isNaN() will coerce whatever is in the brackets to a number then tell you if its a NaN, this results it returning true for strings which is wrong.
    • Number.isNaN() does the same as above but without coercion.
  • Negative Zero:
    • Only way to test if you have one is with Object.is(value, -0).
    • Useful for when you are using signs to distinguish direction.

Fundamental Objects

  • Use the new keyword when creating the following objects:
    • Object()
    • Array()
    • Function()
    • Date()
    • RegExp()
    • Error()
  • Don't use the following with new, they should be used as functions for coercion:
    • String()
    • Number()
    • Boolean()

Abstract Operations

Specification

  • A key thing to keep in mind with these operations is to check the spec and see what other coercion is going on.
  • ToPrimitive(hint): hint tells the operation what primitive you'd like (string, number, or no input... function does the best it can)
    • hint = 'number' -> valueOf() then it will try toString(), if neither work we will get an error.
    • hint = 'string' -> toString() then it will try valueOf(), if neither work we will get an error.
  • ToString(): takes any value and gives us the representation of that value in string form.
    • avoid using this on Arrays due to the amount of edge cases
    • null and undefined are just ignored in arrays
    • for objects it will just return "[object Object]" Unless you overwrite it with:
    {toString(){return "My new object string tag";}}
    
  • ToNumber(): converts a string to number.
    • "" = 0
    • "009" = 9
    • "0." = 0
    • ".0" = 0
    • "." = NaN
    • "0xaf" = 175 (octal)
    • ToNumber(Array):
      • [""] 0
      • ["0"] 0
      • ["-0"] -0
      • [null] 0
      • [undefined] 0
      • [1,2,3] NaN
      • [[[[]]]] 0
    • ToNumber(Object): - We can invoke the ToNumber operation with + - {} NaN
      • {valueOf{return 3;}} 3 -ToBoolean: Is the value falsy or not? - If arg is boolean, return it - If arg is null, undefined, NaN, 0, -0, return false - Else return true

Coercion

    • Overloading
      • If either one of the operands to the + operator is a string, the + will then perform string concatenation.
      • Needs 2 numbers for + to do addition
  • Coercion happens implicitly everywhere

    `There are ${number} chickens here!`
    // Converts the number, which is of type number, to a string.
    
  • The minus operator isn't overloaded like + and this leads to it doing coercion when passed other types... it implicitly calls ToNumber()

  • Boxing: Allows accessing properties of primitive values. It essentially gets primitive values and implicitly coerces it to an object so you can access things like "length"

    var myString = "Hey mate";
    myString.length;
    
  • Coercion Corner Cases:

    Number( "" );                   // 0
    Number("  \t\n");               // 0
    Number( null );                 // 0
    Number( undefined );            // NaN
    Number( [] );                   // 0
    Number( [1,2,3] );              // NaN
    Number( [null] );               // 0
    Number( [undefined] );          // 0
    Number( {} );                   // NaN
    
    String( -0 );                   // "0"
    String( null );                 // "null"
    String( undefined );            // "undefined"
    String( [null] );               // ""
    String( [undefined] );          // ""
    
    Boolean( new Boolean(false) );  // true
    
    • It is not wise to implicitly coerce booleans to numbers. Look at the following example:
    1 < 2 < 3
    (1 < 2) < 3
    true < 3
    1 < 3
    // Accidentally true, not behaving as expected at face value.
    // Look at this one
    3 > 2 > 1
    (3 > 2) > 1
    true > 1
    1 > 1
    // This is now false... OOPS
    

Equality

  • ==: Abstract Equality Comparison
    • If types are the same: ===
    • If null or undefined: equal
    • If non-primitive: convert with ToPrimitive
    • Prefers to use ToNumber!
    • Specification
    • AVOID:
      • using with 0, "" or " "
      • using it with non-primitives
      • use it with true or false (let ToBoolean happen implicitly or just use ===)
      • using it at all if you don't know the types.
    • The double equals is preferred over the triple equals IF APPLICABLE.
  • ===: Strict Equality Comparison
    • If type of x is different to type of y return false
    • If x is a number then:
      • If x is NaN return false
      • If y is NaN return false
      • If x is the same number value as y return true
      • If x is +0 and y is -0 return true
      • If x is -0 and y is +0 return true
      • return false
    • Return the SameValueNonNumber(x,y)
    • If you know the types will be different, === would be broken... it'll always fail The Key here is that == will allow coercion when types don't match. Whereas === will not allow coercion. If you know the types, == is the more sensible choice If possible, you should always try to structure your code so you know the types

Key Takeaways

  • Useful: when the reader is focused on what's important
  • Dangerous: when the reader can't tell what will happens
  • Better: when the reader understands the code

Return