preface

What about data types in JavaScript?

You’ve probably all been asked this question in front end interviews.

A: Data types in Javascript include primitive types and reference types. Primitive types include NULL, undefined, Boolean, string, symbol, bigInt, and number. The reference type refers to Object.

Yeah, that’s what I said, but it’s usually the first question, and it leads to a lot of questions, like

  • NullUndefinedWhat’s the difference? What do you need to pay attention to in the front end?
  • typeof nullWhy is itobject?
  • whyES6To put forwardSymbol?
  • BigIntWhat problem was solved?
  • why0.1 + 0.2! = = 0.3?How do you solve this problem?
  • How do I know if a value is an array?
  • .

Weakly typed language

Because JavaScript is a weakly typed or dynamic language. This means that you don’t need to declare the type of the variable in advance. The type is automatically determined as the program runs, which means that you can use the same variable to hold different types of values

var foo = 42;  // foo is a Number now
foo = "bar";  // foo is a String now
foo = true;   // foo is a Boolean now
Copy the code

While this feature brings us convenience, it also brings us a lot of type errors. Just think about it, if JS is a strong type of language, then there is no conversion between various types, there is a layer of estrangement or a layer of protection, will it be better to maintain it? This is probably why TypeScript was born.

To master JavaScript data types is the most basic knowledge of a front-end

Null or undefinded

define

Undefined denotes an undefined variable. A null value represents an empty object pointer.

Back to basics: In the beginning, JavaScript designer Brendan Eich just defined NULL, which is treated as an object, just like in Java. But because there are two types of data in JavaScript: raw data types and reference data types. Brendan Eich decided that the value for “nothing” had better not be an object.

So the design of Javascript is that NULL is an object that means “nothing” and is zero when converted to a value; Undefined is a primitive value for “none”, which is NaN when converted to a value.

Number(null)
/ / 0

5 + null
/ / 5

Number(undefined)
// NaN

5 + undefined
// NaN
Copy the code

The difference and application of Null and Undefined

Null means “no object”, meaning there should be no value. Typical usage is as follows

  1. As arguments to a function, indicating that the function’s arguments are not objects.
  2. As the end of the object prototype chain.
Object.getPrototypeOf(Object.prototype)
// null
Copy the code

Undefined means “missing value”, that is, there should be a value here, but it is not defined yet. Typical usage:

  1. When a variable is declared, but not assigned, it equalsundefined.
  2. When the function was called, the argument that should have been provided was not providedundefined.
  3. The object has no assigned attribute, which has a value ofundefined.
  4. The function returns no value by defaultundefined.
var i;
i // undefined

function f(x){console.log(x)}
f() // undefined

var  o = new Object(a); o.p// undefined

var x = f();
x // undefined
Copy the code

What should we pay attention to in short?

JavaScript: undefined, null, false, “”, 0, NAN

This can sometimes lead to problems such as

let a = 0;
console.log(a || '/'); // If a is null or Undefined, it will print '/'. If a is null or Undefined, it will print '/'.
Copy the code

And of course we could write that

let a = 0;
if (a === null || a === undefined) {
  console.log('/');
} else {
  console.log(a);
}
Copy the code

Still not very elegant, so the ES specification proposes the null value merge operator (??).

Null-value merge operator (??) Is a logical operator that returns the right-hand operand if the left-hand operand is null or undefined, otherwise returns the left-hand operand.

The above example can be written as:

let a = 0;
console.log(a??'/'); / / 0
Copy the code

Typeof NULL — error made by JS

typeof null // "object"
Copy the code

Values in JavaScript are represented by a label representing the type and the actual data value. The first version of JavaScript stored values in 32-bit bits and identified the type by the lower one or three bits of the value, with the object’s type tag 000. The following

  • 1: Integer (int)
  • 000: Reference type (Object)
  • 010: Floating point double
  • 100: String (string)
  • 110: Boolean (Boolean)

But there are two special values:

  • Undefined: integer −2^30 (minus 2^30, outside the range of integers)
  • Null, machine code null pointer (defined by C/C++ macros), the lower three digits are also 000

Since null represents a null pointer (the lower three digits are also 000), the type label for NULL is 000, and typeof NULL therefore returns “object”.

This is a JavaScript design error, but it can’t be fixed because it would affect the existing code

Number – 0.1 + 0.2! = = 0.3

The phenomenon of

Something like the following occurs in JavaScript

0.1 + 0.2 
0.30000000000000004
Copy the code

why

We need to convert decimal to binary as we work with floating point numbers. Decimal The rules for converting a decimal to binary are as follows:

Multiply the number after the decimal point by 2 and take the integer part of the result (either 1 or 0), then multiply the decimal part by 2 again and take the integer part of the result… And so on, until the decimal part is zero or the number of digits is enough. Then put the integer parts in order

According to the above rules, the final 0.1 is expressed as follows:

0.000110011001100110011(0011Infinite loop)...Copy the code

So the loss of precision isn’t a problem with the language, it’s an inherent flaw in floating-point storage.

JavaScript stores all values of the Number type in 64-bit double precision floating-point numbers. According to IEEE754, the binary Number of 0.1 retains only 52 significant digits, i.e

1.100110011001100110011001100110011001100110011001101 * 2^ (-4)
Copy the code

Similarly, the binary number of 0.2 is

1.100110011001100110011001100110011001100110011001101 * 2^ (-3)
Copy the code

Thus accuracy is lost in conversion between bases. Here’s how it works

0.00011001100110011001100110011001100110011001100110011010
+0.00110011001100110011001100110011001100110011001100110100-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- =0.01001100110011001100110011001100110011001100110011001110
Copy the code

Therefore, the final calculation result is 0.1 + 0.2! = = 0.3

How to solve

  • Convert numbers into whole numbers
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;
}
Copy the code
  • The class library

There are many math libraries on NPM that support JavaScript and Node.js, such as math.js, Decimal. js, d.js, and so on

  • ES6

ES6 adds a tiny constant to the Number object, number.epsilon

Number.EPSILON
/ / 2.220446049250313 e-16
Number.EPSILON.toFixed(20)
/ / "0.00000000000000022204"
Copy the code

The purpose of introducing such a small quantity is to set a margin of error for floating-point calculations, and if the error can be less than Number.EPSILON, the result can be considered reliable.

function withinErrorMargin (left, right) {
    return Math.abs(left - right) < Number.EPSILON
}
withinErrorMargin(0.1+0.2.0.3)
Copy the code

Future solutions –TC39 Decimal proposal

The proposal is currently in Stage 1. BigInt extends JS’s positive bounds beyond 2^53 safe integer problems. Decimal solves the JS Decimal problem -2^53. This proposal introduces a new native type in JS: decimal(suffix M), which states that the number is a decimal operation.

let zero_point_three = 0.1m + 0.2m;
assert(zero_point_three === 0.3m);
// Examples of proposals
function calculateBill(items, tax) {
  let total = 0m;
  for (let {price, count} of items) {
    total += price * BigDecimal(count);
  }
  return BigDecimal.round(total * (1m + tax), {maximumFractionDigits: 2.round: "up"});
}

let items = [{price: 1.25m, count: 5}, {price: 5m, count: 1}];
let tax = .0735m;
console.log(calculateBill(items, tax));
Copy the code

Extension – Storage of floating-point numbers in memory

So what does the final floating-point number look like in memory? EEE754 provides a definition for the representation of floating point numbers

(-1)^S * M * 2^E

The meaning of each symbol is as follows: S, the sign bit, determines the positive and negative, 0 is positive, 1 is negative. M is the significant number, greater than 1 and less than 2. E is the exponent.

Javascript is a 64-bit double-precision floating-point number, with the highest 1 bit being the sign bit S, the next 11 bits being the exponent E, and the remaining 52 bits being the significant digit M.

Use this visualization tool to view the binary representation of floating point numbers in memory.

BigInt — Breaking the biggest limits

JavaScript’s Number type is a double precision IEEE 754 64-bit floating-point type. The maximum value in JavaScript is 2^53.

BigInt, an arbitrary precision numeric type, has entered the stage3 specification. BigInt can represent an arbitrarily large integer. To create a BigInt, we simply append the n suffix to any integer literal. For example, let’s write 123 as 123n. The global BigInt(number) can be used to convert a number to a BigInt, meaning that BigInt(123) === 123n. Now let me use these two points to solve the problem we mentioned earlier:

I’m the only one and the most beautiful

define

ES6 introduces a new primitive data type, Symbol, that represents unique values

let s = Symbol(a);typeof s
// "symbol"
Copy the code

Application scenarios

  • Define a set of constants that are not equal. Erase magic string

  • Object is guaranteed to have different property names

let mySymbol = Symbol(a);// The first way
let a = {};
a[mySymbol] = 'Hello! ';

// The second way
let a = {
  [mySymbol]: 'Hello! '
};

// The third way
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello! ' });

// All the above methods give the same result
a[mySymbol] // "Hello!"
Copy the code
  • VueIn theprovideinject.provideinjectYou can allow an ancestor component to inject a dependency into all of its descendants, regardless of how deep the component hierarchy is, for as long as the upstream and downstream relationship is established. But this is also very invasive to useSymbolsAs akeyCan avoid to reduce the interference to the component code, do not have the same naming problems

Array – a special existence of an object

Can you tell us how to determine Array?

Why do you ask this question?

Because the array is a special existence, is one of the data structures we usually contact the most, it is a special object, its index is the “ordinary object” key value. But it has some methods that “normal objects” don’t have, such as maps

Typeof is a javascript natively provided operator to determine the data type, which returns a string representing the data typeof the parameter. But we can’t use typeof to determine if it’s an array. Because both typeof arrays and ordinary objects and null return “object”

const a = null;
const b = {};
const c= [];
console.log(typeof(a)); //Object
console.log(typeof(b)); //Object
console.log(typeof(c)); //Object
Copy the code

A method to determine an array

  • Object.prototype.toString.call().

Every Object that inherits Object has a toString method, which, if not overridden, returns [Object type], where type is the type of the Object

const a = ['Hello'.'Howard'];
const b = {0:'Hello'.1:'Howard'};
const c = 'Hello Howard';
Object.prototype.toString.call(a);//"[object Array]"
Object.prototype.toString.call(b);//"[object Object]"
Object.prototype.toString.call(c);//"[object String]"
Copy the code
  • Array.isArray()
const a = [];
const b = {};
Array.isArray(a);//true
Array.isArray(b);//false
Copy the code

Array. IsArray () is the ES5 new method, when there is no Array. The isArray (), you can use the Object. The prototype. ToString. Call () implementation

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}
Copy the code
  • instanceof.instanceofThe operator can be used to determine the value of a constructorprototypeWhether the object to which the attribute refers exists on another prototype chain for the object to be detected. Because the constructor of an array isArray, so it can be judged as follows.Note that arrays are also objects, soa instanceof ObjectAlso for thetrue
const a = [];
const b = {};
console.log(a instanceof Array);//true
console.log(a instanceof Object);//true, the Object constructor can also be found on the array's prototype chain
console.log(b instanceof Array);//false
Copy the code
  • constructor. The instance instantiated by the constructor has oneconstructorProperties.
function B() {};
let b = new B();
console.log(b.constructor === B) // true
Copy the code

Arrays are instantiated by a function called Array. So you can

let c = [];
console.log(c.constructor === Array) // true
Copy the code

Note: Constructor is subject to change. So it’s not recommended

let c = [];
c.constructor = Object;
console.log(c.constructor === Array); // false
Copy the code

conclusion

According to the above description, personally recommended judgment methods have the following priorities

isArray > Object.prototype.toString.call() > instanceof > constructor

conclusion

This paper discusses and analyzes some common data type problems in JavaScript. I hope I can help you in your interview or in your daily work. Some of the other things that I might not have mentioned like type conversions and so on, we’ll have a chance to talk about that

Finally, welcome to pay attention to my public account – front-end grocery store, technical issues more discussion ~

reference

  • Undefined and null
  • The history of “typeof null”
  • 0.1 + 0.2 does not equal 0.3? Why does JavaScript have this “slutty” operation?
  • An in-depth understanding of precision loss in JavaScript
  • 0.1 + 0.2! == 0.3, will soon be a thing of the past
  • BigInt: an arbitrary precision integer in JavaScript
  • ECMAScript introduction to 6
  • How do you know an array is an array in JavaScript?