1. Classification of types

1.1 Basic Type (Primitive Type)

There are seven basic types: string, number, Boolean, NULL, undefined, symbol, bigint

1.1.1 Accuracy loss

When dealing with decimals and large integers, the problem of inaccurate calculation will occur

Console. log(0.1 + 0.2) // 0.30000000000000004 console.log(0.3-0.2) // 0.09999999999999998 console.log(0.8 * 3) // 2.4000000000000004 console.log(0.3/0.1) // 2.9999999999999996 console.log(66666666666667000) // 66666666666667000Copy the code

The reason for the loss of decimal precision: Computer data is stored in binary, JavaScript uses double precision floating-point encoding, and the storage is limited to 64 bits (sign bit 1 + exponent bit 11 + decimal bit 52). An infinite loop occurs when some decimal systems are converted to binary, for example, 0.1 binary is 0.0001100110011001100… (1100 loop), will result in binary rounding operation (0 round 1, the first precision loss), when the operation (second precision loss) after converting the result to decimal cause calculation error, precision loss.

/ / binary console. Logo (0.1. ToString (2)) / / 0.0001100110011001100110011001100110011001100110011001101 (57, 58 is 1, 57th change from 0 to 1) / / scientific notation (in order to save the storage space) 1.1001100110011001100110011001100110011001100110011001 * 2 ^ 4 / / the sign bit is 0 (positive), index of - 4, small digital 52, Console. log(0.1.toprecision (20)) // 0.10000000000000000555Copy the code

Solution 1:

The result accuracy of narrowing (because the actual business needs to retain a small digital is only two or three, so precision can be narrowed to intercept a few decimal places), start preparing to use toFixed to retain the decimal rounded, but because there will be a cause for decimal precision error last for 5 carry is not correct, So use math.round instead of toFixed to handle rounding

// 3.335 corresponds to 3.334999999999999964, Log (3.335.tofixed (18)) // '3.334999999999999964' console.log(3.335.tofixed (2)) // '3.33' // Function toFixedHandle(number, decimal) {const sign = (number < 0)? 1: 1 const pow = Math.pow(10, Decimal) let numResult = sign * Math. Round (sign * number * pow)/pow / numResult.toString().split(".") if(arrayNum.length > 1 && arrayNum[1].length < decimal){ numResult += new Array(decimal -arrayNum [1].length + 1).join('0')} return numResult} console.log(toFixedHandle(3.335, 2)) // '3.34' console.log(toFixedHandle(3.335, 18)) // '3.335000000000000000' console.log(toFixedHandle(0.1 + 0.2, 2)) / / '0.30'Copy the code

Solution 2:

Because the integer decimal to binary, is divided by two to the remainder, which can be divided into all, so the integer storage and calculation will not appear precision loss, can be based on the decimal point after a number of times multiplied into integer and then divided by multiple restore (the value of the operation can not exceed the js digital maximum security value)

function add(num1, num2) { const num1Digits = (num1.toString().split('.')[1] || '').length const num2Digits = (num2.toString().split('.')[1] || '').length const baseNum = Math.pow(10, Math.max(num1Digits, Num2Digits)) return (num1 * baseNum + num2 * baseNum)/baseNum} console.log(add(0.1, 0.2)) // 0.3Copy the code

Solution three:

Set an error range for floating-point calculations, and if the error can be less than number. EPSILON, we can also consider the result reliable

Function numberepsilon(arg1,arg2){return math.abs (arg1-arg2) < number.epsilon} console.log(numberepsilon(0.1 + 0.2, arg2)) / / true 0.3))Copy the code

Solution 4 :(recommended)

Using THE JS library can easily solve the floating operation problem, avoid the multi-digit and calculation precision loss after the decimal point, such as number-precision, exact-math, math.js, decimal

Import NP from 'number-precision' np. plus(0.1, 0.2) // 0.3, not 0.30000000000000004 NP. Times (3, 0.8) // 2.4, import NP from 'number-precision' np. plus(0.1, 0.2) // 0.3, not 0.30000000000000004 NP. Not 2.4000000000000004Copy the code

There are 53 significant digits (sign bits + decimal places) in a double-precision floating-point number. If the number is more than 52 digits behind the decimal point, it follows the binary rounding principle of 1. MAX_SAFE_INTEGER indicates the maximum safe Number. The calculated result is 9007199254740991, that is, there is no loss of precision within this range (except for decimals).

console.log(Math.pow(2, 53) - 1) // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER.toString(2)) // 11111111111111111111111111111111111111111111111111111
Copy the code

Large integer processing method: If the number of large integers exceeds the maximum security number, change the number to a string or use a third-party library to process the number.

1.1.2 bigint

To manipulate numbers that exceed the maximum safe number, ES10 introduced a new basic type, BigInt, that appends n to an integer literal or calls the bigint function

Console. log(666666666666666667000) // 666666666667000 console.log(666666666666999n) // 6666666666999n // BigInt(), Log (BigInt(' 666666666666999')) // 6666666666999n console.log(BigInt(666666666666999)) // 66666666666667000nCopy the code

1.1.3 symbol

ES6 introduces a new base type, symbol, which represents a unique value. We can use symbol as an object property name (to prevent property name conflicts) or to define a class’s private property/method

Const mySymbol = Symbol('mySymbol') const mySymbol2 = Symbol('mySymbol2') let obj = {[mySymbol]: 'Hello'} obj[mySymbol2] = 'World' console.log(obj[mySymbol]) // Hello console.log(obj[mySymbol2]) // World // class private properties, Symbol value as the key name, Const Person = (function () {const name = Symbol('name') class PersonClass {constructor(n) {this.age = 18 This [name] = n} getName() {return this[name]}} return PersonClass})() const person = new person Console. log(person.age) // 18 console.log(person[Symbol('name')]) // undefined console.log(person.getname ()) //Copy the code

1.1.4 NaN

NaN indicates that a value is not a number and is used to indicate error cases in numeric types, but it is of type number and is not equal to itself, which can be determined using isNaN or number.isnan ()

Console. log(NaN === NaN) // false //isNaN converts an incoming value to a number before determining if it isNaN, Log (isNaN(123)) // false console.log(isNaN(NaN)) // true console.log(isNaN('a123')) // true // number. isNaN(introduced in ES6) first checks if a value type is a Number, then checks if the value is equal to NaN console.log(number.isnan ('a123')) // falseCopy the code

1.2 Reference Types (Object types)

There is only one reference type, object. Array, Function, Date, RegExp, etc., are all native built-in objects and are subtypes of Object

2. Type storage

  • Values of primitive types are stored directly on the stack, storing the values themselves

  • The value of a reference type is stored in the heap, which stores only the spatial address (the address is stored on the stack), and the spatial reference value is stored in the heap for objects, arrays, and function equivalents

// Basic type // copy value const name = 'name' let nameCopy = name nameCopy = 'console.log' (name) // 'name' Console. log(nameCopy) // 'nameCopy' // let nameValue = 'Hello' function changeName(value){value = 'World'} ChangeName (nameValue) console.log(nameValue) // 'Hello' // reference type // Copy space address const obj = {name: 'name' } const objCopy = obj objCopy.name = 'nameCopy' console.log(obj.name) // 'nameCopy' console.log(objCopy.name) // 'nameCopy' // let objValue = {name: 'Hello' } function changeName(value){ value.name = 'World' } changeName(objValue) console.log(objValue.name) // 'World'Copy the code

3. Type detection

3.1 typeof

  • Typeof can accurately detect primitive types (except null), and all reference types other than functions are treated as object

  • The reason why null type is objet: When js stores variables at the bottom level, it will store their type information in the low 1~3 bits of the variable’s machine code. The criterion for determining the type of object is that the first three bits are 0, while all the machine codes of NULL are 0, so it is treated as object

console.log(typeof 1) // number console.log(typeof NaN) // number console.log(typeof true) // boolean console.log(typeof  'str') // string console.log(typeof 555n) // bigint console.log(typeof Symbol('a')) // symbol console.log(typeof undefined) // undefined console.log(typeof null) // object console.log(typeof []) // object console.log(typeof {}) // object console.log(typeof function(){}) // functionCopy the code

3.2 instanceof

Instanceof can detect the type of a reference type based on whether the type can be found in its prototype chain, but it cannot determine the basic type and the prototype chain of the reference type all has Object

console.log([] instanceof Array)  // true
console.log([] instanceof Object) // true
console.log(function() {} instanceof Function)  // true
console.log(function() {} instanceof Object)  // true
console.log(1 instanceof Number)  // false
console.log(new Number(1) instanceof Number)  // true
console.log(new Number(1) instanceof Object)  // true
Copy the code

3.3 the constructor

Constructor verifies the type by judging the variable’s constructor (undefined and null have no constructor attribute), but constructor’s judgment of the type is inaccurate once its prototype is changed

console.log('str'.constructor === String)  // true
console.log(false.constructor === Boolean) // true
console.log((1).constructor === Number)     // true
console.log(([]).constructor === Array)  // true
console.log(([]).constructor === Object)  // false
console.log((function() {}).constructor === Function) // true
console.log(({}).constructor === Object) // true

function Fn(){}
Fn.prototype = new Object()
const f = new Fn()
console.log(f.constructor === Fn)     // false
console.log(f.constructor === Function)  // false
console.log(f.constructor === Object)  // true
Copy the code

3.4 the Object. The prototype. ToString. Call (recommended)

  • When the Object. The prototype. ToString takes the Object’s [[Class]] ([[Class]] is a string, specification defines a string value of Object classification, it is an internal attributes, and cannot be modified). ES6 Symbol is introduced. The toStringTag allows us to customize the Object type, by redefining the data Symbol. Rewritten toStringTag Object. The prototype. ToString. Call () returns the data type

  • Call is used to change the this pointer inside the toString function to point to the object we need to detect and get the internal attribute [[Class]]. Object. The prototype. ToString after the call, if the parameter is null or undefined, return the result directly, for basic types, the basic types with their corresponding reference type packaging (box), to Object, [[Class]] to achieve this Object property values

  • Use the Object. The prototype. ToString instead of obj. ToString, because Array, such as the Function types are rewriting the toString method

const checkType = Object.prototype.toString console.log(checkType.call(1)) // [object Number] console.log(checkType.call(new String(1))) // [object Number] console.log(checkType.call(new Object(1))) // [object Number] console.log(checkType.call(true)) // [object Boolean] console.log(checkType.call('str')) // [object String] console.log(checkType.call(undefined)) // [object Undefined] console.log(checkType.call(null)) // [object Null] console.log(checkType.call(5n)) // [object BigInt] console.log(checkType.call(Symbol())) // [object Symbol] console.log(checkType.call(new Map())) // [object Map] console.log(checkType.call([])) // [object Array] console.log(checkType.call(new Date())) // [object Date] console.log(checkType.call(function(){})) // [object Function] Console. log(checktype.call ({})) // [object object] // symbol.toStringTag Redefines the type let myObj = {[symbol.tostringTag]: 'Sdj} the console. The log (Object. The prototype. ToString. Call (myObj)) / / [Object Sdj] / / different objects rewrite the toString method the console. The log (function () {}.toString()) //'function () {}' console.log([1, 2, 3]. The toString ()) / / '1, 2, 3'. The console log ({}. The toString ()) / / 'function () {}'Copy the code

Object.prototype.toString.callDeficiency in

  • Use the Object. The prototype. ToString. Call the base type with their corresponding reference type packaged and create many temporary objects (use will be destroyed after), so we can use the typeof to early detection basic types (pay attention to the particularity of null)

  • You can use instanceof to distinguish custom object types

const checkType = function (obj) { if (obj === null) return 'null' if (typeof obj ! == 'object') return typeof obj return Object.prototype.toString.call(obj).slice(8, -1).toLocaleLowerCase() } console.log(checkType(1)) // 'number' console.log(checkType(true)) // 'boolean' console.log(checkType('str')) // 'string' console.log(checkType(undefined)) // 'undefined' console.log(checkType(null)) // 'null' console.log(checkType(5n)) // 'bigint' console.log(checkType(Symbol())) // 'symbol' console.log(checkType(new Map())) // 'map' console.log(checkType([])) // 'array' console.log(checkType(new Date())) // 'date' console.log(checkType(function(){})) // 'function' console.log(checkType({})) // 'object'Copy the code

4. Type conversion

4.1 Basic Type Dereference Type (Stuffing operation)

4.1.1 Basic packaging types

  • Numeric, String, and Boolean values all have their own corresponding wrapper objects (such as the Number, String, and Boolean constructors), and data that converts a primitive type to an object by calling their respective constructors with new can be called a primitive wrapper type

  • When we use a property or method of a primitive type, JS implicitly automatically creates an object instance of the wrapper type, then calls the property or method, and finally destroys the instance

Const STR = "ABC" console.log(str.toupperCase ()) // ABC, equivalent to new String(STR).touppercase ()Copy the code
  • ObjectThe constructor can take a variable of any type and perform different conversions. Other primitive types can also be passed in
console.log(new Object('1'))      // String{'1'}
console.log(new Object(1))        // Number{1}
console.log(new Object(true))     // Boolean{true}
console.log(new Object(Symbol())) // Symbol{Symbol()}
console.log(new Object(1n))       // BigInt{1n}
Copy the code

4.2 Changing Reference Type to Basic Type (Unpacking)

You need to understand the valueOf and toString functions before you can understand unpacking operations

2 the toString

  • ToString returns a string representing the object. The toString method is automatically called when the object is represented as a text value or referenced as a desired string. When the value is numeric or bigINT, arguments can be passed to define the cardinality of the number, which is converted to a specified base string

  • Primitive data types call toString, which is replaced by a string, null and undefined have no toString methods on their prototypes

Console. log('123a'.toString()) // '123a' console.log((123).tostring ())) // '123'. Console.log ((123).tostring (2)) // '1111011' console.log(true.tostring ()) // 'true' console.log(123n.toString()) // '123' console.log(Symbol().toString()) // 'Symbol()' console.log(null.toString()) // Cannot read properties of null (reading 'toString') console.log(undefined.toString()) // Cannot read properties of undefined (reading 'toString')Copy the code
  • Reference data type invocationtoString, according to the[[class]]Type to get different values
/ / Object, Log ({}.toString()) // "[object object]" console.log({a:1}.tostring ())) // "[object Object]" //Array Array, convert each item to a string and then concatenate with ",", Is equivalent to directly call the join () method to the console. The log ([1, 2, 3]. The toString ()) / / '1, 2, 3'. The console log ([' ' ' ', ']. ToString ()) / / ', ' console.log([].toString()) // '' console.log([{ a: 1 }, { a: 2}].toString()) // '[object object],[object object]' //Function Console. log(function () {return 3}.tostring ()) // 'function () {return 3}' console.log(class Test {return 3}) constructor(data) { this.data = data } }.toString()) // 'class Test { constructor(data) { this.data = data } }' The console. The log (/ ^ 1 d {10} $/. The toString ()) / / / ^ 1 'd {10} $/'/Date/Date, Console.log (new Date().tostring ()) // 'Tue Nov 09 2021 11:09:37 GMT+0800 '//Map, Set, and Promise, The Symbol. ToStringTag attribute is present in the prototype, Log (new Map().toString()) // "[Object Map]" console.log(new Set().toString()) // "[object Set]" console.log(promise.resolve ().toString()) // "[object Promise]" // Wrapper objects directly return the string console.log(new) of their basic type values Object(1).toString()) // "1" console.log(new Object(true).toString()) // "true"Copy the code

4.2.2 the valueOf

  • ValueOf returns the basic type valueOf the current object, similar to toString, with automatic invocation

  • The basic data type call valueOf directly returns the caller’s original valueOf the same type

  • When valueOf is called by a reference type, the date object returns a number of milliseconds, otherwise the caller itself is returned

4.2.3 Unpacking operation -toPrimitive

  • ToPrimitive(Value, type?) automatically calls toPrimitive (value, type?) when an object is converted to its basic type. Where value is the object to be called and type is the expected result type (number and string, default number)

    If the object returns a primitive type value, js converts the value to a string. If the object has no toString() method or returns a non-primitive type after the call, valueOf() is called. The valueOf() method throws a type error exception if it returns a value other than a primitive type.

    If type is number, valueOf() is called first, followed by toString() (default type is string if the object is a date).

  • ToPrimitive: Symbol. ToPrimitive: Symbol. ToPrimitive: Symbol. ToPrimitive: Symbol. ToString () or valueOf() methods are no longer called, i.e. symbol.toprimitive has a higher priority than toString() and valueOf().

4.3 Convert other Types to Digits

This Number

The Number function converts the value to a Number, and if the value cannot be converted, the Number() function returns NaN

Log (Number('')) // 0 console.log(Number('123')) // 123 console.log(Number('0x16')) // 22 Console. log(Number('123a')) // NaN // Boolean type to Number console.log(Number(true)) // 1 console.log(Number(false)) // 0 //null to Number Console. log(Number(null)) // 0 //undefined to numeral console.log(Number(undefined)) // NaN //bigint to numeral Console. log(Number(123n)) // 123 //symbol convert to console.log(Number(symbol ())) // Uncaught TypeError: Cannot convert a Symbol value to a number, according to toPrimitive, ToString () console.log(Number({})) // NaN, valueOf->{}, toString->"[object object]", NaN console.log(Number([])) // 0, toString->"", 0 console.log(Number([1])) // 1, valueOf->[1], Console. log(Number([1,2])) // NaN, valueOf->[1,2], toString-> 1,2, Log (Number(new Object('123'))) // 123, valueOf->'123' 123 console.log(Number(function(){})) // NaN, valueOf->function(){}, toString-> function(){}, NaN console.log(Number(new Date())) // 1636165977196 milliseconds, valueOf->'1636165977196', and 1636165977196Copy the code

4.3.2 parseInt and parseFloat

  • The parseInt(STR, radix) function parses a string into the specified base, parsing in left-to-right order, stopping if non-numeric characters are encountered, and finally returning a decimal integer

    STR: The value to be parsed, converted to a string if the argument is not a string. Whitespace at the beginning of the string will be ignored

    Radix: optional parameter, numeric base, can be interpreted as base, range from 2 to 36, default to decimal when radix is not filled in, but note that 10 is not the default value, when the string starts with 0x it will be directly processed to hexadecimal, when the parsed value exceeds the numeric base it will stop parsing

Log (parseInt('123')) // NaN console.log(parseInt('123')) // 123 console.log(parseInt('0x16')) // 22 Console. log(parseInt('5.66')) // 5 console.log(parseInt('123aa')) // 123 // Boolean to number console.log(parseInt(true)) // NaN Log (parseInt(null)) // NaN //undefined to digital console.log(parseInt(undefined)) // NaN //bigint to digital Console. log(parseInt(123n)) // 123 //symbol to number console.log(parseInt(symbol ())) // Uncaught TypeError: Cannot convert a Symbol value to a number, according to toPrimitive, ToString () console.log(parseInt({})) // NaN, valueOf->{} ToString ->"[object object]" console.log(parseInt([])) ToString ->" console.log(parseInt([1])) // 1, toString->" console.log(parseInt([1])) // 1, ToString ->"1,2" console.log(parseInt(new Object('123')) Console. log(parseInt(function(){})) // NaN, valueOf (){}, ToString ->"function(){}" console.log(parseInt(new Date())) // NaNCopy the code
// console.log(parseInt('123', 6)) // 51 = 1*6^2 + 2*6^1 + 3*6^0 Since ^ is the bitwise operator, and the power should be used here, it corresponds to 1* math.pow (6, 2) + 2* math.pow (6, 1) + 3* math.pow (6, 0) // Scientific counting effects // When numbers are converted to strings, if a number is a decimal, Log (parseInt(0.000001)) // 0 console.log(parseInt(0.0000001)) // 1, parseInt(0.0000001)), Scientific notation is' 1E-7 'console.log(parseInt(0.0000051)) // 0 console.log(parseInt(5.0000001)) // 5 Console. log(parseInt('0.0000001')) // 0 // Base digits corresponding to letters // A~Z (A~Z) indicates 10~35 console.log(parseInt(' ABC ', 11)) // 10. Log (parseInt(false, 16)) // 250, f-15, A-10, L-21, The results calculated for 15 * math.h pow (16, 1) + 10 * math.h pow (16, 0) / / map, filter and so on transfer and the console. The log ([' 1 ', '2', '3', '12'. // [1, NaN, NaN, 3], ['1', '2', '3', '5','2']. Map ((currentValue, index, arr) => parseInt(currentValue, index) Default is decimal, therefore 1 // parseInt('2', 1) base 1, range 0, therefore NaN // parseInt('3', 2) base 2, range 0-1, therefore NaN // parseInt('12', 3) base 3, Math.pow(3, 1) + 2* math.pow (3, 0), therefore 5 // parseInt('26', 4) cardinality 4, range 0-3, 6 is out of range so parsing stops, i.e., parsing only string 2, result 2* math.pow (4, 0), Log (['1', '2', '3', '12', '26']. Filter (parseInt)) // ['1', '12', '26']Copy the code
  • parseFloatandparseIntThe difference between integers and floating-point numbers,parseFloatWithout the second argument, the default is resolved to decimal, and parsing the second decimal point is invalid

4.4 Change other Types to Strings

4.4.1 String

The String function converts a value to a String, following toPrimitive rules. By default, toString is called first, so it returns the same result as toString. Except for null and undefined, which return their own strings and other base strings that do not return numbers like (123).toString(2)

4.5 Convert other Types to Boolean types

4.5.1 Boolean

Boolean functions convert values to Boolean values, except for undefined, null, +0, -0, NaN, “, false will be converted to false, and all other values will be true

console.log(Boolean(undefined))            // false
console.log(Boolean(null))                 // false
console.log(Boolean(0))                    // false
console.log(Boolean(0n))                   // false
console.log(Boolean(NaN))                  // false
console.log(Boolean(''))                   // false
console.log(Boolean(false))                // false
console.log(Boolean(''))                   // false
console.log(Boolean('0'))                  // true
console.log(Boolean(-Infinity))            // true
console.log(Boolean({}))                   // true
console.log(Boolean([]))                   // true
console.log(Boolean(new Boolean(false)))   // true
Copy the code

4.6 Implicit conversion caused by various operators

When the operator in the operation, if both sides do not have a unified data type, CPU cannot be computed, then the compiler will automatically the data on both sides of the operators to do a data type conversion, converted to the same data type in the operation, but the implicit conversion is a double-edged sword, use it while it is possible to write less code but sometimes hard to find bugs, So you need to know more about the conversion rules (TS does not allow arbitrary implicit type conversions)

4.6.1 Comparison Operators (==, >)

Console. log(null == undefined) console.log(null == undefined) // true, undefined, null False console.log(null == false) // false console.log(NaN == NaN) // false console.log(true == 1) // true, Log ('true' == 1) // false, where true is a string converted to NaN console.log('' == 0) // true, The empty string is converted to 0 console.log('c' > 'ab') //true Console. log(1 > 'ab') //true, the string will be converted to NaN, false against any value console.log(null >= 0) //true, null will be converted to 0, Console. log(undefined >= 0) //false, undefined will be converted to NaN, false against any value, Do not use >= > < <= to compare a variable that may be null/undefined/that has a reference type. Convert the reference type to the base type console.log({} == true) // false, {} converts to "[object object]", true converts to 1, and "[object object]" converts to NaN console.log({} == {}) // false Different object space addresses are different console.log([] == 0) // true, [] first converts to empty string, then converts to 0 console.log([NULL] == false) // true, [null] first converts to null string, Console. log(['1'] == ['1']) // false, console.log([] > -1) // true, [] first converts to empty string, Console. log(['1'] > 0) // true, ['1'] converts first to string 1 and then to number 1Copy the code

4.6.2 Logical Operators (! , | |, & &)

  • ||The statements on either side of the operator are converted to Boolean, evaluating the operands from left to right if the result istrue, stops the calculation, returns the initial value of the operand, and returns the last operand at the end of the calculation
The console. The log (true | | false) / / true on the console. The log (null | | false | | 'aa') / / aa console. The log (true | | 2 && false) / / true, With operation & priority than or operation | |, equivalent to a true | | && false (2) the console. The log (null | | true & false) / / falseCopy the code
  • &&The statements on either side of the operator are converted to Boolean, evaluating the operands from left to right if the result isfalse, stops the calculation, returns the initial value of the operand, and returns the last operand at the end of the calculation
The console. The log (true & false) / / false console. The log (null && false && 'aa') / / null console. The log (null && 1 | | true && 0) / / 0, Equivalent to null && (1) | | (true && 0)Copy the code
  • !The operator converts the operation value to a Boolean type and then inverts it
console.log(! '11') // false, 11 converts to true, true inverts to false console.log(!! '11') // true, 11 converts to true, true to false, false to true console.log(! {} == []) // true! {} converts to false, false converts to 0, [] converts to empty string, and empty string converts to 0Copy the code

4.6.3 Arithmetic Operators (+, -, *, /)

  • - * /An equal operator operates by converting both sides of a symbol to a number
Console. log(1-true) // 0 console.log(1 * [3]) // 3, following toPrimitive rules, first through valueOf, then through toString conversion toString 3, Log (1 * {}) // NaN console.log(1 * null) // 0 console.log(1 * undefined) // NaNCopy the code
  • The + operator, when used as a unary operator, is directly converted to a numeric type, equivalent to the Number function

  • When the + operator is a binary operator, when one side is a string or reference type, both sides are converted to string concatenation; otherwise, both sides are converted to numbers for operation

console.log(+ '1a') // NaN console.log(+ true) // 1 console.log(+ {}) // NaN console.log(+ []) // 0 console.log('a' + 1) Log ({} + 1) // '[object object]1', console.log([2] + 1) // '21', Log (true + 1) // 2 console.log([] + []) // "" console.log({} + {}) // "[Object object][object object]" {} +[] // 0, when the statement starts with '{', '{}' will be treated as a block statement instead of an object literal, equivalent to the +[] statement, i.e. the Number([]) operation {a: 1} +[1,2] // NaN, in the same way +[1,2], Number([1,2]) converts to NaN ({}) +[] // "[object object]", using parentheses as object literals instead of block statements, Console. log({} + []) // "[object object]" console.log([] + {}) // "[object object]"Copy the code

5. Other extensions

5.1 Rewrite toString, valueOf, or symbol.toprimitive

let str = { toString () { console.log('toString') return 5 }, valueOf () { console.log('valueOf') return [1, 2, 3]}} console.log(Number(STR)) // valueOf toString 5 ToString let STR = {toString () {console.log('toString') return 5}, valueOf () { console.log('valueOf') return [1, 2, 3] }, [symbol.toprimitive] () {console.log(' Symbol ') return 2}} console.log(Number(STR)) // Symbol 2, Symbol. ToPrimitive priority, and does not call valueOf and toStringCopy the code

5.2 (a == 1&&a == 2&&a == 3) The condition is valid

Customize valueOf, toString, or symbol.toprimitive

let a = { value: 0, valueOf () {return ++this.value}} console.log(a == 1 && a == 2 && a == 3) // true, == Every time toPrimitive rules are invoked, ValueOf can be replaced with toString or symbol.toprimitiveCopy the code

The array implicitly calls join

Let a = [1, 2, 3] a['join'] = function () {return this.shift()} console.log(a == 1 &&a == 2 &&a == 3) // true, == triggers the toPrimitive rule. ToString automatically calls the join function for arrays, so you can override the join functionCopy the code

Data hijacking Object.defineProperty or Proxy

The method of data hijacking can not only judge (a == 1&& A == 2&& a == 3), but also judge (a == 1&& A == 2&& a === 3), which does not trigger implicit conversion and thus does not follow toPrimitive rules. So full equality cannot be triggered using toString

// Object.defineProperty let value = 1 Object.defineProperty(window, "a", {get() {return this.value++}}) console.log(a == 1 &&a == 2 &&a == 3) // true, hijack the property a on the global variable window, // Proxy let test = {count: 1 } test = new Proxy(test, { get(target, Key) {return target[key]++}}) console.log(test.count == 1 && test.count == 2 && test.count == 3) // true, It has no effect on the Window object, so use an object instead for nowCopy the code

5.3 Currization of functions

Corrification is the conversion of a function that takes multiple arguments to a series of functions that take one argument (call the function by passing it only one argument, and let it return a function to handle the rest). The implementation relies on closures and recursion to call a multi-parameter function by splitting arguments

Corrification of a function compared to a normal function

Function add(x, y, Function curryingAdd(x) {function (y) {return function (z) {return x + y + z} }} add(1, 2, 3) // 6 curryingAdd(1)(2)(3) // 6, use a function to receive x, return a function to process y, and return a function to process zCopy the code

The coriolization parameter of the function is fixed in length

Function sum(a, b, c) {return sum(a, b, c) {return a + b + c} args) { if (args.length >= fn.length) { return fn(... Args)} // Return function (... args2) { return curry(fn, ... args, ... // return curry.bind(null, fn,... args) } let add = curry(sum) console.log(add(1)(2)(3)) console.log(add(1, 2)(3)) console.log(add(1, 2, 3))Copy the code

The length of the coriolization parameter of the function is not fixed

function sum(... Args) {return args. Reduce ((a, b) => a + b)} Function Curry (fn) {let args = [] return function temp(... newArgs) { if (newArgs.length) { args = [...args, ...newArgs] return temp } else { let val = fn.apply(this, Return val}}} let add = curry(sum) console.log(add(1)(2)(3)(4)()) //10 The console. The log (add (1) (2) (3, 4, 5) ()) / / 15 console. The log (add (1) (2, 3, 4, 5) (6) (a)) / / / / second, 21 ToString and valueOf are automatically triggered by toPrimitive rules. Function add() { Define an Array specially used to store all of the parameters of the let _args = Array. The prototype. Slice. The call (the arguments) / / in declared within a function, Let _adder = function() {_args.push(... arguments) return _adder } _adder.toString = function () { return _args.reduce((a, B) => a + b)} return _adder} // Note that you need to use unary operators or String functions to trigger implicit conversions, Console. log(+add(1)(2)(3)(4)()) //10 console.log(String(add(1)(2)(3, 4), 5)())) //15 console.log(Number(add(1)(2, 3, 4, 5)(6)())) //21Copy the code

reference

  • Data type conversion from 206 console.log()

  • (2021) (2021) (2021)

  • Do you really understand variables and types