-
Here is the 7 built in types in js:
- null
- undefined
- boolean
- number
- string
- object
- symbol /* Added in CS6*/
-
the 1st 5 types are so called : "primitive values", primitive values in js cannot be changed or altered in anyway. for example if we have a number 42, we want to apply methods to it, we need to change it to object then change the object.
-
the only type of value that can be altered is object
-
typeof undefined; //"undefined" primitive value typeof true; //"boolean" primitive value typeof 42; //"number" primitive value typeof "42"; //"string" primitive value typeof { life: 42 }; //"object" typeof null; //"object" !!! primitive value typeof function a(){/*...*/} //"function" // added in ES6! typeof Symbol(); //"symbol"
-
typeof
operator returns your result instring
-
typeof null; //"object"
this original bug in JS has persisted for nearly 20 years. Just remember this special case. -
function a()
in js is not a stand alone type of value, understand it as a "sub-type" of object. -
Again, just to reemphasize my last point, functions are just objects in JS.
-
array in functions are object as well.
typeof [1,2,3]; //"object"
-
In js, variables don't have types. the values that got assign to it has types.
-
the engine does not insist that a variable always hold values of the same initial type that it starts out with. (a.k.a coercion can happen)
-
Variables that do not have a current value will be assigned to the value of
undefined
-
var a; typeof a; //undefined
-
-
undefined
is different than undeclared. -
undefined
is the initial state of a variable before you assign any value into it-
Confusing browser error message 1: (below snippet)
-
var a; a;//undefined b;//Reference Error: b is not defined (this is the error message chrome will give you, however b is not undefined, b is not being declared at all. Do not get confused with this.)
-
Confusing browser error message 2: (below snippet)
-
var a; typeof a; //undefined typeof b; //undefined //again, b is not undefined, b is un-declared....
-
the
typeof
operator returns "undefined" even for a un-declared value, this is a special safety guard in the behavior oftypeof
-
-
A few ways of using
typeof
operator.-
global variable (''DEBUG'') check:
-
// oops, this would throw an error! if (DEBUG) { console.log( "Debugging is starting" ); } // this is a safe existence check if (typeof DEBUG !== "undefined") { console.log( "Debugging is starting" ); }
-
-
feature check for built-in API
-
if (typeof atob === "undefined") { atob = function() { /*..*/ }; }
-
-
another ways of doing all these checks against global variables but without the safety guard feature of
typeof
are:-
if (window.DEBUG) { // .. } if (!window.atob) { // .. }
-
-
check if the program you are working on has defined a certain variable, so that you can use it
-
function doSomethingCool() { var helper = (typeof FeatureXYZ !== "undefined") ? FeatureXYZ : function() { /*.. default feature ..*/ }; var val = helper(); // .. }
-
if someone includes this utiliti into their module/progam, it safely checks if they've defined the FeatureXYZ or not
-
// an IIFE (see "Immediately Invoked Function Expressions" // discussion in the *Scope & Closures* title of this series) (function(){ function FeatureXYZ() { /*.. my XYZ feature ..*/ } // include `doSomethingCool(..)` function doSomethingCool() { var helper = (typeof FeatureXYZ !== "undefined") ? FeatureXYZ : function() { /*.. default feature ..*/ }; var val = helper(); // .. } doSomethingCool(); })();
-
-
or you can use "dependency injection method"
-
function doSomethingCool(FeatureXYZ) { var helper = FeatureXYZ || function() { /*.. default feature ..*/ }; var val = helper(); // .. }
-
-
-
-
-
array container can contain any types of value, string, number, object even another array
-
var a = [ 1, "2", [3] ]; a.length; // 3 a[0] === 1; // true a[2][0] === 3; // true
-
-
you dont need to presize array
-
var a = [ ]; a.length; // 0 a[0] = 1; a[1] = "2"; a[2] = [ 3 ]; a.length; // 3
-
-
using
delete
on an array will createundefined
holes in array and the length of the array will not be changed automatically-
var array = [0,1,2,3]; delete array[0]; array.length; //4 so this array pretty much becomes: //[undefined,1,2,3]
-
-
do not create arrays that have empty slots in them
-
var a = []; a[0]=0; a[2]=2; a.length; //3 //bad practice and can lead to more confusion, avoid this
-
-
arrays are objects
-
arrays can have string keys and properties added to them, just like real object (but this is not recommended and should be avoided at all cost) a.k.a DO NOT PUT NUMBER STRINGS INTO AN ARRAY AS AN ELEMENT
-
var a = [ ]; a[0] = 1; a["foobar"] = 2; a.length; // 1 a["foobar"]; // 2 a.foobar; // 2
-
-
the real gotcha
-
if a
string
value intended as a key can be coerced to a standard base-10number
, then it is assumed that you wanted to use it as anumber
index rather than as astring
key! -
var a = [ ]; a["13"] = 42; a.length; // 14
-
-
convert "array- like" values into a true array and call array utilities on them
-
function foo() { var arr = Array.prototype.slice.call( arguments ); arr.push( "bam" ); console.log( arr ); } foo( "bar", "baz" ); // ["bar","baz","bam"]
-
or you can use
Array.from(..)
in ES6. -
... var arr = Array.from( arguments ); ...
-
-
Strings and arrays are different.
-
Strings are primitive value.
-
Arrays are object.
-
Strings are not immutable.
-
Arrays are mutable.
-
a[1] = "O"; b[1] = "O"; a; // "foo" b; // ["f","O","o"]
-
-
Both strings and arrays have :
length
propertyindexOf()
methodconcat()
method
-
var a = "foo"; var b = ["f","o","o"]; a.length; // 3 b.length; // 3 a.indexOf( "o" ); // 1 b.indexOf( "o" ); // 1 var c = a.concat( "bar" ); // "foobar" var d = b.concat( ["b","a","r"] ); // ["f","o","o","b","a","r"] a === c; // false b === d; // false a; // "foo" b; // ["f","o","o"]
-
Strings cannot be (directly) altered when it comes to its individual elements. It can only be changed as a whole. Arrays can.
-
c = a.toUpperCase(); a === c; // false a; // "foo" c; // "FOO" b.push( "!" ); b; // ["f","O","o","!"]
-
-
If you want to alter string's down to each elements level, you can use this method:
-
a.join; // undefined a.map; // undefined var c = Array.prototype.join.call( a, "-" ); var d = Array.prototype.map.call( a, function(v){ return v.toUpperCase() + "."; } ).join( "" ); c; // "f-o-o" d; // "F.O.O."
-
-
Here is one of the ways to reverse a string.
-
The split() method splits a String object into an array of string by separating the string into sub strings.
-
The reverse() method reverses an array in place. The first array element becomes the last and the last becomes the first.
-
The join() method joins all elements of an array into a string.
-
function reverseString(str) { return str.split("").reverse().join(""); } reverseString("hello");
-
-
Or, you can strore your strings as arrays, so you can always call
join("")
methods on them.
-
JS has only ONE numeric type:
number
-
in js, if a number does not have a decimal value or its decimal value is 0, we treat it as an "integer"
- 42.0 is an integer in js
-
Syntax
-
a = 42;
-
a = 0.42;
-
a = .42; // 0 can be neglected
-
a = 42.0;
-
a = 42. //samehere
-
var a = 5E10; a; // 50000000000 a.toExponential(); // "5e+10" var b = a * a; b; // 2.5e+21 var c = 1 / a; c; // 2e-11
-
number
can be boxed intoNumber()
object wrapper. so number values can access methods that are built into theNumber.prototype()
for example:a.toFixed()
-
a.toFixed()
will output a string that has a same value of the number :-
var a = 42.709; a.toFixed(1); //"42.7"
-
// invalid syntax: 42.toFixed( 3 ); // SyntaxError // these are all valid: (42).toFixed( 3 ); // "42.000" 0.42.toFixed( 3 ); // "0.420" 42..toFixed( 3 ); // "42.000" 42 .toFixed(3); // "42.000" technically valid, but should be avoided cos it is confusing
-
-
-
Small Decimal values
-
for any languages that use IEEE 754 standard , (js included)
- 0.1+0.2===0.3; //false
-
Use
Number.EPSILON
to round off small errors.-
function numbersCloseEnoughToEqual(n1,n2) { return Math.abs( n1 - n2 ) < Number.EPSILON; } var a = 0.1 + 0.2; var b = 0.3; numbersCloseEnoughToEqual( a, b ); // true numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false
-
-
Polyfill for pre-ES6:
-
if (!Number.EPSILON) { Number.EPSILON = Math.pow(2,-52); }
-
-
-
Safe Integer Ranges
- maximum integer that exist in js: 2^53 - 1 a.k.a 9007199254740991 a.k.a
Number.MAX_SAFE_INTEGER
- minimum integer that exist in js : -9007199254740991 a.k.a
Number.MIN_SAFE_INTEGER
- The main way that JS programs are confronted with dealing with such large numbers is when dealing with 64-bit IDs from databases, etc. 64-bit numbers cannot be represented accurately with the
number
type, so must be stored in (and transmitted to/from) JavaScript usingstring
representation.
- maximum integer that exist in js: 2^53 - 1 a.k.a 9007199254740991 a.k.a
-
Testing for Integers
-
test whether a number is an interger :
-
Number.isInteger( 42 ); // true Number.isInteger( 42.000 ); // true Number.isInteger( 42.3 ); // false
-
-
polyfill for pre-ES6
-
if (!Number.isInteger) { Number.isInteger = function(num) { return typeof num == "number" && num % 1 == 0; }; }
-
-
test whether a number is a safe integer
-
Number.isSafeInteger( Number.MAX_SAFE_INTEGER ); // true Number.isSafeInteger( Math.pow( 2, 53 ) ); // false Number.isSafeInteger( Math.pow( 2, 53 ) - 1 ); // true
-
-
polyfill for pre-ES6
-
if (!Number.isSafeInteger) { Number.isSafeInteger = function(num) { return Number.isInteger( num ) && Math.abs( num ) <= Number.MAX_SAFE_INTEGER; }; }
-
-
-
32-bit (Singed) Integers
- to force a number value in
a
to a 32-bit signed interger value, usea|0
- to force a number value in
-
Special Values
-
Non-valuable Values
undefined
value: undefined, type: undefinednull
value : null, type: null
-
Undefined
-
dont overwrite
undefined
no matter under what kind of circunstances -
another way to create
undefined
value is usingvoid
operator-
var a = 42; console.log( void a, a ); // undefined 42
-
other ways of using void
-
-
-
Special Numbers
-
using one number one non-number under one mathematic operation will give you
NaN
value-
var a = 2 / "foo"; // NaN typeof a === "number"; // true
-
-
NaN
is an invalid number, an failed number, an bad number, but it is still a number. (see above snippet) -
NaN
is never equals to another NaN, and it is never equals to itself-
var a = 2 / "foo"; a == NaN; // false a === NaN; // false
-
-
how to check whether a number is
NaN
or not?-
if (!Number.isNaN) { Number.isNaN = function(n) { return n !== n; }; }
-
pre-ES6 fix
-
//polyfill 1
if (!Number.isNaN) { Number.isNaN = function(n) { return ( typeof n === "number" && window.isNaN( n ) ); }; }
var a = 2 / "foo"; var b = "foo";
Number.isNaN( a ); // true Number.isNaN( b ); // false -- phew!
-
//polyfill 2 if (!Number.isNaN) { Number.isNaN = function(n) { return n !== n; }; }
-
-
in js numbers that are divided by 0 will give you this result :
-
var a = 1/0; //Infinity var b = -1/0; //-Infinity
-
-Infinity
(akaNumber.NEGATIVE_INFINITY
) results from a divide-by-zero where either (but not both!) of the divide operands is negative. -
According to the specification, if an operation like addition results in a value that's too big to represent, the IEEE 754 "round-to-nearest" mode specifies what the result should be. So, in a crude sense,
Number.MAX_VALUE + Math.pow( 2, 969 )
is closer toNumber.MAX_VALUE
than toInfinity
, so it "rounds down," whereasNumber.MAX_VALUE + Math.pow( 2, 970 )
is closer toInfinity
so it "rounds up".
-
-
in js there is a +0 and a -0.
-
additive and subtractive operation cannot give you -0.
-
**converting -0 to string will give you ''0" **
-
var a = 0 / -3; // (some browser) consoles at least get it right a; // -0 // but the spec insists on lying to you! a.toString(); // "0" a + ""; // "0" String( a ); // "0" // strangely, even JSON gets in on the deception JSON.stringify( a ); // "0"
-
-
converting from string to number will always give you -0
-
+"-0"; // -0 Number( "-0" ); // -0 JSON.parse( "-0" ); // -0
-
-
comparison operator is configured to lie to you as well (tells you 0 and -0 equal to each other)
-
var a = 0; var b = 0 / -3; a == b; // true -0 == 0; // true a === b; // true !!!!!! -0 === 0; // true !!!!!! 0 > -0; // false a > b; // false
-
-
how to distinguish -0 from 0?
-
function isNegZero(n) { n = Number( n ); return (n === 0) && (1 / n === -Infinity); } isNegZero( -0 ); // true isNegZero( 0 / -3 ); // true isNegZero( 0 ); // false
-
-
-
Special Equality
-
in ES6 we use
Object.is()
to test whether a value isNaN
or-0
-
var a = 2 / "foo"; var b = -3 * 0; Object.is( a, NaN ); // true Object.is( b, -0 ); // true Object.is( b, 0 ); // false
-
pre-ES6 polyfill
-
if (!Object.is) { Object.is = function(v1, v2) { // test for `-0` if (v1 === 0 && v2 === 0) { return 1 / v1 === 1 / v2; } // test for `NaN` if (v1 !== v1) { return v2 !== v2; } // everything else return v1 === v2; }; }
-
-
-
Value VS Reference
-
In JavaScript, there are no pointers, and references work a bit differently. You cannot have a reference from one JS variable to another variable. That's just not possible.
-
A reference in JS points at a (shared) value, so if you have 10 different references, they are all always distinct references to a single shared value; none of them are references/pointers to each other.
-
Moreover, in JavaScript, there are no syntactic hints that control value vs. reference assignment/passing. Instead, the typeof the value solely controls whether that value will be assigned by value-copy or by reference-copy.
-
primitive values are passed by value copy
-
objects (including arrays ,functions) are passed by reference copy
-
var a = 2; var b = a; // `b` is always a copy of the value in `a` b++; a; // 2 b; // 3
-
b is assigned another copy of value 2. a is untouched
-
var c = [1,2,3]; var d = c; // `d` is a reference to the shared `[1,2,3]` value d.push( 4 ); c; // [1,2,3,4] d; // [1,2,3,4]
-
since we changed the object value by adding 4 at the end of it, all the references changed accordingly
-
var a = [1,2,3]; var b = a; a; // [1,2,3] b; // [1,2,3] // later b = [4,5,6]; a; // [1,2,3] b; // [4,5,6]
-
b=a
means we assigned another copy of [1,2,3] to b. so now b is [1,2,3] -
b=[4,5,6]
b is assigned a reference to a different array of values. -
-
-
-
**Here are some of my own understanding about the above snippet: **
-
var a = 2; var b = a; //in javascript, when you assign a number to a variable, the number does not belong to the variable. //the number itself is a "seperate" primitive value, what got assigned to a, is a copy of that value. and this copy is an object. //so,in this snippet there are actually 3 value of 2. //1st: the primitive value 2 //2nd: a copy of 2 that got assigned to a (var a = 2;) //3rd: another copy of 2 that got assigned to b (var b = a;) b++; //because you are value copying primitive number 2, so changing b does not affect the value of a
-
var c = [1,2,3]; var d = c; // `d` is a reference to the shared `[1,2,3]` value d.push( 4 ); c; // [1,2,3,4] d; // [1,2,3,4] //in this snippet, both c and d are references of the same array [1,2,3] therefore when one is changed, all is changed
-
var a = [1,2,3]; var b = a; a; // [1,2,3] b; // [1,2,3] // later b = [4,5,6]; a; // [1,2,3] b; // [4,5,6] //b=a created another reference of [1,2,3] //b=[4,5,6] , b is assigned another reference , a new reference that is [4,5,6]
-
function foo(x) { x.push( 4 ); x; // [1,2,3,4] // later x = [4,5,6]; x.push( 7 ); x; // [4,5,6,7] } var a = [1,2,3]; foo( a ); a; // [1,2,3,4] not [4,5,6,7] //in the above snippet there was a moment that both a and x are pointing at the same value, [1,2,3,4] but after x=[4,5,6] , the value in a does not change, because we are reference copying the arrays here. so , changing the value of one does not affect another
-
function foo(x) { x.push( 4 ); x; // [1,2,3,4] // later x.length = 0; // empty existing array in-place x.push( 4, 5, 6, 7 ); x; // [4,5,6,7] } var a = [1,2,3]; foo( a ); a; // [4,5,6,7] not [1,2,3,4] //in the snippet above here's what happened: // 1. var a got assigned to a reference of [1,2,3] // 2. x got assigned to a reference of [1,2,3] // 3. x.push(4) makes the array changes from [1,2,3] to [1,2,3,4] // 4. x.length=0 makes the array becomes empty // 5. empty array becomes [4,5,6,7] // 6. throughout the whole procedure there is only one array existed. x.lengh=0 and x.push(4,5,6,7) did not create any new array. a and x are both references of the same array, so both of them are [4,5,6,7]
-
function foo(x){ x.push(4); x; x=[]; x.push(4,5,6,7); x; //[4,5,6,7] } var a = [1,2,3]; foo(a); a; //[1,2,3,4] // in the snippet above x = [] created a new reference and assigned it to x // so in this snippet there are two values, and a is a reference of [1,2,3,4] and x is a reference of [] //then x becomes [4,5,6,7]
-
-
-
When coding if you decided to use explicit coercion, the more explicit your code is, the easier it is to read later either by yourself or other people.
-
Few ways of corerce strings to numbers.
-
var a = 42; var b = String( a ); var c = "3.14"; var d = Number( c ); b; // "42" d; // 3.14
-
The above way was the more direct way and you should use this whenver you want to explicitly coerce your value.
-
var a = 42; var b = a.toString(); var c = "3.14"; var d = +c; b; // "42" d; // 3.14
-
toString()
cannot be called on a primitive value so JS has to box 42 in an object wrapper then convert it to string. -
You should straight up avoid using the
var d = +c
method. To avoid confusions like:-
var c = "3.14"; var d = 5+ +c; d; // 8.14
-
1 + - + + + - + 1; // 2
-
-
-
Date to number.
-
var d = new Date( "Mon, 18 Aug 2014 08:53:06 CDT" ); +d; // 1408369986000
-
var timestamp = +new Date();
-
// Non coercion verion 1 var timestamp = new Date().getTime(); // var timestamp = (new Date()).getTime(); // var timestamp = (new Date).getTime();
-
// Non coercion version 2 (Best version) var timestamp = Date.now();
-
// How to polyfill Date.now() into older browser if (!Date.now) { Date.now = function() { return +new Date(); }; }
-
-
Curious case of
~
. (page 75-78). -
Parsing Numeric Strings :
-
var a = "42"; var b = "42px"; Number( a ); // 42 parseInt( a ); // 42 Number( b ); // NaN parseInt( b ); // 42
-
Parsing is not as same as coercion.
-
Parse a
string
as anumber
when you don't know/care what other non-numeric characters there may be on the right-hand side. -
Coerce a
string
(to anumber
) when the only acceptable values are numeric and something like"42px"
should be rejected as anumber
. -
parseInt()
works on string value. -
in a non-string value is being passed in, it will be coerced to string 1st.
-
Never use a
parseInt()
with a non-string value.
-
-
Explicitly coerce everything to boolean
-
var a = "0"; var b = []; var c = {}; var d = ""; var e = 0; var f = null; var g; Boolean( a ); // true Boolean( b ); // true Boolean( c ); // true Boolean( d ); // false Boolean( e ); // false Boolean( f ); // false Boolean( g ); // false
-
the above method is the normal method.
-
var a = "0"; var b = []; var c = {}; var d = ""; var e = 0; var f = null; var g; !!a; // true !!b; // true !!c; // true !!d; // false !!e; // false !!f; // false !!g; // false
-
!!
method is actually prefered. -
how to force a true/false coercion in the JSON serialization of a data structure
-
var a = [ 1, function(){ /*..*/ }, 2, function(){ /*..*/ } ]; JSON.stringify( a ); // "[1,null,2,null]" JSON.stringify( a, function(key,val){ if (typeof val == "function") { // force `ToBoolean` coercion of the function return !!val; } else { return val; } } ); // "[1,true,2,true]"
-
-
-
Implicit coercions are any type of coercions that aren't obvious. Most of the complaints about js coercion are towards implicit coercion.
-
Numbers to Strings
-
-
string + number/number + string
-
the number is gonna be coerced to a string. then two strings are being added.and the result is a string.
-
var a = 42; var b = "0"; a+b; // "420"
-
-
-
string + string
-
two strings are being added and result is a string.
-
var a = "42"; var b = "0"; a+b; //"420"
-
-
-
number + number
-
two numbers are being added and result is a number.
-
var a = 42; var b = 0; a+b; //42
-
-
-
difference between
String(a)
anda+""
-
-
-
Strings to Numbers
-
The
-
(minus) operator is defined only for a numeric osubstracrion.-
var a = "3.14"; var b = 0; a-b; //3.14
-
-
object - boject
-
var a = [3]; var b = [1]; a-b; //2
-
Both array got coerced into string 1st, then get converted into number. The result is number.
-
-
-
Booleans to Numbers
-
Everything to Boolean
- What sort of expression operations require/force (implicitly) a boolean coercion?
- The test expression in an
if (..)
statement. - The test expression (second clause) in a
for ( .. ; .. ; .. )
header. - The test expression in
while (..)
anddo..while(..)
loops. - The test expression (first clause) in
? :
ternary expressions. - The left-hand operand (which serves as a test expression -- see below!) to the
||
("logical or") and&&
("logical and") operators.
- The test expression in an
- What sort of expression operations require/force (implicitly) a boolean coercion?
-
||
and&&
-
these two are " operand selector operators"
-
they DO NOT produce a boolean value a.k.a truth or false
-
they select one of the operand's value as the outoput of the operation
-
var a = 42; var b = "abc"; var c = null; a || b; // 42 a && b; // "abc" c || b; // "abc" c && b; // null
-
||
will see whether the 1st operand is a truthy value. if it is, as in this case,||
will output the 1st operand as the result. -
&&
will see whether the 1st operand is a truthy value. if it is,||
will output the 2nd operand as the result. -
javascript's version of the C# "null coallescing operator"
-
function foo(a,b) { a = a || "hello"; b = b || "world"; console.log( a + " " + b ); } foo(); // "hello world" foo( "yeah", "yeah!" ); // "yeah yeah!"
-
-
"guard operator/short circuited operator"
-
function foo() { console.log( a ); } var a = 42; a && foo(); // 42
-
foo()
gets called only if a is a truthy value. if it is not, thea&&foo()
simply will not get executed a.k.a getting ignored.
-
-
||
and&&
in if else loop-
implicitly coerced:
-
var a = 42; var b = null; var c = "foo"; if (a && (b || c)) { console.log( "yep" ); }
-
-
explicitly coerced:
-
var a = 42; var b = null; var c = "foo"; if (!!a && (!!b || !!c)) { console.log( "yep" ); }
-
-
-
-
Symbol Coercion
-
you can only explicitly coerce a symbol.
-
var s1 = Symbol( "cool" ); String( s1 ); // "Symbol(cool)" var s2 = Symbol( "not cool" ); s2 + ""; // TypeError
-