As a front end white, I don’t know if you have the same problem as me. After reading the analysis of an interview question, I thought I could at that time, but I couldn’t look again two days later. Blindly pursuing all kinds of new technology, I feel like I can do everything, but I can’t do it once I get started… After reflecting on the pain, I finally realized the problem and began to focus on the training of basic skills. Nearly half a year to read through (in fact is swallowed) “JavaScript Advanced Programming”, “You don’t know JavaScript on, in, under” and other books, this series of articles is my reading process of knowledge points in some summary. Like the students remember to help me point 😁.

Understand wang series (1) thoroughly understand JavaScript function execution mechanism understand Wang series (2) thoroughly understand JavaScript scope understand Wang series (3) thoroughly understand JavaScript objects understand Wang series (4) thoroughly understand JavaScript classes To understand this, we need to understand JavaScript data types. To understand this, we need to understand JavaScript data types Complete JavaScript type conversion

Converting a value from one type to another is often called type casting, when it is explicit; The implicit case is called an coercion.

Casts occur at compile time for statically typed languages, while casts occur at runtime for dynamically typed languages.

1. Abstract value operations

1.1 the ToString

1.1.1 Common Objects

For normal objects, unless you define it, toString() returns the value of the internal property [[Class]], such as [object object].

1.1.2 Array objects

The array’s default toString() method has been redefined toString all the units and then concatenate them with “,” :

var a = [1.2.3];
a.toString(); / / "1, 2, 3"
Copy the code

1.1.3 JSON string

JSON.stringify(..) Undefined, Function, and symbol are automatically ignored when encountered in objects, and null is returned in arrays (to keep the cell position unchanged). Such as:

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}"
Copy the code

Note: Perform json.stringify (..) on an object that contains a circular reference Will go wrong

If a toJSON() method is defined in the object, JSON stringization calls that method first and then serializes with its return value.

// Custom JSON serialization
a.toJSON = function() {
  // Serialization contains only b
  return {b: this.b};
};
JSON.stringify(a); // "{"b":42}"
Copy the code

ToJSON () should return an appropriate value, which can be of any type, and then be returned by json.stringify (..) String it. That is, toJSON() should “return a safe JSON value that can be stringed,” not “return a JSON string.”

JSON. Stringify parameters

  1. replacer

We can use the JSON. Stringify (..) Pass an optional argument, replacer, which can be an array or function that specifies which properties should be processed and which should be excluded during object serialization, much like toJSON().

If the replacer is an array, it must be an array of strings containing the names of the properties of the object to be serialized with, and all other properties are ignored.

var a = { 
  b: 42.c: "42".d: [1.2.3]};JSON.stringify( a, ["b"."c"]);// "{"b":42,"c":"42"}"
JSON.stringify( a, function(k,v){
  if(k ! = ="c") return v;
});
/ / "{" b" : 42, "d" : [1, 2, 3]}"
Copy the code

If replacer is a function, its argument k is undefined the first time it is called (on the object itself). The if statement excludes the property “c”. Since stringization is recursive, each element in the array [1,2,3] is passed to the replacer with the argument v, which is 1,2, and 3, and the argument k is their index value, which is 0, 1, and 2.

  1. space

Json. stringify also takes an optional argument, space, which specifies the indented format of the output. When space is a positive integer, it specifies the number of characters to indent at each level. It can also be a string, where the first ten characters are used for each level of indentation:

var a = { 
  b: 42.c: "42".d: [1.2.3]};JSON.stringify( a, null.3 );
{/ /"
  // "b": 42,
  // "c": "42",
  // "d": [
    / / 1.
    / / 2,
    / / 3
  // ]
// }"
JSON.stringify( a, null."-- -- -- -- --" );
{/ /"
// -----"b": 42,
// -----"c": "42",
// -----"d": [
/ / -- -- -- -- -- -- -- -- -- - 1,
/ / -- -- -- -- -- -- -- -- -- -- 2,
/ / -- -- -- -- -- -- -- -- -- -- 3
/ / -- -- -- -- --
// }"
Copy the code

JSON.stringify(..) It’s not a cast. I introduce it here because it involves ToString casts, which are expressed in two ways.

(1) Json.stringify (..) for strings, numbers, Booleans, and null The rules are basically the same as for ToString. (2) If passed to json.stringify (..) The toJSON() method is defined in the

Called before serialization to convert the object to a safe JSON value.

1.2 ToNumber

True converts to 1, and false converts to 0. Undefined is converted to NaN and NULL is converted to 0. Objects (including arrays) are first converted to the corresponding primitive type value, and if a non-numeric primitive type value is returned, it is then cast to a number following the above rules.

Note: An Object [[Prototype]] created using object.create (null) has a null property and no valueOf() or toString() methods, so it cannot be cast

1.3 ToBoolean

These are false values:

• Undefined • null • false • +0, -0 and NaN • “”

A Boolean cast of a false value results in false.

var a = []; // Empty array -- true or false?
var b = {}; // Empty object -- true or false?
var c = function(){}; // Empty function -- is it true or false?
var d = Boolean(a && b && c); // true
Copy the code

[], {}, and function(){} are not in the list of false values, so they are all true.

2. Explicit cast

2.1 Explicit conversion between strings and numbers:

In addition to the String (..) And the Number (..) There are other ways to explicitly convert strings and numbers:

var a = 42;
var b = a.toString();
var c = "3.14";
var d = +c;
b; 42 "/ /"
d; / / 3.14
Copy the code

A.tostring () is explicit (” toString “means” to a string “), but it involves implicit conversions. Because toString() is not appropriate for a value of a basic type like 42, the JavaScript engine automatically creates a wrapper object for 42, and then calls toString() on that object. There are implicit conversions within explicit conversions.

  1. The date is explicitly converted to a number

Another common use of the unary operator + is to cast a Date object to a number, returning a Unix timestamp in milliseconds

var d = new Date("Mon, 18 Aug 2014 08:53:06 CDT");
+d; / / 1408369986000
Copy the code

To get the current timestamp, we often use the following methods, for example:

var timestamp = +new Date(a);Copy the code

JavaScript has a peculiar syntax that allows constructors to take no arguments without (). Var timestamp = +new Date; Let me write it this way. Whether this improves code readability is debatable, since this is only for new fn(), not for generic function calls to fn().

Casting Date types is not recommended. Instead, use date.now () to get the current timestamp and new Date(..). .gettime () to get a timestamp for the specified time.

  1. ~The operator

Minus x is roughly the same thing as minus x plus 1. Strange, but relatively easy to illustrate:

~42; / / - (42 + 1) = = > - 43
Copy the code

The only x value in minus (x+1) where you get 0 (or strictly negative 0) is minus 1. That is, if x is negative 1, ~ with some numeric values will return false 0; otherwise, it will return true.

Together with indexOf(), the result can be cast (really just cast) to true/false:

var a = "Hello World";
~a.indexOf( "lo" ); // -4 <-- true value!
if (~a.indexOf( "lo" )) { // true
 // Find a match!
}
~a.indexOf( "ol" ); // 0 <-- false value!! ~a.indexOf("ol" ); // true
if(! ~a.indexOf("ol" )) { // true
 // No match found!
}
Copy the code

If the indexOf (..) Return -1, ~ converts it to the false value 0, and otherwise to true.

2.2 Explicitly parses numeric strings

There is still a clear difference between parsing and transformation

var a = "42";
var b = "42px";

Number(a); / / 42
parseInt(a); / / 42

Number(b); // NaN
parseInt(b); / / 42
Copy the code

Parsing allows a string to contain non-numeric characters, in left-to-right order, stopping if a non-numeric character is encountered. The conversion does not allow non-numeric characters, or it will fail and return NaN. Parsing and transformation are not substitutes for each other. They are similar, but each has its own purpose. If the non-numeric characters to the right of the string do not affect the result, you can use parsing. Conversions require that all characters in the string be numbers, not strings like “42px.”

parseInt(..) It’s for string values. The parseInt (..) It is useless to pass numbers and other types of arguments, such as true, function(){… } and [1, 2, 3]

Non-string arguments are first cast to a string. Relying on such implicit casts is not a good policy. You should avoid using parseInt(..) Pass non-string arguments.

Starting with ES5 parseInt(..) Defaults to decimal unless specified otherwise. If your code needs to run in a pre-ES5 environment, be sure to set the second parameter to 10.

2.3 Display conversion to Boolean value

In the if (..) . In such a Boolean context, if Boolean(..) is not used And!!!!! , the ToBoolean conversion is automatically and implicitly performed. Boolean(..) is recommended. And!!!!! To do an explicit conversion to make the code more readable.

Boolean(a) and!! A to perform an explicit cast

3. Implicit casting

3.1 Implicit casting between strings and numbers

In simple terms, if one of the operands of + is a string (or a string can be obtained from the above steps), string concatenation is performed; Otherwise, perform number addition.

3.2 Implicit casting of Boolean values to numbers

(1) if (..) Statement. (2) for ( .. ; . ; ..) Statement (second). (3) while (..) And do.. while(..) A conditional judgment expression in a loop. (4)? Is a conditional judgment expression in:. (5) logical operators | | (logical or) and && (and logic) than the left operand () as a conditional expression.

3.3 | | and &&

&& and | | first judgment can be carried to the first operand condition, if it is not a Boolean value to ToBoolean casts, and then execute condition judgment.

For | |, if the condition judgment result to true will return the value of the first operand, if to false will return the value of the second operand.

Am&, on the other hand, returns the second operand if true, and the first if false.

a || b;
// Roughly equivalent to:
a ? a : b;
a && b;
// Roughly equivalent to:
a ? b : a;
Copy the code

The main reason why a | | b and a? A: B is roughly the same because they return the same result with a slight difference. In a? In a: b, if a is a more complex expression (such as a function call with side effects, etc.), it may be executed twice (if the first result is true). In a | | b in a executed only once, the result is used to condition judgment, and returns the result (if applicable). A && B and a? B: The same is true of a.

function foo() {
 console.log( a );
}
var a = 42;
a && foo(); / / 42
Copy the code

If the condition fails, a && foo() quietly terminates (also known as “short circuiting”), and foo() is not called.

Why a && (b | | c) such expressions in the if and the for no problem?

var a = 42;
var b = null;
var c = "foo";
if (a && (b || c)) {
 console.log( "yep" );
}
Copy the code

Here a && (b | | c) is, in fact, the result of the foo, not true, and then by the if foo casts for Boolean value, so the result is true. So you see, there’s an implicit cast. If you want to avoid implicit casts you have to do this:

if(!!!!! a && (!! b || !! c)) {console.log( "yep" );
}
Copy the code

3.4 Cast symbols

ES6 allows explicit casts from symbols to strings, whereas implicit casts produce errors

var s1 = Symbol( "cool" );
String( s1 ); // "Symbol(cool)"
var s2 = Symbol( "not cool" );
s2 + ""; // TypeError
Copy the code

4. Loose equality and strict equality

4.1 General Comparison

  1. Equality comparison between strings and numbers

(1) If Type(x) is a number and Type(y) is a string, return the result of x == ToNumber(y). (2) If Type(x) is a string and Type(y) is a number, return ToNumber(x) == y.

  1. Equality comparison between other types and Boolean types

(1) if Type(x) is Boolean, return ToNumber(x) == y; (2) If Type(y) is a Boolean Type, return the result of x == ToNumber(y).

  1. Equality comparison between null and undefined

(1) If x is null and y is undefined, the result is true. (2) If x is undefined and y is null, the result is true.

  1. Equality comparison between objects and non-objects

(1) If Type(x) is a string or number and Type(y) is an object, return the result of x == ToPrimitive(y); (2) If Type(x) is an object and Type(y) is a string or number, return the result of ToPrimitive(x) == y.

var a = "abc"; 
var b = Object(a); // Same as new String(a)
a === b; // false 
a == b; // true
Copy the code

A == b results in true because B is cast via ToPrimitive (also known as unboxed or unwrapped) and returns the scalar basic type value ABC, which is equal to a.

Some values do not, however, because == other higher-priority rules in the algorithm. Such as:

var a = null; 
var b = Object(a); // Same as Object()
a == b; // false 
var c = undefined; 
var d = Object(c); // Same as Object()
c == d; // false 
var e = NaN; 
var f = Object(e); // Same as new Number(e)
e == f; // false
Copy the code

Null and undefined cannot be boxed because there are no corresponding encapsulated objects. Object(null) and Object() both return a regular Object.

NaN can be encapsulated as a digitally encapsulated object, but when unwrapped NaN == NaN returns false because NaN is not equal to NaN

4.2 Relatively rare Cases

  1. Return other numbers
Number.prototype.valueOf = function() {
 return 3;
};
new Number( 2) = =3; // true
Copy the code

Both 2 and 3 are numeric primitives, and the number.prototype.valueof () method is not called. Number(2) involves a ToPrimitive cast, so valueOf() is called.

if (a == 2 && a == 3) {
 // ..
}

1 / / method
let i = 1;
Number.prototype.valueOf = function() {
 return i++;
};
var a = new Number( 42 );
if (a== 1 && a == 2 && a == 3) {
 console.log( "Yep, this happened." );
}

2 / / method
let a = [1.2.3];
a.valueOf = a.shift;
// a.toString = a.shift;
if (a == 1 && a == 2 && a == 3) {
	console.log('OK');
}

3 / / method
let a = {
	i: 0.toString() {
		return ++this.i; }};if (a == 1 && a == 2 && a == 3) {
	console.log('OK');
}

4 / / method
let i = 0;
Object.defineProperty(window.'a', {
	get() {
		//=> Trigger when obtaining window.a
		return ++i;
	},
	set() {
		//=> Trigger when setting a property value for window.a}});if (a == 1 && a == 2 && a == 3) {
	console.log('OK');
}
Copy the code
  1. Equality comparison of false values
"0"= =null; // false
"0"= =undefined; // false
"0"= =false; // true = true;
"0"= =NaN; // false
"0"= =0; // true
"0"= =""; // false
false= =null; // false
false= =undefined; // false
false= =NaN; // false
false= =0; // true = true;
false= =""; // true = true;
false= = [];// true = true;
false= = {};// false
""= =null; // false
""= =undefined; // false
""= =NaN; // false
""= =0; // true = true;
""= = [];// true = true;
""= = {};// false
0= =null; // false
0= =undefined; // false
0= =NaN; // false
0= = [];// true = true;
0= = {};// false
Copy the code
  1. extreme
[] = =! []// true
Copy the code

! What does the operator do? According to the ToBoolean rule, it performs an explicit cast of the Boolean value (while reversing the parity bits). So [] ==! [] becomes [] == false. False == [] false == []

2= = [2]; // true
""= = [null]; // true
Copy the code

[2] in the first line is converted to 2, and then to 2 by ToNumber. The [null] in the second line is directly converted to “”.

  1. Integrity check
"0"= =false; // true = true;
false= =0; // true = true; `
false= =""; // true = true;
false= = [];// true = true;
""= =0; // true = true;
""= = [];// true = true;
0= = [];// true = true;
Copy the code

There are four cases where == false is involved, which we said earlier should be avoided and should not be hard to master. Now there are three:

""= =0; // true = true;
""= = [];// true = true;
0= = [];// true = true;
Copy the code

DoSomething (0) and doSomething([])

function doSomething(a,b) {
 if (a == b) {
 // .. }}Copy the code

So will doSomething(“,0) and doSomething([],””).

  1. Use implicit casts safely

We should carefully examine the values on both sides of ==, and the following two principles can effectively prevent us from making mistakes.

• Never use == if both values are true or false. • Try not to use == if the values on both sides have [], “”, or 0.

One case in which casting is perfectly safe is the Typeof operation. Typeof always returns one of seven strings, with no empty strings. So no implicit casts occur during type checking. Typeof x == “function” is 100% safe,

Alex Dorey, who goes by @Dorey on GitHub, has created a chart on GitHub showing the various equivalence comparisons,

5. Abstract relationship comparison

If both sides of the comparison are strings, the comparison is done alphabetically:

var a = ["42"];
var b = ["043"];
a < b; // false
Copy the code

A and B are not converted to numbers because ToPrimitive returns strings, so the strings “42” and “043” are compared here, which start with “4” and “0”, respectively. Because “0” is less than “4” alphabetically, the final result is false. In the same way:

var a = [4.2];
var b = [0.4.3];
a < b; // false
Copy the code

A is converted to “4, 2” and B to “0, 4, 3”, again in alphabetical order. Such as:

var a = { b: 42 };
var b = { b: 43 };
a < b; // ??
Copy the code

The result is false because a is [object object] and b is [object object], so the alphabetical order a < b is not true. The following example is a little strange:

var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false
a > b; // false
a <= b; // true
a >= b; // true
Copy the code

Why is a == b not true? They have the same string value (“[object object]”). Actually, that’s not the case, and you can recall that we talked about equality of objects earlier.

But if a < b and a == b result in false, why should a <= b and a >= b result in true? Because according to the specification A <= b is processed as B < a, and then the result is reversed. Since the result of b < a is false, the result of a <= b is true.

This may be very different from what we thought, which is that <= should be less than or equal to. In fact, <= in JavaScript means no greater than (i.e. (a > b), handle as! (b (a)). Similarly, a >= b is treated as b <= a.

There is strict equality in equality comparison, but there is no strict relational comparison. In other words, there is no other way to avoid implicit casts in a < b than to ensure that a and B are of the same type.

As with integrity checks for == and ===, we should use casts when necessary and safe, such as: 42 < “43”. In other words, to be safe, you should explicitly cast the values in the relationship comparison:

var a = [ 42 ];
var b = "043";
a < b; // false -- string comparison!
Number( a ) < Number( b ); // true -- number comparison!
Copy the code