Wednesday, November 20, 2013

21 JavaScript Parts I Struggle To Remember

21 JavaScript Parts I Struggle To Remember
This article is a review of the JavaScript ES3 parts that, for whatever reason, fade over time in my mind or I outright forget about. Some of the parts mentioned are quirks and some are just plain language realities that I personally find difficult to keep straight.

The eluding parts I discuss here are not focused on method nuances that can be resolved by referring to a JavaScript reference. I have tried to mainly focus on the parts of JavaScript that cannot easily be found in a language reference (i.e the difference between apply() and call()).

Additionally, before I begin, I need to clarify the following three points about what I am NOT suggesting by writing this article:

  1. I am not suggesting these parts are forgettable in general or by nature. I just find the following parts hard for me to remember over time (i.e. details become hazy over time unless I am consistently studying). These parts might be easy for you to remember and it is possible that this is just an issue with my brain. Nevertheless, I thought this might be helpful to someone besides myself.
  2. I am not suggesting that you use these parts. I am only suggesting that you be aware of these parts so you can read code from other developers with ease. Most of these parts are helpful to recognizes but not ideal solutions.
  3. I am excluding ES5 strict mode, ES5 in general, and ES6 parts from this article. I will cover these parts in a future article with the same premise. I acknowledge that some parts discussed in this article either change or evolve in future editions of ECMAScript.

With that said, let's begin.

1. Negative Zero = Positive Zero = Zero (i.e. −0 = +0 = 0)

In JavaScript there is the numeric value -0 and +0 which can indicate from which direction you approached zero. JavaScript, however, attempts to hide this by converting -0 and +0 to 0.

console.log(-0); //logs 0
console.log(+0); //logs 0
console.log(-0 === +0); //logs true
console.log(0 === -0); //logs true
console.log(0 === +0); //logs true

2. new Alone Can Invoke A Constructor Function

Calling a constructor function with no arguments and the new keyword alone without the () invocation operator will still invoke the constructor function.

var Human = function(){
    this.type = 'human';
    console.log('I was called');
};

new Human; //new invokes without () and logs "I was called"

console.log((new Human).type); //logs human

3. instanceof Does not Work On Primitive Values

The instanceof operator will return false for the string, number and boolean primitive values even though JavaScript provides object wrappers for these values. If these values are in their object form (i.e. primitive object wrappers) then instanceof can be used. Don't forget that null and undefined do not have object wrappers.

//instanceof does not work on primtive values
console.log("" instanceof String); //logs false
console.log(3 instanceof Number); //logs false
console.log(false instanceof Boolean); //logs false

//instanceof works on complex values (i.e. objects)
console.log(new String() instanceof String); //logs true
console.log(new Number() instanceof Number); //logs true
console.log(new Boolean() instanceof Boolean); //logs true
console.log([] instanceof Array); //logs true
console.log({} instanceof Object); //logs true
console.log(/foo/ instanceof RegExp); //logs true

//be aware
console.log([] instanceof Object); //logs true

4. The typeof Operator Is Limited and Buggy

The typeof operators usefulness and accuracy is limited to primitive values and contains a bug (i.e. typeof null === "object" is true). Because of this, it is commonly avoided and non-native solutions for type checking are best.

//handy for string, number, and boolean primtive values
console.log(typeof ""); //logs string
console.log(typeof 4); //logs number
console.log(typeof true); //logs boolean
console.log(typeof undefined); //logs undefined

//But buggy when it comes to null
console.log(typeof null); //WHAT? wrong! logs Object

5. Boolean Object Wrappers Are Always true

new Boolean(false) is not a false value, because this returns an object wrapper not a primitive false boolean value. The only false values are NaN, false, 0, null, underfined and ''.

6. Function Arguments vs. Function Parameters

Arguments are used when a function is being called. We pass arguments to functions. Parameters are set up when the function is defined. So it is said that parameters are used to define a function and arguments are used to invoke a function. These words are typically interchangeable but knowing the distinction can be handy.

var myFunction = function(x,y,z){ //x,y,z are parameters
    return x+y+z
};

myFunction(1,2,3); //1,2,3 are arguments

7. Changing Parameter Value Does Not Change Argument Value

If you change the value of a parameter, it does not effect the argument. For instance, if you pass an array argument into a function, then change the value of the parameter, the array argument does not change its reference.

var myFunction = function(parameter){
    parameter = undefined; //change parameter value to undefiend
    console.log(parameter);
};

var myArray = [1,2,3];

myFunction(myArray); //invoke myFunction, passing array

//myArray is not undefined regardless of if parameter is change in function
console.log(myArray); //logs [1,2,3]

8. Calling Boolean() As Function Manually Coverts Any Value To A Boolean

Calling the Boolean() constructor as a function (i.e. without the new keyword) with an argument value will convert the value to a boolean.

//All below return false 
console.log(Boolean(undefined));
console.log(Boolean(null));
console.log(Boolean(0));
console.log(Boolean(''));
console.log(Boolean(NaN));

//All below return true
console.log(Boolean(1));
console.log(Boolean('false'));
console.log(Boolean([]));
console.log(Boolean({}));
console.log(Boolean(function(){}));
console.log(Boolean(/foo/));

Of course, using the not operator twice has the same effect. It is just not verbose enough for my liking.

//All below return false 
console.log(!!undefined);
console.log(!!null);
console.log(!!0);
console.log(!!'');
console.log(!!NaN);

//All below return true
console.log(!!1);
console.log(!!'false');
console.log(!![]);
console.log(!!{});
console.log(!!function(){});
console.log(!!/foo/);

9. Calling Primitive Wrapper Constructors Without new Returns Primitive Values

The primitive string, number, and boolean values can be created by invoking the corresponding constructor function without the new keyword.

//logs true
console.log(String('foo') === 'foo');
console.log(Number(5) === 5);
console.log(Boolean('true') === true);

//logs false because using new created Object wrapper
console.log(new String('foo') === 'foo');
console.log(new Number(5) === 5);
console.log(new Boolean('true') === true);

10. Object() Can Produce Primitive Object Wrappers

When the Object() constructor is called as a function and passed a primitive string, number, or boolean value a primitive object wrapper is returned.

//use Object() to create primitve number wrapper object 
console.log(Object(1) instanceof Number); //logs true

//use Object() to create primitve string wrapper object
console.log(Object('foo') instanceof String);

//use Object() to create primitve boolean wrapper object
console.log(Object(true) instanceof Boolean);

11. Accessing Properties On Primitive Values Fails

While no error occurs when adding properties to primitive values, it is impossible to access these properties. The reason being that the object wrapper, created on the fly, is immediately discarded when access is completed.

//create primtives
var N = 5;
var S = 'foo';
var B = true;

//add properties to primitives, that get destroyed when wrapper is trashed
N.test = 'test';
S.test = 'test';
B.test = 'test';

//verify that non of these properties remain.
console.log(N.test); //logs undefined
console.log(S.test); //logs undefined
console.log(B.test); //logs undefined

12. delete Won't Delete Inherited Properties

The delete operator will only work if the object property you are deleting is actually a property (i.e. hasOwnProperty() === true) of the object as opposed to a property inherited from the prototype chain.

var Person = function(){}; //create Person constructor

Person.prototype.type = 'person'; //add inherited type property

var cody = new Person(); //create instance of Person

delete cody.type //delete type

console.log(cody.type) //logs person, becuase you can't delete inherited properties

13. String Wrapper Objects Are Array-like

Because string wrapper objects create an array-like object (e.g. console.log(new String('foo')); //logs foo {0="f", 1="o", 2="o"}) we can use an index to access individual characters in a string (fyi >ie7).

//create primtive string
var foo = 'foo';

//Access characters like an array
console.log(foo[0]) //logs "f" because wrapper objects is {0="f", 1="o", 2="o"}

//Can even use braket notion to get properties
console.log(foo['length']); //logs 3

14. Accessing Properties From Primitive Numbers

The first . after a primitive number is parsed by JavaScript as a floating point literal. To gain access to object properties on a number wrapper object you can do the following:

//use braket notation
console.log(2['toString']()); //logs 2

//use dot notation after accounting for floating point
console.log(2..toString()); //logs 2

//use brakets
console.log((2).toString()); //logs 2

15. Array.length Can Be Set And Has Side Effects

By setting the length property on an array, one can add undefined values or remove items from an array.

//add undefined value to array using length property
var myArray1 = [];
myArray1.length = 3;
console.log(myArray1); //logs [undefined, undefined, undefined]

//remove items from array by changing length property
myArray2 = [1,2,3,4,5,6,7,8,9]
myArray2.length = 1;
console.log(myArray2); //log [1]

16. Logical || Operator Short-Circuits, Returns First true Value

The logical || operator will return the first true value, leaving the remaining values unevaluated (i.e. short-circuiting) because once a true value is found no other values can change this fact.

//Return first true value, then stop evaluating other expressions
var foo = false || 0 || '' || 4 || 'foo' || true;

console.log(foo); //logs 4, because its the first true value in the experssion

17. Logical && Operator Short-Circuits, Returns First false Value

The logical && operator will return the first false value, leaving the remaining values un-evaluated (i.e. short-circuiting) because once a false value is found no other values can change this fact.

//Return first false value, then stop evaluating other expressions
var foo = true && 'foo' && '' && 4 && 'foo' && true;

console.log(foo); //logs '' an empty string, because its the first false value

18. When To Use null And When Not To Use undefined

Simply stated, null is the value for nothing, while undefined is the lack of a value. It is common practice for a developer to only make use of the null value when needing to indicate no value as of yet, and leave undefined for JavaScript to use indicating a total lack of value. By using null in your program you can differentiate between JavaScript telling you a value is missing and you reminding yourself that you have not yet provided a value.

19. undefined By Default

undefined represents no value. The following creates no value (i.e. undefined).

//variables, with no value assigned, are undefined
var foo;
console.log(foo); //logs undefined

//Attempting to access properties that do not exist returns undefined
console.log(this.foo); //logs undefined

//Passing missing arguments to function, expecting them, results in undefined parameters
var myFunction = function f(x){return x}
console.log(myFunction()); //logs undefined

//Functions return undefined, if no return value is specified
var myFunc = function f(){};
console.log(myFunc()); //logs undefined

20. Expressions Can Go Anywhere While Statements Can't

Expressions produce values, while statements perform an action. The clearest picture of this can be found in the different between an if statement and the ternary operator for creating a conditional expression. The if statement can't be used anywhere a value can be used, but a conditional expression using the ternary operator can.

var verify = true;

//if statement used to represent all statement's
if(verify){console.log('verify is true');}

//Consider that an expression can go anywhere, while the statement above can't

//stored in a variable
var check = verify ? console.log('verify is true') : console.log('verify is false');

//passed to a function
console.log(verify ? 'verify is true' : 'verify is false');

21. Placement Of ++ and -- Operators

If the placement of ++/-- operators comes after a variable (i.e. boo++) then the value returned is the value previous to incrementing/decrementing. If the placement of ++/-- operator comes before a variable (i.e. ++foo) then the value is incremented/decremented and this new value is returned.

var boo = 1;
var foo = 1;

console.log(boo++); //logs 1, but console.log(boo) would log 2
console.log(++foo); //logs 2, returns the value of foo