Primitive | T/F |
---|---|
0 | falsey |
NaN |
falsey |
undefined |
falsey |
null |
falsey |
Empty string ('') | falsey |
Numbers other than 0 | truthy |
Non-empty string | truthy |
Infinity |
truthy |
0 is falsey, meaning that it is coerced to false in some contexts:
if (0) console.log('0 is truthy'); // no output
Boolean(0); // => false
false
1 is truthy, meaning that it is coerced to true in some contexts:
if (1) console.log('1 is truthy'); // => "1 is truthy"
console.log(Boolean(1)); // => true
1 is truthy true
undefined
-1 is truthy:
if (-1) console.log('-1 is truthy'); // => "-1 is truthy"
console.log(Boolean(-1)); // => true
-1 is truthy true
undefined
So 0 is falsey, and numbers other than 0 are truthy.
Infinity
is truthy:
if (Infinity) console.log('Infinity is truthy'); // => "Infinity is truthy"
console.log(Boolean(Infinity)); // => true
Infinity is truthy true
undefined
NaN
is falsey:
if (NaN) console.log ('NaN is truthy'); // no output
console.log(Boolean(NaN)); // => false
false
undefined
undefined
is falsey:
if (undefined) console.log('undefined is truthy'); // no output
var foo; if (foo) console.log('undefined is truthy'); // no output
console.log(Boolean(undefined)); // => false
console.log(Boolean(foo)); // => false
false false
undefined
null
is falsey
if (null) console.log('null is truthy'); // no output
console.log(Boolean(null)); // => false
false
undefined
Strings are truthy:
if ("foo") console.log('The string "foo" is truthy'); // => 'The string "foo" is truthy'
console.log(Boolean('foo')); // => true
The string "foo" is truthy true
undefined
...except for an empty string, which is falsey:
if ("") console.log('The empty string "" is truthy'); // no output
console.log(Boolean('')); // => false
false
undefined
var arr = [0, NaN, undefined, null, '', 1, 'foo', Infinity];
console.log(arr.filter(Boolean)); // => [1, 'foo', Infinity]
[ 1, 'foo', Infinity ]
undefined
Using the !
operator:
var arr = [0, NaN, undefined, null, '', 1, 'foo', Infinity];
// => [ 0, NaN, undefined, null, '' ]
console.log(arr.filter(function(val) {return !val}));
// => [1, 'foo', Infinity]
console.log(arr.filter(function(val) {return !!val}));
[ 0, NaN, undefined, null, '' ] [ 1, 'foo', Infinity ]
undefined
!
operator¶console.log(!0); // => true
true false
undefined
console.log(!1); // => false
false
undefined
Used twice (!!
):
console.log(!!0 === Boolean(0)); // => true
true
undefined
var foo = 'foo';
console.log(foo); // => 'foo'
console.log(!foo); // false
console.log(!!foo); // true
foo false true
undefined
Returning a Boolean value instead of a value stored in a variable:
var foo = 'foo';
function f(foo) { return foo };
console.log(f(foo)); // => foo
// Is foo truthy?
function ff(foo) { return !!foo };
console.log(ff(foo)); // => true
foo true
undefined
Here's an example of the usefulness of type coercion. There is no need to convert the numeric value of age
to a string before sending it to console.log()
, because the JS parser does that for you:
var x = 1;
console.log("The numeric value of x is " + x);
// => "The numeric value of x is 1"
The numeric value of x is 1
undefined
Here's an example of where type coercion is less useful. If you add a string value representing a number to a numeric value, what you will get back is a concatenated string value:
var x = 1;
console.log(x + "1"); // => "11"
11
undefined
Here's another example of where type coercion is less useful. String values are truthy, but they are not true: that is, comparison with the boolean true
is not one of the contexts in which string values are coerced to truthiness:
if ("foo") console.log('The string "foo" is truthy');
// => 'The string "foo" is truthy'
console.log("foo" == true); // => false
The string "foo" is truthy false
undefined
The equality operator ==
(and the inequality operator !=
) performs type coercion. The strict equality operator ===
(and the strict inequality operator !==
) do not:
console.log(null == undefined); // => true
console.log(null === undefined); // => false
console.log(1 == "1"); // => true
console.log(1 === "1'"); // => false
console.log(0 == false); // => true, because 0 is falsey
console.log(0 === false); // => false
true false true false true false
undefined
Let's say we want to reduce a sparse array to a dense array by excluding null
or undefined
values. The non-strict inequality operator !=
will produced the desired result, but the strict inequality operator !==
will not:
var arr = [0, 1, 2, 3, , null, undefined];
console.log(arr.filter(function (val) {
return val != null; // Non-strict inequality
})); // => [0, 1, 2, 3]
console.log(arr.filter(function (val) {
return val !== null; // Strict inequality
})); // => [0, 1, 2, 3, undefined]
[ 0, 1, 2, 3 ] [ 0, 1, 2, 3, undefined ]
undefined
An alternative is to include only truthy values, with the explicit addition of zero, since zero is falsey:
var arr = [0, 1, 2, 3, , , null, undefined];
console.log(arr.filter(function (val) {
// Return either zero, or any truthy value
return val === 0 || val;
})); // => [0, 1, 2, 3]
[ 0, 1, 2, 3 ]
undefined
Very basic validation, rejecting null, undefined, an empty (undefined) array element, and empty string:
function isTruthy(value) {
return (!value) ? false : true;
}
console.log(isTruthy(null)); // => false
console.log(isTruthy(undefined)); // => false
console.log(isTruthy([1,,][1])); // => false
console.log(isTruthy("")); // => false
console.log(isTruthy(1)); // => true
console.log(isTruthy("foo")); // => true
false false false false true true
undefined
Creating an array using array literal notation:
var myArray = [1, 2, 3, 4, 5];
console.log(myArray); // => [ 1, 2, 3, 4, 5 ]
[ 1, 2, 3, 4, 5 ]
undefined
Arrays are a special type of object. They can have named properties, like objects. The built-in named property length
is an example:
var myArray = [1, 2, 3, 4, 5];
console.log(myArray.length); // => 5
5
undefined
You can add a named property to an array (here, a string) and access it using dot notation:
myArray.name = 'foo';
console.log(myArray); // => [ 1, 2, 3, 4, 5, name: 'foo' ]
console.log(myArray.name); // => 'foo'
[ 1, 2, 3, 4, 5, name: 'foo' ] foo
undefined
You can add a function as a named property, making it a method of the array. This function will log to the console the property values stored in the array:
myArray.list = function() {
for (var i = 0, j = this.length; i < j; i++) {
console.log(this[i]);
}
};
console.log(myArray); // => [ 1, 2, 3, 4, 5, name: 'foo', list: [Function] ]
console.log(myArray.list); // => [Function]
[ 1, 2, 3, 4, 5, name: 'foo', list: [Function] ] [Function]
undefined
One might expect that when called, myArray.list()
will log to the console not only the property values 1, 2, 3, 4, and 5, but also 'foo' and the list()
function itself. However, the named properties name and list appear to be just that: properties with names attached to them, but without values. Since they have no values, myArray.list()
displays nothing for them:
myArray.list();
1 2 3 4 5
undefined
Here, on the other hand, is a function that takes an array, and using a for...in
loop, displays the properties of the array object:
var displayArray = function(array) {
for (var prop in array) {
console.log(prop);
}
};
undefined
displayArray()
logs to the console the array indices 0, 1, 2, 3, and 4 for the numeric values stored in the first five indices of the array, followed by the property names name and list. Evidently, the array indices themselves stand in for or serve as property names, for those first five unnamed values:
displayArray(myArray);
0 1 2 3 4 name list
undefined
The name of a named property can be accessed using both dot and bracket notation:
console.log(myArray.name); // => 'foo'
console.log(myArray['name']); // => 'foo'
foo foo
undefined
However, the array indices that seem to stand in for or serve as property names cannot be accessed using dot notation:
// Does not return '0' but a syntax error
console.log(myArray.0);
evalmachine.<anonymous>:2 console.log(myArray.0); ^^ SyntaxError: Unexpected number at Object.exports.runInThisContext (vm.js:73:16) at run ([eval]:179:19) at onMessage ([eval]:63:41) at process.emit (events.js:110:17) at handleMessage (child_process.js:324:10) at Pipe.channel.onread (child_process.js:352:11)
In addition to being an instanceof
any constructor specified explicitly, an object is also an instanceof
the built-in Object
constructor:
var MyConstructor = function() {};
var myObject = new MyConstructor;
console.log(myObject instanceof MyConstructor); // => true
console.log(myObject instanceof Object); // => true
true true
undefined
If an object is not created with a specified constructor, its constructor is the built-in Object
constructor, which also has its own prototype (both prototypes are empty here):
var MyConstructor = function() {};
var myObject = new MyConstructor;
console.log(myObject.constructor); // => [Function]
console.log(myObject.constructor.prototype); // => {}
var myObject = {}; // Reset using (empty) object literal notation
console.log(myObject.constructor); // => [Function: Object]
console.log(myObject.constructor.prototype); // => {}
[Function] {} [Function: Object] {}
undefined
Use the built-in hasOwnProperty()
method to determine if a property is a property of an object or a property of its prototype:
var MyConstructor = function() {};
var myObject = new MyConstructor;
MyConstructor.prototype.myProperty = 'foo';
myObject.hasOwnProperty('myProperty'); // => false
false
Both empty objects and non-empty objects are truthy, but not true when compared:
var o1 = {};
var o2 = { whatAmI: "object" };
if (o1) console.log('An empty object is truthy');
if (o2) console.log('A non-object is truthy');
// => 'An empty object is truthy'
// => 'A non-empty object is truthy'
if (o1 == true) console.log('An empty object is true when compared');
if (o2 == true) console.log('A non-empty object is true when compared');
An empty object is truthy A non-object is truthy
undefined
An empty object is not equal to another empty object:
var o1 = {};
var o2 = {};
console.log(o1 == o2); // => false
false
undefined
A non-empty object is not equal to another non-empty object even if they have identical properties and values:
var o1 = { whatAmI: "object", whyAmI: "for demonstration purposes" };
var o2 = { whatAmI: "object", whyAmI: "for demonstration purposes" };
console.log(o1 == o2); // => false
console.log(o1 === o2); // => false
false false
undefined
This is because objects are stored as references, and the references for these two objects are not the same. Only if we assign the reference of one object to another object can both objects point to the same memory location:
var o1 = { whatAmI: "object", whyAmI: "for demonstration purposes" };
var o2 = { whatAmI: "object", whyAmI: "for demonstration purposes" };
var o1 = o2;
console.log(o1 == o2); // => true
console.log(o1 === o2); // => true
true true
undefined
If we subsequently add another property-value pair to either of two such objects, it will be reflected in the other object, because the memory references are identical:
var o1 = { whatAmI: "object", whyAmI: "for demonstration purposes" };
var o2 = { whatAmI: "object", whyAmI: "for demonstration purposes" };
var o1 = o2;
o1.whereAmI = "nobody knows";
console.log(o2.whereAmI); // => "nobody knows"
nobody knows
undefined
The only way to compare two different objects that have identical properties and values is to compare their property-value pairs one at a time.
Functions are also stored as references, so that just like objects, they are both truthy and unequal with each other when compared (both empty functions and non-empty functions), regardless of whether strict or non-strict equality operators are used:
var foo = function(){},
bar = function(){};
if (foo) console.log("foo() is truthy"); // => "foo() is truthy"
console.log(foo == bar); // => false
console.log(foo === bar); // => false
foo = function(){};
bar = function(){return true};
console.log(foo == bar); // => false
console.log(foo === bar); // => false
foo = function(){return true};
bar = function(){return true};
if (foo) console.log("foo() is truthy"); // => "foo() is truthy"
console.log(foo == bar); // => false
console.log(foo === bar); // => false
foo() is truthy false false false false foo() is truthy false false
undefined
If not given a return value, a function will return undefined
:
function f1() {return 'foo';};
function f2() {};
console.log(f1()); // => 'foo'
console.log(f2()); // => undefined
foo undefined
undefined
When passed to a function and bound to a function parameter, primitives are passed as copies of the data stored in a reference.
A copy of the data of the numeric value 3 is passed to a function:
var val1 = 3
var f1 = function(x) {return x + 1};
console.log(f1(val1), val1); // => 4 3
4 3
undefined
A copy of the data of the string value "test" is passed to a function:
var val2 = "test";
var f2 = function(x) {return x + 'foo'};
console.log(f2(val2), val2); // => 'testfoo' 'test'
testfoo test
undefined
When passed to a function and bound to a function parameter, objects are passed as copies of the reference itself. Since a copy of the reference still points to the references, this means that properties of an object can be changed.
A copy of the reference to an object {x: 1, y: 2} is passed to a function:
var val3 = {x:1, y:2};
var f3 = function(x) {x.x = 2; x.y = 1; return x};
console.log(f3(val3), val3); // => { x: 2, y: 1 } { x: 2, y: 1 }
{ x: 2, y: 1 } { x: 2, y: 1 }
undefined
A copy of the reference to an array [1, 2, 3] is passed to a function:
var val4 = [1, 2, 3];
var f4 = function(x) {x[0] = 0; return x};
console.log(f4(val4), val4); // => [ 0, 2, 3 ] [ 0, 2, 3 ]
[ 0, 2, 3 ] [ 0, 2, 3 ]
undefined
arguments
object¶Every function inherits an array-like arguments
object. This is a good way to handle a variable or unknown number of arguments:
function foo() {
for (var i = 0, len = arguments.length; i < len; i++) {
console.log(arguments[i]); // Or do whatever else you like
}
}
foo(1, 2); // => 1\n2
foo(1, 2, 3); // => 1\n2\n3
1 2 1 2 3
undefined
this
and lexical scoping¶In a function defined in the global scope, this
is defined as the global object (window
in a browser). Surprisingly, this
is also defined as the global object inside a nested function:
function outer() {
console.log("`this` in function `outer()`: ", this);
function inner1() {
console.log("`this` inside nested function `inner()`: ", this);
}
inner();
}
outer(); // => (Node.js global object, logged twice to consile)
Inside a constructor function, this
refers to the object being constructed. Inside an object created by constructor function, this
refers to that object. Inside a method of the object, this
also refers to that object. But inside a function nested inside the object, this
refers to the global object. In order to give a nested function the scope of the object, bind this
to another variable and use that in the nested function:
function C() {
this.prop = 'foo';
console.log('`this` inside object created by constructor `C()`:', this);
this.myMethod = function() {
console.log('`this` inside object method `myMethod`:', this);
}
this.myMethod();
// Bind `this` to another variable so the nested function gets the object's scope
var self = this;
function inner() {
console.log("`this` inside object's nested function `inner()`: ", self);
}
inner();
}
var obj = new C();
/* Console output: `this` refers to the object in all three cases.
`this` inside object created by constructor `C()`: { prop: 'foo' }
`this` inside object method `myMethod`: { prop: 'foo', myMethod: [Function] }
`this` inside object's nested function `inner()`: { prop: 'foo', myMethod: [Function] }
*/
`this` inside object created by constructor `C()`: { prop: 'foo' } `this` inside object method `myMethod`: { prop: 'foo', myMethod: [Function] } `this` inside object's nested function `inner()`: { prop: 'foo', myMethod: [Function] }
undefined
Inside an event handler function, this
refers to the event target element:
function foo() {
console.log('`this` inside an event handler function: ', this);
}
var div = document.createElement('div');
div.innerHTML = 'click me';
div.onclick = foo;
document.body.appendChild(div);
/* Console output when element is clicked:
`this` inside an event handler function: <div>click me</div>
*/
This might not be desirable if you want to use an object's method as an event handler, since this
inside such a method will be redefined as the event target:
function C() {
this.myMethod = function() {
console.log('`this` inside object method `myMethod`:', this);
}
}
var obj = new C();
var div = document.createElement('div');
div.innerHTML = 'click me';
div.onclick = obj.myMethod; // inside `myMethod()`, `this` is now the event target
document.body.appendChild(div);
/* Console output when element is clicked:
`this` inside object method `myMethod`: <div>click me</div>
*/
To change the scope of this
in such a case, call it from within a function:
function C() {
this.myMethod = function() {
console.log('`this` inside object method `myMethod()`:', this);
}
}
var obj = new C();
var div = document.createElement('div');
div.innerHTML = 'click me';
/* Ensure that `this` is defined as the event target inside `scoper()`
and as the object containing `myMethod()` when `myMethod()`
is called */
div.onclick = function scoper() {
console.log('`this` inside enclosing function `scoper()`:', this);
obj.myMethod();
};
document.body.appendChild(div);
/* Console output when element is clicked:
`this` inside enclosing function `scoper()`: <div>click me</div>
`this` inside object method `myMethod`: { myMethod: [Function] }
*/