Of these powerful operators in JS, there are always a few you have never heard of

The operators in JS are used every day, and there are some new practical operators added by ES2020 and ES2021, which together constitute the flexible syntax ecology of JS. In addition to introducing commonly used operators, this article will also introduce some infrequently used but very powerful operators in JS. Let's take a look at it together~

1. Numerical separator _

ES2021 introduces the value separator _ to provide separation between value groups, making it easier to read a long value. Chrome has provided support for numeric separators, you can try it in the browser.

let number = 100_0000_0000_0000 // 0太多了不用数值分割符眼睛看花了
console.log(number)             // 输出 100000000000000

In addition, numeric separators can also be used in the fractional part of decimal, and numeric separators can also be used in binary and hexadecimal.

0x11_1 === 0x111   // true 十六进制
0.11_1 === 0.111   // true 十进制的小数
0b11_1 === 0b111   // true 二进制

2. The comma operator,

What, can a comma also be an operator? Yes, I have seen such a simple function that swaps the first and second items of an array and returns the sum of the two items:

function reverse(arr) {
    
    
    return [arr[0], arr[1]]=[arr[1], arr[0]], arr[0] + arr[1]
}
const list = [1, 2]
reverse(list)   // 返回 3,此时 list 为[2, 1]

The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.

expr1, expr2, expr3...

Will return the result of the last expression expr3, other expressions will only be evaluated.

3. Zero coalescing operator??

The zero coalescing operator?? is a logical operator. When the left operand is null or undefined, it returns the right operand, otherwise it returns the left operand.

expr1 ?? expr2

Null merge operators are generally used to provide default values ​​for constants to ensure that the constants are not null or undefined. Previously, || was generally used to do this variable = variable ||'bar'. However, since || is a Boolean logic operator, the operand on the left will be coerced into a Boolean value for evaluation. Any false value (0,'', NaN, null, undefined) will not be returned. This leads to unpredictable consequences if you use 0,'', NaN as valid values.

It is precisely because || has such problems, and the emergence of ?? is to solve these problems. ?? will only return the latter when the left side is undefined or null. ?? can be understood as a perfect solution for ||.

You can execute the following code in the browser to get a feel:

undefined || 'default' // 'default'
null || 'default'      // 'default'
false || 'default'     // 'default'
0 || 'default'         // 'default'

undefined ?? 'default' // 'default'
null ?? 'default'      // 'default'
false ?? 'default'     // 'false'
0 ?? 'default'         // 0

In addition, when assigning, you can use the abbreviation of the assignment operator??=

let a = {
    
    b: null, c: 10}
a.b ??= 20
a.c ??= 20
console.log(a)     // 输出 { b: 20, c: 10 }

4. Optional chain operator?.

Optional chain operator?. Allows to read the value of an attribute located deep in the chain of connected objects without having to verify whether each reference in the chain is valid. The function of the ?. operator is similar to that of the chain operator. The difference is that it does not cause an error when the reference is null or undefined. The short-circuit return value of the expression is undefined.
When trying to access object properties that may not exist, the optional chain operator will make the expression shorter and more concise.

const obj = {
    
    
  a: 'foo',
  b: {
    
    
    c: 'bar'
  }
}

console.log(obj.b?.c)      // 输出 bar
console.log(obj.d?.c)      // 输出 undefined
console.log(obj.func?.())  // 不报错,输出 undefined

It used to be possible to obtain a deeply nested sub-attribute through obj && obj.a && obj.ab, now you can directly obj?.a?.b.
The optional chain can not only be used to obtain the properties of the object, but also the index arr?.[index] of the array, and it can also be used in the judgment of the function func?.(args), when trying to call a method that may not exist An optional chain can also be used.
When calling a method that may not exist on an object (for version reasons or the current user's device does not support the function), using the optional chain can make the expression return undefined when the function does not exist instead of throwing an exception directly.

const result = someInterface.customFunc?.()

5. Private methods/attributes

In a class, you can add # private mark in front of the property to mark it as private. In addition to the property can be marked as private, getter/setter can also be marked as private, and methods can also be marked as private.

class Person {
    
    
  getDesc(){
    
     
    return this.#name +' '+ this.#getAge()
  }
  
  #getAge(){
    
     return this.#age } // 私有方法

  get #name(){
    
     return 'foo' } // 私有访问器
  #age = 23                   // 私有属性
}
const a = new Person()
console.log(a.age)       // undefined 直接访问不到
console.log(a.getDesc()) // foo 23

6. Bitwise operators >> and >>>

The signed right shift operator >> moves the first operand to the right by the specified number of bits, and the excess displacement to the right is discarded. The high bit is filled with its sign bit, positive numbers are filled with 0, and negative numbers are filled with 1. Because the new leftmost bit has the same value as the previous leftmost bit, the sign bit (leftmost bit) will not change.

(0b111>>1).toString(2)   // "11"
(-0b111>>1).toString(2)  // "-100" 感觉跟直觉不一样

A good understanding of positive numbers. How to understand negative numbers. Negative numbers are stored in the computer according to the complement. The calculation method of the complement is to invert and add one. When shifting, the complement form is shifted to the right, and the leftmost complement is the sign bit. , After shifting, take the inverse again and add one's complement to get the processed original code.

-111      // 真值
1 0000111 // 原码(高位的0无所谓,后面加不到)
1 1111001 // 补码
1 1111100 // 算数右移
1 0000100 // 移位后求补码获得原码
-100      // 移位后的真值

Generally, we use >> to divide a number by 2, which is equivalent to discarding the decimal places and then performing a Math.floor:

10 >> 1    // 5
13 >> 1    // 6 相当于
13.9 >> 1  // 6
-13 >> 1   // -7 相当于
-13.9 >> 1 // -7 

The unsigned right shift operator >>> moves the sign bit as part of the binary data to the right. The high bit is always filled with 0. There is no difference between a positive integer and arithmetic right shift. For a negative number, the sign bit is filled with 0 and becomes positive. There is no need to find one's complement after counting, so the result is always non-negative. Even if shifted by 0 bits to the right, the result is non-negative.

(0b111>>>1).toString(2)   // "11"
(-0b111>>>1).toString(2)  // "1111111111111111111111111111100"

Can be understood like this

-111      // 真值
1 000000000000000000000000000111 // 原码
1 111111111111111111111111111001 // 补码
0 111111111111111111111111111100 // 算数右移(由于右移后成为正数,就不要再求补码了)
1073741820      // 移位后的真值

The left shift operator << is similar to it, the left shift is very simple to remove the highest bit on the left, and add 0 to the low bit:

(0b1111111111111111111111111111100<<1).toString(2)   // "-1000"
(0b1111111111111111111111111111100<<<1).toString(2)  // "-1000"

PS: There is no unsigned left shift in JS, and other languages ​​such as JAVA also do not have unsigned left shift.

7. Bitwise operator & and |

Bitwise operators are bitwise operations, & and, | or, ~ not, ^ are bitwise exclusive or:

&: 1010  |: 1010  ~: 1010  ^: 1010
   0110     0110              0110
   ----     ----     ----     ----
   0010     1110     0101     1100

When using bit operators, decimal places are discarded. We can use this feature to round numbers, such as putting any number & on 32 binary ones, or | on 0. Obviously the latter is simpler.
So we can round a number | 0, and the same applies to negative numbers

1.3 | 0         // 1
-1.9 | 0        // -1

In addition to the common remainder% 2 for judging odd and even numbers, & 1 can also be used to judge whether the lowest bit of the binary number is 1, so that all but the lowest bit are set to 0, and the result of the remainder is only the lowest bit. Isn't it clever? The same applies to negative numbers:

const num = 3
!!(num & 1)                    // true
!!(num % 2)                    // true

8. Double bit operator~~

You can use two-bit operators to replace Math.floor() for positive numbers and Math.ceil() for negative numbers. The advantage of the double-negation positioning operator is that it performs the same operation faster.

Math.floor(4.9) === 4      // true
// 简写为:
~~4.9 === 4      // true

Note, however, that for positive numbers, the result of the operation is the same as that of Math.floor( ), and for negative numbers, the result of the operation is the same as that of Math.ceil( ):

~~4.5                // 4
Math.floor(4.5)      // 4
Math.ceil(4.5)       // 5
 
~~-4.5               // -4
Math.floor(-4.5)     // -5
Math.ceil(-4.5)      // -4

PS: Note the difference between ~~(num/2) and num >> 1 when the value is negative

9. Short-circuit operator && and ||

We know that logical AND && and logical OR || are short-circuit operators. The short-circuit operator is a left-to-right operation in which the former meets the requirements, and the latter is no longer executed.
It can be understood as:

&& is a false operation, judged in turn from left to right, if a false value is encountered, it will return a false value, and will not be executed later, otherwise it will return the last true value
|| is a true operation, judged in turn from left to right, If a true value is encountered, the true value will be returned, and no further execution will be performed, otherwise the last false value will be returned

let param1 = expr1 && expr2
let param2 = expr1 || expr2

Insert picture description here
Therefore, it can be used to do many interesting things, such as assigning initial values ​​to variables:

let variable1
let variable2 = variable1  || 'foo'

If variable1 is true, it will return directly, and the short circuit will not be returned. If it is false, the following foo will be returned.

It can also be used to make simple judgments instead of lengthy if statements:

let variable = param && param.prop
// 有了可选链之后可以直接 param?.prop

If param is true, return the param.prop property, otherwise return the false value of param, so that in some places, it can prevent param from being undefined and taking its properties to cause an error.

10. void operator

The void operator evaluates the given expression, and then returns undefined. It
can be used to use the immediate call function expression (IIFE). You can use the void operator to let the JS engine recognize a function keyword as a function expression Style rather than function declaration.

function iife() {
    
     console.log('foo') }()       // 报错,因为JS引擎把IIFE识别为了函数声明
void function iife() {
    
     console.log('foo') }()  // 正常调用
~function iife() {
    
     console.log('foo') }()      // 也可以使用一个位操作符
(function iife() {
    
     console.log('foo') })()     // 或者干脆用括号括起来表示为整体的表达式

It can also be used in arrow functions to avoid value leakage. Arrow functions allow return values ​​directly without using parentheses in the function body. This feature brings a lot of convenience to users, but sometimes it also brings unnecessary trouble. If a function that originally has no return value is called on the right side, its return value will change, which will cause unexpected side effects.

const func = () => void customMethod()   // 特别是给一个事件或者回调函数传一个函数时

To be safe, when you don't want the function return value to be anything other than null, you should use void to ensure that undefined is returned, so that when the return value of customMethod changes, it will not affect the behavior of the arrow function.

11. Other commonly used operators

  1. Ternary expression: It's very simple, you often use it, expr? Expr1: expr2 If expr is true, return expr1, otherwise return expr2
  2. Shorthand for assignment operator: addition assignment +=, subtraction assignment -=, multiplication assignment *=, division assignment /=, exponentiation assignment **=, bitwise or copy |=, bitwise and assignment &=, signed bitwise Right shift assignment >>=, unsigned bitwise right shift assignment >>>=, logical null assignment??=…
  3. Exponentiation operator: var1 ** var2 is equivalent to Math.pow, the result is var1 to the var2 power

12. Operator precedence

Because of the operator precedence, the meaning of variable = 1, 2 is to first assign the variable to 1, and then return the number 2, instead of assigning the variable to the return value of 1, 2. This is because of the precedence of the = operator The level is higher than the comma operator. Another example is the expression 6-2 * 3 === 0 && 1,-* === && The four operators with the highest precedence * are operated first, and then-the result of the operator is 0, === operator precedence Higher than && and true && 1 results in 1, so this is the result of the operation.
The following table arranges operators from high (20) to low (1) according to their precedence, but this is not the latest one, at least not including the optional chain. It is recommended to refer to this table or MDN .

Insert picture description here

Reprinted from: https://juejin.cn/post/6918902650964557838

Guess you like

Origin blog.csdn.net/weixin_43881166/article/details/114868994