Data type is the computer language entry knowledge, for the whole JS learning is particularly important, because the design of JS, often encounter boundary data type problem, so that the code only in a specific type, can continue to execute

In interviews, handwritten code is often examined, so it is better to consider the boundary judgment question, which is easier to score points

Data type concept

> JS数据类型一共有8种
基础数据类型: undefined, Null, Boolean, String, Number, Symbol, BigInt
引用数据类型: object 
  object包含: Array,RegExp,Data, Math, Function
Copy the code

The following two points are important to understand, because the various JavaScript data types end up in different memory after initialization, so the above data types can be roughly divided into two categories for storage:

  1. The base type is stored in stack memory, and when referenced or copied, an identical variable is created;
  2. Reference types are stored in heap memory and store addresses. Multiple references point to the same address. There is a concept of “sharing” involved.

demo1

let a = { name: 'lee', age: 18 } let b = a; console.log(a.name); // first console, result 'lee' b.name = 'son'; console.log(a.name); // Second console, result 'son' console.log(b.name); // the third console, result 'son' after changing b.name, a, b attribute name is 'son', this uses the property of reference type sharing, i.e. one value changes, the other changesCopy the code

demo2

let a = { name: 'Julia', age: 20 } function change(o) { o.age = 24; o = { name: 'Kath', age: 30 } return o; } let b = change(a); // Notice there is no new console.log(b.age); {name: "Kath", age: 30} console.log(a.age); {name: "Julia", age: 24} cause: Function and return bring something different here. The function passes in o, which is the memory address of the object in the heap, and does change the age property of object A by calling O.age = 24 (line 7). {name: "Kath", age: 30}} line 12 returns the address of the argument o, storing {name: "Kath", age: 30}, and finally returning b is {name: "Kath", age: 30}. If line 12 is removed, b returns undefined.Copy the code

Data type detection

< p style = "max-width: 100%; clear: both; min-height: 1em; Let you write a piece of code that identifies the various data types in JavaScript, and so on. There are many similar questions, and they are often used in our daily work. During the interview, answer "judged by typeof", then there is no other answer, but the answer of the interviewer is not satisfying, because he is the chief study of JS, the depth of the data type of the understanding, so to do first clear judgment method of various data types, and then to sum up, a satisfactory answer to the interviewer.  In fact, there are many ways to determine data types, such as Typeof and Instanceof. This paper mainly introduces three methods of data type detection that are often encountered in work.Copy the code

The first judgment method: Typeof

This is one of the more common ones, so use this method in a piece of code.

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'
Copy the code

As you can see, the first six are basic datatypes, but why is the sixth null typeof ‘object’? Note that typeof NULL outputs object, but this is a long-standing JS Bug, does not mean that NULL refers to data types, and null itself is not an object. Therefore, NULL returns problematic results after typeof and cannot be used as a method to determine null. If you need to check whether the value is null in an if statement, you can check it directly by ‘===null’.

Note that if you refer to a datatype (Object), you can use typeof (function) as’ Object ‘and (function) as’ Object’.

The second judgment method: instanceof

The instanceof method can be used to determine the data type of the new object. The instanceof method can be used to determine the data type of the new object.

let Car = function() {}
let benz = new Car()
benz instanceof Car // true
let car = new String('Mercedes Benz')
car instanceof String // true
let str = 'Covid-19'
str instanceof String // false
Copy the code

The instanceof method is used to determine the type of data. If you want to implement an instanceof method, what should you do?

Look at the code below.

Function myInstanceof(left, right) {// Return false if(typeof left! == 'object' || left === null) return false; Let proto = object.getProtoTypeof (left); If (proto === null) return false; if(proto == null) return false; if(proto === right.prototype) return true; // Find the same prototype Object, return true proto = object.getProtoTypeof (proto); } // check myInstanceof console.log(myInstanceof(new Number(123), Number)); // true console.log(myInstanceof(123, Number)); // falseCopy the code

Now that you know the two ways to determine data types, what are the differences between them? The following two points are summarized:

  1. Instanceof can accurately determine complex reference data types, but not the underlying data types.

  2. Typeof also suffers from the fact that while it can determine the underlying data type (except null), it cannot determine any reference data type other than the function type.

In short, typeof and Instanceof alone are not sufficient for all scenarios, and can only be judged by mixing them. But this way of judging is actually only the majority of cases, and it is also uncomfortable to write.

The author recommends the third method below to solve the data type detection problem better than the two above.

The third judgment method: Object. The prototype. ToString

ToString () is an Object prototype method that returns uniformly a string of the format “[Object Xxx]”, where Xxx is the type of the Object. For Object objects, toString() returns [Object Object]; Other objects need to be called by call to return the correct type information. Let’s look at the code.

Object. The prototype. ToString ({}) / / "[Object Object]" Object. The prototype. ToString. Call ({}) / / same as above result, Plus the call also ok Object. The prototype. ToString. Call (1) / / "[Object Number]" Object. The prototype. ToString. Call (' 1 ') / / "[Object String]" Object.prototype.toString.call(true) // "[object Boolean]" Object.prototype.toString.call(function(){}) // "[object Function]" Object.prototype.toString.call(null) //"[object Null]" Object.prototype.toString.call(undefined) //"[object Undefined]" Object.prototype.toString.call(/123/g) //"[object RegExp]" Object.prototype.toString.call(new Date()) //"[object Date]" Object.prototype.toString.call([]) //"[object Array]" Object.prototype.toString.call(document)  //"[object HTMLDocument]" Object.prototype.toString.call(window) //"[object Window]"Copy the code

This code can be seen from the above Object. The prototype. ToString. The call () is a good way to judge a reference type, it can even make a distinction between the document and the window.

Note that this method returns “[object Xxx]”, and the first letter of “Xxx” in the string must be capitalized (note: typeof returns lowercase).

So let’s implement a global general datatype determination method to deepen your understanding, the code is as follows.

function getType(obj){ let type = typeof obj; if (type ! == "object") {// Return type; } // If typeof returns object, Regular returns the return Object. The prototype. ToString. Call (obj). Replace (/ ^ \ [Object (\ S +) \] $/, "$1"); } /* Regex () {// Regex () {// regex () {// regex () {// regex () {// */ getType([]) // "Array" typeof [] is object, ToString returns getType('123') // "string" typeOF directly returns getType(window) // "window" toString returns getType(null) // "null" uppercase letter, Typeof NULL is object, GetType (undefined) // "undefined" typeof returns getType() // "undefined" typeof returns getType(function(){}) // "Function" typeof can determine, so lowercase getType(/123/g) //"RegExp" toString returnsCopy the code

There are many other methods, such as array isArray, number isNumber and so on, need to choose the appropriate method to use in the work according to the scene

Data type conversion

In daily business development, we often encounter JavaScript data type conversion problems, which need to take the initiative to cast, and sometimes JavaScript will carry out implicit conversion, so we need to pay attention to implicit conversion.

So what’s going on in this section?

Let’s take a look at some code

'123' == 123 // false or true? '' == null // false or true? '' == 0 // false or true? [] == 0 // false or true? [] == '' // false or true? [] = =! [] // false or true? null == undefined // false or true? Number(null) // Return what? Number(") // Returns what? parseInt(''); // Return what? What does {}+10 return? let obj = { [Symbol.toPrimitive]() { return 200; }, valueOf() { return 300; }, toString() { return 'Hello'; } } console.log(obj + 200); // What is the printed value here?Copy the code

The above 12 questions I believe you are not unfamiliar, basically cover some of the situations we are usually easy to overlook, this is when doing data type conversion often encountered coercion and implicit conversion, the following is around the two types of data type conversion in detail

Cast casting

The cast methods include Number(), parseInt(), parseFloat(), toString(), String(), and Boolean(). These methods are similar and can be easily understood literally. All data types are cast by their own methods. Here are a few to illustrate.

In the code above, the result of line 8 is 0, line 9 is also 0, and line 10 is NaN. These are obvious casts because of the use of Number() and parseInt().

In fact, the principle of the above several casts is roughly the same, I will pick two representative methods to explain.

Cast rules for the Number() method

  • If Boolean, true and false are converted to 1 and 0, respectively;

  • If it is a number, return itself;

  • If null, return 0;

  • If undefined, NaN is returned;

  • If it is a string, follow these rules: If the string contains only numbers (or a string of hexadecimal digits starting with 0X / 0X, plus or minus signs are allowed), convert it to decimal; If the string contains a valid floating-point format, convert it to a floating-point value; If it is an empty string, it is converted to 0; NaN is returned if it is not a string of the above format;

  • If Symbol, an error is thrown;

  • Call this method if it is an object and [symbol.toprimitive] is deployed, otherwise call the valueOf() method of the object and convert the returned value according to the previous rules; If the result of the conversion is a NaN, the Object’s toString() method is called, and the conversion returns the corresponding value in the previous order (the Object conversion rules are covered in more detail below).

The following code illustrates the above rules.

Number(true);        // 1
Number(false);       // 0
Number('0111');      //111
Number(null);        //0
Number('');          //0
Number('1a');        //NaN
Number(-0X11);       //-17
Number('0X11')       //17
Copy the code

Examples of common Number conversions are listed, which convert the corresponding non-numeric type to numeric type, and some that simply cannot be converted to numeric type, which only outputs NaN results. Boolean() method cast rules

The rule for this method is that it is true except for undefined, null, false, ”, 0 (including +0 and -0), NaN, and everything else.

This rule should be easy to understand, not so much rules and regulations, but knowledge through code, as shown below.

Boolean(0)          //false
Boolean(null)       //false
Boolean(undefined)  //false
Boolean(NaN)        //false
Boolean(1)          //true
Boolean(13)         //true
Boolean('12')       //true
Copy the code

For the rest of the parseInt(), parseFloat(), toString(), and String() methods, you can sort out the rules the way I want, but I don’t need too much space here.

Implicit type conversion

By logical operators (&&, | |,!) , operators (+, -, *, /), relational operators (>, <, <=, >=), equality operators (==), or if/while conditions. Implicit type conversions occur when two data types are not identical. This is where you need to pay attention, because it’s hidden and easy to miss.

Here are the implicit conversion rules for the more commonly used “==” and “+” symbols.

Implicit type conversion rule for ‘==’

  • If the types are the same, no type conversion is required.

  • If one of the operators is null or undefined, the other operator must be null or undefined to return true. Otherwise, false is returned.

  • If one of them is of type Symbol, return false;

  • If the two operation values are string and number, the string is converted to number.

  • If an operation value is Boolean, convert to number;

  • If an operation value is object and the other is string, number, or symbol, the object is converted to its original type for evaluation (call the valueOf/toString method of object for conversion).

It’s a little confusing to memorize these theories, but it’s easier to understand them by looking at the code, as shown below.

Null == undefined // true Rule 2 NULL == 0 // False Rule 2 "== NULL // False rule 2" == 0 // true Rule 4 The string is converted to Number implicitly before comparison '123' == 123 // True rule 4 The string is converted to Number and then compared 0 == false // true E rule The string is converted to Number and then compared 1 == true // True E rule Var a = {value: 0, valueOf: function() {this.value++; var a = {value: 0, valueOf: function() {this.value++; return this.value; }}; Console. log(a == 1 &&a == 2 &&a ==3); If (a==3) {a==3) {if (a==3) {if (a==3) {if (a==3)Copy the code

After reading the above code and comments against this rule, you can go back to the 12 problems I covered before “casting”. Is it easy to solve?

Implicit type conversion rules for ‘+’

The ‘+’ operator can be used not only for numeric addition, but also for string concatenation. Only if both sides of the ‘+’ sign are numbers, we’re adding; If both sides are strings, concatenation is done without implicit type conversion.

In addition to the more general cases mentioned above, there are some special rules, as shown below.

  • If one of them is a string and the other is undefined, NULL, or Boolean, the toString() method is called to concatenate strings. For pure objects, arrays, regees, and so on, the default conversion method that calls the object has precedence (more on that in the next lecture) before concatenation.

  • If one of them is a number and the other is undefined, NULL, Boolean, or a number, it is converted to a number for addition, again referring to the previous rule.

  • If one of them is a string and the other is a number, they are concatenated according to string rules.

Again, the code is used to understand the above rules, as shown below.

'1' = '1'; '1' = '1'; '1' = '1'; Null null null string '1' + true // "1true" rule 1, True convert string '1' + 1n // '11' special string and BigInt add, BigInt convert string 1 + undefined // NaN rule 2, undefined convert numeral add NaN 1 + null // 1 rule 2, Null converts to 0 1 + true // 2 Rule 2, true converts to 1, both add 2 1 + 1n // Error cannot mix BigInt and Number types directly add '1' + 3 // '13' Rule 3, string concatenationCopy the code

In general, JavaScript type conversions still prefer to convert to strings if there are strings in the data, because as you can see from rule 3, adding strings and numbers returns a string, which is something to be concerned about here.

Now that we know the conversion rules for ‘+’, let’s finally look at the conversion rules for Object.

Object conversion rules

The rules for converting objects call the built-in [ToPrimitive] function first, and the rule logic is as follows:

  • If the Symbol. ToPrimitive method is deployed, call it first and return it.

  • Calls valueOf(), which returns if converted to an underlying type;

  • Call toString(), which returns if cast to base type;

  • If none of the underlying types are returned, an error is reported.

It’s a little hard to understand directly, so look at the code directly, or you can type it out on the console yourself to get a better impression.

var obj = { value: 1, valueOf() { return 2; }, toString() { return '3' }, [Symbol.toPrimitive]() { return 4 } } console.log(obj + 1); // We have symbol.toprimitive, so we execute this first; If Symbol. ToPrimitive is deleted, valueOf prints 3; If valueOf is also removed, toString returns '31'(string concatenation) // Look at two special cases: 10 + {} // "10[object object]", note: {} will call valueOf by default, which is {}, not the base type. Continue with the conversion, call toString, return "[object object]", and then perform the '+' operation with 10, following the string concatenation rules, C [1,2,undefined,4,5] + 10 // "1,2, 4,510", note that [1,2,undefined,4,5] will call valueOf by default. Call toString again, return "1,2, 4,5", and then add to 10, again following the string concatenation rule, rule 3 of '+'Copy the code

This is the end of the Object conversion. I hope you can have a deep understanding of the principle and content mentioned above.

summary

Data types are deepened in three ways

  1. Basic concepts of datatypes: This is a must-know knowledge as a foundation for a deeper understanding of JavaScript.

  2. Data type judgment method: typeof and instanceof, as well as the Object. The prototype. The toString judgment data types, handwritten instanceof code fragments, these are daily development often encountered, so you need to have a good master.

  3. Data type conversion mode: Two data types of conversion mode, daily code writing process should pay more attention to implicit conversion, if the understanding is not in place, it is easy to cause bugs in the coding process, get some unexpected results.