preface

This paper is the knowledge collation notes made when learning data types, and found many knowledge loopholes.

In JavaScript programming, we often encounter the problem of boundary data type condition judgment, and much of the code can only be reliably executed under a certain data type.

Especially in the big factory interview, often need to handwritten code, so it is necessary to consider the boundary judgment of data types in advance, and before you write JavaScript logic, so that the interviewer can see your rigorous programming logic and in-depth thinking ability, the interview can add points.

Therefore, this paper will sort out and learn the knowledge of JavaScript data type from the concept of data type, storage method, detection method and conversion method.

Data type concept

There are eight types of JavaScript data:

  • 7 Basic data types (primitive types)
    • Undefined– Used for declared but unassigned values
    • Null– Used for unknown values, representing “none”, “null”, “value unknown”
    • Boolean– used totruefalse
    • String– For strings: A string can contain zero or more characters, so there is no separate single-character type (in C)char)
    • Number– Used for any type of number: integer or floating point, in53-1) + (2 ^Integers in the range
    • Symbol– The unique identifier used to create the object
    • BigInt– Used for integers of arbitrary length
  • 1 complex data type (reference type)
    • Object– A collection of data and functions that can store multiple values as properties.
      • Array– Array object
      • RegExp– Regular object
      • Date– Date object
      • Math– Mathematical object
      • Function– Function object

storage

Because the various JavaScript data types end up in different memory after initialization, the above data types can be divided into two classes to store:

  • Base type – stored in stack memory

    When referenced or copied, a perfectly equal variable is created.

  • Reference type – stored in heap memory

    Addresses are stored, multiple references to the same address, and there is a concept of “sharing” involved.

To understand the concept of “sharing,” let’s look at two examples:

Example 1:

let a = {
  name: 'bnn'.age: 3
}

let b = a;
console.log(a.name);

b.name = 'jj';
console.log(a.name); 
console.log(b.name); 
Copy the code

The answer:

console.log(a.name); // 'bnn'
console.log(a.name); // 'jj'
console.log(b.name); // 'jj'
Copy the code

After executing b.name = ‘jj’, why is the name attribute of both a and B jj?

The reason for this is the “sharing” of reference types, where references to a and B refer to the same address, and one changes as the other changes.

Example 2:

let a = {
  name: 'jay'.age: 5
}

function change(o){
  o.age = 10;
  o = {
    name: 'jj'.age: 3
  }
  return o;
}

let b = change(a);

console.log(b);
console.log(b.age);
console.log(a.age); 
Copy the code

The answer:

console.log(b); // {name: 'jj',age:'3'}
console.log(b.age); / / 3
console.log(a.age); / / 10
Copy the code

Why is b {name: ‘jj’,age:’3′}, a.age becomes 10?

Because the function passes in object A, the age property of object A is modified with O.age = 10.

It then changes o to another address, not the same a, and returns it, so that the value of b is {name: ‘jj’,age:’3′}.

If there is no return o, b would be undefined.

Data type detection

There are many ways to judge data types. The following focuses on three frequently encountered data type detection methods.

typeof

Here’s a look at the Typeof code:

typeof undefined // "undefined"
typeof null // "object"  
typeof "0" // "string"
typeof 0 // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof Symbol(a)// "symbol"
typeof [] // "object"
typeof {} // "object"
typeof console // "object"
typeof console.log // "function"
Copy the code

We can find:

  • Typeof returns the data type as a string.

  • Why is typeof NULL ‘object’?

    This is an early bug in JavaScript and has been retained for compatibility.

    Null is by no means an object; it has its own type.

    To check for NULL, run the === null command.

  • Typeof cannot determine reference data types other than function, which are all objects.

instanceof

The instanceof operator is used to check whether the constructor’s prototype property appears on the prototype chain of an instance object.

Take a look at instanceof in code:

function Person() {};
let jj = new Person();
jj instanceof Person // true

class Fruit {}
let banana = new Fruit();
banana instanceof Fruit // true

Copy the code

In the following code, why does instanceof return true? A is clearly not created by B().

function A() {}
function B() {}

A.prototype = B.prototype = {};

let a = new A();

a instanceof B // true
Copy the code

It is true that A is not created by B().

But Instanceof doesn’t care about the function, but rather the prototype that the function matches the prototype chain.

Here A.__proto__ === A.protoType === B.prototype.

So a instanceof B returns true.

In short, it is prototype, not the constructor, that really determines the type for Instanceof.

instanceofThe principle of

In ES6, the instanceof operator uses the symbol.hasinstance function to determine the relationship.

The function with symbol. hasInstance performs the same operation, but the operands are switched:

function Foo() {}
let f = new Foo();
console.log(Foo[Symbol.hasInstance](f)) // true 
Copy the code

Typically, Instanceof takes prototype chains into account in its checks. In addition, we can set custom logic in the static method symbol.hasinstance.

The implementation process of obj Instanceof Class algorithm is roughly as follows:

  1. If there is a static method symbol.hasInstance, call this method directly:

    Such as:

    // Set the instanceOf check
    // Assume that none of the notLearn attributes are Person, and none of the Learn attributes are Person
    class Person {
      static [Symbol.hasInstance](obj) {
        if (obj.notLearn) return false;
        if (obj.learn) return true; }}let sb = { notLearn: true };
    sb instanceof Person; // false
    
    let me = { learn: true};
    me instanceof Person; //true
    Copy the code
  2. Most classes do not have symbol.hasinstance. In this case, the standard logic is to use obj instanceOf Class to check whether class.prototype is equal to one of the prototypes in OBj’s prototype chain.

    That is, compare them one by one:

    obj.__proto__ === Class.prototype? obj.__proto__.__proto__ === Class.prototype? obj.__proto__.__proto__.__proto__ === Class.prototype? .// If either answer is true, true is returned
    Otherwise, if true is not found at the end of the prototype chain, return false
    Copy the code

There’s another method here obja.isProtoTypeof (objB)

Returns true if objA is in objB’s prototype chain

So, can be obj instanceof Class inspection to the Class. The prototype. IsPrototypeOf (obj).

[] instanceof Object // true
Object.prototype.isPrototypeOf([]) // true
Copy the code

Note: The Class constructor itself does not participate in the check! The check process is only related to the prototype chain and class.prototype.

Object.isPrototypeOf([]) // false
Copy the code
handwritteninstanceof

So how do you implement an Instanceof yourself?

function myInstanceof(left,right) {
  // Right must be a constructor, not an arrow function or an instance object.
  if (typeofright ! = ='function') {throw new TypeError('Right-hand side of \'instanceof\' is not callable')}// left cannot be a basic type. Remember to consider the special case of typeof NULL.
  if(typeofleft ! = ='object'|| left === null) return false;
  
  The object.getProtoTypeof () method returns the prototype of the specified Object
 	let proto = Object.getPrototypeOf(left);
  while(true) {// Loop down the prototype chain
    console.log(proto)
    if(proto === null) return false;
    // Find the same prototype object, return true
    if (proto === right.prototype) return true;
    // If not, move on to the next prototype
    proto = Object.getPrototypeOf(proto)
    console.log(proto)
  }
}

myInstanceof(new Number(1),Number); // true
myInstanceof(1.Number); // false

Copy the code
andtypeofThe difference between

Summarize the following two points:

  • instanceofComplex data types can be correctly judged, but basic data types cannot. ` `
  • typeofBasic data types can be determined (except for NULL), but reference data types, other than the function type, cannot be determined.

In short, typeof and Instanceof alone are not sufficient for all scenarios, and can only be judged by mixing them. The third approach, below, solves the data type detection problem better.

Object.prototype.toString

ToString () is an Object prototype method that returns 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.prototype.toString({}) // "[object Object]"
Object.prototype.toString.call({})  // Same result as above, add call also ok
Object.prototype.toString.call(1)    // "[object Number]"
Object.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

It can be seen that the Object. The prototype. ToString. Call () is a good way to judge a reference type, it can even make a distinction between the document and the window.

This method returns “[object Xxx]”, whereas “Xxx” in the string must be capitalized because it is a class constructor (note: typeof returns lowercase). The way to distinguish a function from a class is to use lowercase functions and uppercase classes

The ultimate way to

So how do you implement a universal method of determining data types?

function getType(obj) {
  // First typeof judgment, if it is a basic data type, return directly
	let type = typeof obj;
  if(type ! = ='object') {return type;
  }
  
  // If typeof returns object, do the following
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\$]/.'$1');
}

/* Code validation, need to pay attention to case, which is typeof judgment, which is toString judgment? Think about the next * /
getType([]) 		// "Array" typeof [] is object, so toString is returned
getType('123') 	// "string" typeof returns directly
getType(window) // "Window" toString returns
getType(null)   Typeof Null is object and toString is required
getType(undefined)   	// "undefined" typeof returns directly
getType()             // "undefined" typeof returns directly
getType(function(){}) // "function" typeof can determine, so the first letter is lowercase
getType(/123/g)      	// "RegExp" toString is returned
getType(Object)	 			// "function" typeof can determine, so the first letter is lowercase
Copy the code

Data type conversion

We often encounter JavaScript data type conversion problems, sometimes we need to actively cast, and sometimes JavaScript implicit conversion, we need to pay attention to the implicit conversion.

Let’s start with a piece of 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(' ')      // Return what?
parseInt(' ');    // Return what?{} +10           // Return what?
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 problems are common cast and implicit casts when doing data type conversions.

Common type conversions

Before we look at casts and implicit conversions, let’s look at four common types of conversions:

String conversion

Conversion occurs when the content is output, and can also be explicitly converted by String(value). String conversions of primitive values are usually obvious.

Digital conversion

Conversions occur when performing arithmetic functions and expressions, and can also be performed explicitly by Number(value).

Digital conversions follow these rules:

value become
undefined NaN
null 0
truefalse 1 s and 0 s
string The number contained in a numeric string with Spaces removed (including hexadecimal strings starting with 0x). If the rest of the string is empty, return 0. Returns if it is not a string in the above formatNaN
Boolean conversion

Conversions occur when logical operations are performed, and explicit conversions can also be performed through Boolean(value).

Boolean conversions follow the following rules:

value become
0, null.undefined , NaN , "" false
Other values true
Object-raw value conversion

All objects are true in a Boolean context. So for objects, there are no to-Boolean conversions, only string and numeric conversions.

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.

Take a look at this code:

var obj = {
  value: 1.valueOf() {
    return 2;
  },
  toString() {
    return '3'},Symbol.toPrimitive]() {
    return 4}}console.log(obj + 1); / / output 5
Copy the code

Since Symbol. ToPrimitive is available, this will take precedence;

If Symbol. ToPrimitive is deleted, valueOf prints 3;

If valueOf is also removed, the toString call returns ’31’ (string concatenation).

Let’s look at a few more examples:

10 + {} // "10[object Object]"
Copy the code

ValueOf is called by default. It is {}, not the base type.

Call toString, return “[object object]”

So I’m going to add plus to 10, and I’m going to do string concatenation.

{} + 10 / / 10
Copy the code

So we find that the interpreter always calls valueOf() first when an object is an operand; In other cases the interpreter always thinks we want a string, so it calls toString() first

So, the object is in front, and the return isNumber; Otherwise, objects are used by defaulttoString

[1.2.undefined.4.5] + 10 / / "1, 2, 4510"
Copy the code

[1,2,undefined,4,5] by default, valueOf will be called first.

Call toString, return “1,2, 4,5”, and then add to 10, again using string concatenation.

Let’s look at casts and implicit conversions:

Cast casting

The cast methods include

  • Number()
  • parseInt()
  • parseFloat()
  • toString()
  • String()
  • Boolean()

These are all methods that can cast data types. How to convert is a combination of the method’s rules and the transformation rules above.

For example, in the 12 questions above:

The result of Number(null) is 0, and the result of Number(” “) is also 0 because Number() is cast.

The result of parseInt(“) is NaN because parseInt() was cast.

Implicit conversion

By logical operators (&&, | |,!) , operators (+, -, *, /), relational operators (>, <, <=, >=), equality operators (==), or if/while conditions. Implicit type conversions occur when two data types are not identical.

Let’s look at the implicit conversion rules for a few symbols that we use a lot.

+
  • Unary operator

    The plus sign + applies to a single value and has no effect on numbers.

    But if the operand is not a number, the plus sign converts it to a number.

    Its effect and Number(…) The same.

    The negative operator is the unary operator of the inverted symbol.

    Note: Unary operators take precedence over binary operators.

    +' ' / / 0
    +null / / 0
    +undefined // NaN
    +true / / 1
    +false / / 0
    
    -' ' / / - 0
    -null / / - 0
    -undefined // NaN
    -true // -1
    -false / / - 0
    Copy the code
  • Binary operator

    • I have numbers on both sides and I add them.

    • String on both sides, string concatenation.

    • As long as either operand is a string, the other operand is also converted to a string.

    • If one is a number and the other is undefined, NULL, Boolean, or a number, it is converted to a number for addition.

    • Binary + is the only operator that supports strings in this way. Other arithmetic operators operate only on numbers and always convert their operators to numbers.

      1 + 2        / / 3
      '1' + '2'    / / '12'
      
      '1' + undefined   // "1undefined" rule 3, undefined conversion string
      '1' + null        // "1NULL" rule 3, null conversion string
      '1' + true        // "1true" rule 3, true converts strings
      
      "" + 1 + 0        // For "10" rule 3, first convert the number 1 to a string: "" + 1 = "1", then get "1" + 0, apply the same rule again to get the final result.
      "9" + 5			// "-9 5" rule 3, string addition adds the number 5 to the end of the string
      2 + 2 + '1'				// Rule 3 of "41", operators work sequentially. The first + adds the two numbers, so returns 4, then the next + adds the string 1 to it, so 4 + '1' = 41.
      
      '1' + 1n          // '11' is a special string that is added to BigInt, which is converted to a string
      
      1 + undefined     // NaN rule 4, undefined convert numbers to add NaN
      1 + null          // 1 Rule 4, null is converted to 0
      1 + true          // 2 Rule 4, true converts to 1, and the two add up to 2
      1 + 1n            // Error cannot add BigInt and Number directly
      
      "" - 1 + 0    		// "-1" rule 5, subtraction - can only be used for numbers, it converts the empty string "" to 0
      "9" - 5			// Rule 5 of -14, subtraction always converts a string to a number, so it converts "-9" to a number -9 (ignoring Spaces at the beginning and end of the string)
      " \t \n" - 2 = -2	// -2 Rule 5, subtraction always converts strings to numbers. When a string is converted to a number, space characters at the beginning and end of the string are ignored. Here, the entire string consists of space characters, including \t, \n, and the "normal" Spaces between them. So, it's like an empty string, so it goes to 0.
      Copy the code

In general, for binary + if there is a string in the operand, the implicit conversion is more likely to be a string, because as you can see from the third rule, adding a string and a number returns a string.

Also, note that the other arithmetic operators operate only on numbers and always convert their operators to numbers.

= =,! = =
  • When comparing values of different types, JavaScript first converts them to a number and then determines the size.

  • null == undefined

    Null and undefined cannot be converted to other types of values for comparison.

    So they can’t be equal to anything other than each other!

    nullundefinedGood CP, love no one but each other!

  • As long as any operand is NaN, == returns false,! == returns true. NaN == NaN returns false.

    This means NaN cannot be compared and is not equal to any value, including itself!

    NaNI don’t love anyone, I don’t love myself!

  • If both operands are objects, it compares whether they are the same object, and true if so.

  • If one operand is an object and the other is not, the valueOf() method of the object is called to get its original value, which is then compared according to the previous rules.

    false= =0  // true rule 1 false turns the number to 0
    true= =1  // true rule 1 true turns the number to 1
    true= =2  // False rule 1 true turns to number 1! True turns the number to 1! Is 1.
    ' '= =0		// True rule 1 empty string is 0
    "5"= =5  // true Rule 1 The string is converted to a number
    
    null= =undefined  // true rule 2 null == undefined
    undefined= =0  // False rule 2 null and undefined are not converted to other types of values!
    null= =0  // Rule 2: null and undefined
    
    "NaN"= =NaN  // False rule 3 NaN does not equal any value!
    5= =NaN  // False rule 3 NaN does not equal any value!
    NaN= =NaN  // False rule 3 NaN does not equal any value! Including yourself!
    NaN! =NaN  // True rule 3 NaN loves no one! I don't love myself! I'm tired of saying this!
    
    var a = {
      value: 0.valueOf: function() {
        this.value++;
        return this.value; }};// Note that a can be equal to 1, 2, 3 again
    console.log(a == 1 && a == 2 && a == 3);  // True rule 5 calls the object's 'valueOf()' method to get its original value
    // If a==3 or the number before it is executed, it is false because value has been added
    Copy the code
<>,< => =
  • Null is converted to 0, and undefined to NaN.

  • When comparing the size of strings, compare them character by character (actually Unicode encoding) :

    1. First, compare the size of the first character of two strings.
    2. If one character is larger (or smaller), the string is larger (or smaller) than the other string. The algorithm ends.
    3. Otherwise, if the first character of two strings is equal, the last character of each string is extracted for comparison.
    4. Repeat the above steps until all characters of a string are compared.
    5. If two strings run out of characters at the same time, they are judged equal, otherwise the unended (and uncompared) string is larger.
    null > 0  // Rule 1: null is converted to 0 for size and comparison
    null> =0  // True rule 1 is converted to 0 for size and comparison
    null= =0  // False Rule 1 For normal equality, null == undefined and not equal to other values. Null is converted to 0 only for size and comparison
    
    undefined > 0  // Rule 2 for size and comparison,undefined is converted to NaN. NaN cannot be compared
    undefined = 0  // False rule 2: Undefined is changed to NaN when comparing sizes and sizes. NaN is not equal to anyone
    undefined0  // Rule 2 for size and comparison,undefined is converted to NaN. NaN cannot be compared
    
    "Z" > "A"  2 / / rules
    "Asd" > "Asa"  2 / / rules
    "Aaa" > "Aa"  2 / / rules
    Copy the code

The last

If have a problem, please big guy correct ~

If helpful, hope to be able to click on the collection ~

References:

  • JavaScript Advanced Programming (version 4)
  • Modern JavaScript Tutorial
  • If the big guy from the core principle of JavaScript
  • Hotpot boy’s handwriting instanceof