- Coercion & Grammar
Mia Yang
Converting a value from one type to another
var num = 42
var implicit = num + ""
var explicit = String(num)
When any non-string value is coerced to a string
representation, it can either be called explicitly, or it will automatically be called if a non-string is used in a string context.
Built-in primitive values have natural stringification,
and for regular object, unless you specify your own, the default will return [object Object]
const a = null // "null"
const b = true // "true"
const c = undefined // "undefined"
const d = 42 // "42"
const e = 1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 // "1.07e21"
const f = { name: "Mia", age: 21 } // [object Object]
As shown earlier, if an object has its own toString()
method on it, and you use that object in a string-like
way, its toString() will automatically be called, and the string result of that call will be used instead.
let obj = {
name: 'mia'
}
console.log(obj.toString()) // [object Object]
obj.toString = () => {
return 'over'
}
const implicit = obj + 'ride!' // "override!"
const explicit = obj.toString() + 'ride!' // "override!"
const arr = [1, 2, 3]
arr.toString() // "1, 2, 3"
Any JSON-safe value can be stringified by JSON.stringify()
JSON.stringify( 42 ); // "42"
JSON.stringify( "42" ); // ""42""
JSON.stringify( null ); // "null"
JSON.stringify( true ); // "true"
It may be easier to consider values that are not JSON-safe
JSON.stringify( undefined ); // undefined
JSON.stringify( function(){} ); // undefined
JSON.stringify( [1,undefined,function(){},4] ); // "[1,null,null,4]"
JSON.stringify( { a:2, b:function(){} } ); // "{"a":2}"
var o = {};
var a = {
b: 42,
c: o
};
o.a = a;
JSON.stringify(o); // Converting circular structure to JSON
JSON stringification has the special behavior that if an object value has a toJSON() method defined, this method will be called first to get a value to use for serialization
var obj = {
value: 42
};
obj.toJSON = function() {
return { value: 1 };
};
console.log(JSON.stringify(obj)); // { "value": 1 }
It's a very common misconception that toJSON() should return a JSON stringification representation
var a = {
val: [1, 2, 3],
// probably correct!
toJSON: function() {
return this.val.slice(1);
}
};
var b = {
val: [1, 2, 3],
// probably incorrect!
toJSON: function() {
return "[" + this.val.slice(1).join() + "]";
}
};
console.log(JSON.stringify(a)) // "[2,3]"
console.log(JSON.stringify(b)) // ""[2,3]""
var a = {
b: 42,
c: " 42 ",
d: [1, 2, 3]
};
JSON.stringify(a, ["b", "c"]) // "{"b":42,"c":"42"}"
JSON.stringify(a, function(key, value) {
if (key !== "c") return value;
})
// "{"b":42,"d":[1,2,3]}"
console.log(JSON.stringify(a, null ,4))
/*
{
"b": 42,
"c": " 42 ",
"d": [
1,
2,
3
]
}
*/
console.log(JSON.stringify(a, null, '--'))
/*
{
--"b": 42,
--"c": " 42 ",
--"d": [
----1,
----2,
----3
--]
}
*/
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
console.log(Number("")) // 0
console.log(Number("42")) // 42
console.log(Number("number")) // NaN
var a = {
valueOf: function() {
return " 42 ";
}
};
var b = {
toString: function() {
return " 42 ";
}
};
var c = [4, 2];
console.log(Number(a)); // 42
console.log(Number(b)); // 42
console.log(Number(c)); // 42
console.log(Number([])) // 0
console.log(Number(["abc"])) // NaN
console.log(Number({ a: 10})) // NaN
It's a common misconception that the values 1 and 0 are identical to true/false. While that may be true in other languages, in JS the numbers are numbers and the booleans are booleans
The following as the so-called falsy list, and everything else will be Truthy
A value is truthy if it's not on the falsy list.
In other words, the truthy list is infinitely long. It's impossible to make such a list. You can only make a finite falsy list and consult it.
var a = 42;
var b = a.toString();
var c = "3.14";
var d = +c;
b; // "42"
d; // 3.14
const a = +new Date();
const b = new Date().getTime()
const c = Date.now()
+ 2^(n-1) - 1 | 0111 …. 1111 |
---|---|
. | |
+ 2 | 0000 …. 0010 |
+ 1 | 0000 …. 0001 |
0 | 0000 …. 0000 |
– 1 | 1111 …. 1111 |
– 2 | 1111 …. 1110 |
– 3 | 1111 …. 1101 |
. | |
– 2^(n-1) | 1000 …. 0000 |
~ x ==> - (x + 1)
~42; // -(42+1) ==> -43
In JavaScript, -1 is commonly called a "sentinel value"
var a = " Hello World ";
if (a.indexOf("lo") >= 0) {
console.log("found it");
}
if (a.indexOf("lo") != -1) {
console.log("found it");
}
if (a.indexOf("ol") < 0) {
console.log("Not found");
}
if (a.indexOf("ol") == -1) {
console.log("Not found");
}
var a = "Hello World";
if (~a.indexOf("lo")) {
console.log("found it");
}
if (!~a.indexOf("ol")) {
console.log("Not found");
}
Parsing a numeric value out of a string is tolerant of non-numeric characters, It just stops parsing left-to-right when encountered ,whereas coercion is not tolerant and fails resulting in the NaN value.
var a = "42";
var b = "42px";
console.log(Number(a)); // 42
console.log(parseInt(a)); // 42
console.log(Number(b)); // NaN
console.log(parseInt(b)); // 42
If you pass a non-string, the value you pass will automatically be coerced to a string first, so never use parseInt(..) with a non-string value.
parseInt ( 1 / 0 , 19 ); // 18
parseInt ( 0.000008 ); // 0 ("0" from "0.000008")
parseInt ( 0.0000008 ); // 8 ("8" from "8e-7")
parseInt ( false , 16 ); // 250 ("fa" from "false")
parseInt ( parseInt, 16 ); // 15 ("f" from "function..")
parseInt ( "0x10" ); // 16
parseInt ( "103" , 2 ); // 2
parseInt(..) would look at the beginning character(s) to make a guess.
var a = "0";
var b = [];
var c = {};
var d = "";
var e = 0;
var f = null;
var g;
console.log(Boolean(a)) // true
console.log(Boolean(b)) // true
console.log(Boolean(c)) // true
console.log(Boolean(d)) // false
console.log(Boolean(e)) // false
console.log(Boolean(f)) // false
console.log(Boolean(g)) // false
var a = "0";
var b = [];
var c = {};
var d = "";
var e = 0;
var f = null;
var g;
console.log(!!a) // true
console.log(!!b) // true
console.log(!!c) // true
console.log(!!d) // false
console.log(!!e) // false
console.log(!!f) // false
console.log(!!g) // false
var a = 42
var b = a + ""
b // "42"
var a = [ 1 , 2 ];
var b = [ 3 , 4 ];
a + b; // "1,23,4"
But if you're using an object instead of a regular primitive number value, you may not necessarily get the same string value!
var a = {
valueOf: function () { return 42; },
toString: function () { return 4; }
};
console.log(a + ""); // "42"
console.log(String(a)) // "4"
The value produced by a && or || operator is not necessarily of type Boolean. The value produced will always be the value of one of the two operand expressions.
var a = 42;
var b = "abc";
var c = null;
console.log(a || b) // 42
console.log(a && b) // "abc"
console.log(b || c) // "abc"
console.log(b && c) // null
function foo () {
console.log(a);
}
var a = 42;
a && foo(); // 42
A very common misconception about these two operators is: "== checks values for equality and === checks both values and types for equality."
The correct description is: "== allows coercion in the equality comparison and === disallows coercion."
What's difference between those two explanation ?
var a = 42;
var b = "42";
a === b; // false
a == b; // true
var a = "42";
var b = true;
var c = false
a == b; // false
a == c; // false
var a = null;
var b;
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false
var a = 42;
var b = [42];
var c = Object("42")
a == b; // true
a == c; // true
var i = 1;
Number.prototype.valueOf = function () {
return ++i;
};
var a = new Number(42);
if (a == 2 && a == 3) {
console.log(" Yep, this happened. "); // Yep, this happened.
}
"0" == null; // false
"0" == undefined; // false
"0" == false; // true --噢!
"0" == NaN; // false
"0" == 0; // true
"0" == ""; // false
false == null; // false
false == undefined; // false
false == NaN; // false
false == 0; // true --噢!
false == ""; // true --噢!
false == []; // true --噢!
false == {}; // false
"" == null; // false
"" == undefined; // false
"" == NaN; // false
"" == 0; // true --噢!
"" == []; // true --噢!
"" == {}; // false
0 == null; // false
0 == undefined; // false
0 == NaN; // false
0 == []; // true --噢!
0 == {}; // false
[] == ![] // true --噢!
The algorithm is only defined for a > b
So, a > b is handled as b < a
var a = [42];
var b = ["43"];
a < b // true
var c = [4, 2]
var d = [4, 3]
c < d // true
var a = { x: 10 }
var b = { x: 11 }
a < b // false
a == b // false
a > b // false
a <= b // true
b <= a // true
a <= b ===> ! (a > b)