Do you know the strange symbols of JS and TS?

start

With the advent of ES6 + and Typescript, the world of Javascript has been an era of symbols flying all over the sky. I feel that I can't keep up with it without studying hard. Reading the code written by others is like reading a book from heaven.

I saw a function in a recent project, Em... Call directly, good guy!!!

In order to move bricks more efficiently, the purpose of this chapter is to explore the magical symbols and writing methods that may make you unable to understand. Of course, if you encounter more amazing operations, you are welcome to leave a message in the comment area and grab a seat (you can also grab ^ 0 ^).

(all for better fishing, come on!!!)

JS

Deconstruction assignment (writing method)

The deconstruction and assignment of variables is a new feature brought by ES6. This thing is a good thing. It's very cool to use. In short, it's Nice!!! I believe that practice makes perfect. I won't explain its basic usage here. Let's mainly take a look at the following interesting writing methods to see if we can give you some new experience and learn some new skills.

console method

Debugging code is a key link. Although there are a variety of debugging methods, the console method is the simplest and most practical. However, I wonder if you will feel that writing console.log() is cumbersome for a moment? Then you can try it a little bit like this:

({log: window.log, error: window.error, warn: window.warn} = console);
log('ordinary');
warn('remind');
error('DANGER');

It's good to taste and eat by yourself ⊙ ω ⊙ escape~

Arrays are special objects

How free is it to use objects to deconstruct arrays? (T_T)

var arr = ['L', 'O', 'V', 'E'];
var {0: l, 1: o, 2: v, 3: e} = arr;
console.log(l, o, v, e); // L O V E

Take the first and last items of the array

var arr = ['first', 'second', 'last'];
var {[0]: first, [arr.length - 1]: last} = arr;
console.log(first, last); // first last

What's this called? This is called taking an unusual road!!! Wait, we can find a little knowledge here, such as:

var arr = ['first', 'second', 'last'];
var {[arr.length <= 1 ? 0 : arr.length - 1]: last} = arr;
console.log(last); // last

Em... There are many things that can be done by using expressions? hey!!!

Strings are also deconstructed

var [a1, a2, a3, a4] = 'YYDS';
console.log(a1, a2, a3, a4); // Y Y D S
var [lastname, ...name] = 'Orange sb';
console.log(lastname, name); // Orange, man, front, end

Although there may be few usage scenarios, it may be useful one day.

Variable value exchange

var x = 1;
var y = 2;
[x, y] = [y, x];

There's nothing to say. You don't have to define temporary variables anyway.

Default value of default value

This is what I really saw in the project. It's roughly simplified like this. You watchmen will taste it carefully.

var upperValue = ''; // Value of an interface
let {value: newValue = upperValue || 'The previous interface has no value'} = {value: undefined}; // The value that another interface might return
console.log(newValue)

Shakespeare said: there are a thousand Hamlets in a thousand and people's eyes

I think the same is true for writing code... (⊙o⊙)

Label template of template string (fn ` `)

console.log`Orange sb`; // ['orange someone']
// Equivalent to
console.log(['Orange sb']); // ['orange someone']

This is actually a special call form of a function. Although you may not use it in your life, you can't rule out how others will write it. Know yourself and know the other. For more usage, click file.

Numeric separator ()

The values allowed in ES6 use underscores () as separators. With this, you don't have to worry about counting zeros and circles.

let num1 = 137_9083_7051; // phone number
console.log(num1); // 13790837050
let num2 = 1_000_000_000; // Large number
console.log(num2); // 1000000000
let num3 = 1.000_000_000_1; // Multiple decimal places
console.log(num3); // 1.0000000001

rest parameters (...)

rest parameter (in the form of... Variable name) is used to receive redundant parameters of the function. This parameter stores redundant parameters in the form of array.

function fn(val, ...vals) {
  console.log(val, vals);
}
fn(1, 2, 3, 4, 5); // 1 [2, 3, 4, 5]

It better replaces the arguments parameter. The opacity and concealment of the arguments parameter and its existence in the form of pseudo array can not directly use array related methods have brought more trouble to people; Of course, more importantly, it can serve the arrow function. We know that there is no so-called this inside the arrow function, and there will be no arguments parameter.

It can not only be used on function parameters, but also play with deconstruction assignment:

var [a, ...rest] = [1, 2, 3, 4];
console.log(a); // 1
console.log(rest); // [2, 3, 4]

Precautions for using the rest parameter:

  • The rest parameter can only be placed in the last bit of all parameters, otherwise an error will be reported.
  • The rest parameter is not included in the length attribute of the function.

    (function(a) {}).length  // 1
    (function(...a) {}).length  // 0
    (function(a, ...b) {}).length  // 1

Extended operator (...)

The extension operator is also a fun idea, but note that it is different from the rest parameter. Don't confuse it.

console.log(...[1, 2, 3]); // 1 2 3
console.log({...{a: 1, b: 2, c: 3}}); // {a: 1, b: 2, c: 3}
console.log([...document.querySelectorAll('div')]); // [div, div, div]
console.log(...new Set([1, 2, 3])); // 1 2 3
console.log(...new Map([['name', 'Orange sb'], ['age', 18]])); // ["name", "orange someone"] ["age", 18]

The extension operator is easy to understand and use, and the readability is also very good. However, even so, it can't support the strange operations of the great gods, and it's easy to write a head wrenching writing method.

console.log({...['orange', 'some', 'people']}); // {0: "orange", 1: "a", 2: "person"}
console.log({...'Orange sb'}); // {0: "orange", 1: "a", 2: "person"}
function fn(...[a, b, c]) {
  console.log(a, b, c);
}
fn(1, 2, 3); // 1, 2, 3
fn(1, 2, 3, 4); // 1, 2 and 3 here are only extension operators, not rest parameters

Exponential operator (* *)

The exponential operator (* *) is the same as Math.pow(), but it's simpler to write.

console.log(2 ** 2); // 2*2=4
console.log(2 ** 3); // 2*2*2=8
 Equivalent to
console.log(Math.pow(2, 2)); // 4
console.log(Math.pow(2, 3)); // 3

But a little note, this guy starts from the right:

console.log(2 ** 3 ** 2); // 2 ** (3*3) = 2 ** 9 = 2*2... = 512

Chain judgment operator (.?)

I wonder if you have ever written such a statement:

var response = {}
console.log(response && response.data && response.data.name); // undefined

In order not to report an error on the console when reading an attribute inside the object, we often need to judge whether the upper object of the attribute exists. There is no problem in writing, but once the number of read attributes is large, it will be cumbersome to write. This time it came, ES2020 With the chain judgment operator coming to us.

console.log(response?.data?.name); // undefined

Isn't it very concise? It's great. Is there (- ^ 0 ^ -)?

The principle of chain judgment operator is to judge whether the object on the left is null or undefined. If yes, it will no longer operate down, but return undefined.

Null judgment operator (?)

Sometimes we need to judge whether a variable is empty. If it is empty, set the default value, otherwise it is the original value. What would we probably write:

// var value = '';
// var value = 0;
// var value = undefined;
// var value = null;

console.log(value ? value : 'value Null value'); // '' and 0 are also null
console.log(value !== undefined || value !== null ? value : 'value Null value'); // The writing method is cumbersome

ES2020   A new null judgment operator is introduced, Only when the value on the left of the operator is null or undefined, the value on the right will be returned; otherwise, the value on the left will be returned.

console.log(value ?? 'value No value');

globalThis

JavaScript can run in different environments, such as browser, Worker, Node, etc. Although they are JS and the syntax is basically the same, they have different global objects.

  • Browser global objects are   window.
  • The Web Worker global object is self.
  • The Node global object is   global.

In this case, in order to use unified global objects in different environments, the ES2020 standard introduces globalThis.

// browser
console.log(globalThis);    // => Window {...}

// node
console.log(globalThis);    // => Object [global] {...}

// web worker
console.log(globalThis);    // => DedicatedWorkerGlobalScope {...}

TS

After learning about various symbols in JS, we will come to some symbols in TS. however, there are not many strange symbols in TS, and most of them are the same as or evolved from JS. Let's continue to observe them.

(in order to show the effect of error reporting, the following codes will basically use screenshots instead of codes. They are very simple codes to illustrate the function and significance of each symbol)

Non null assertion operator (!)

First, let's introduce the first symbol non empty assertion operator, but to show the role of this symbol, we still need to make some modifications to tsconfig.json. This file is the TS configuration file. You can see it in the root directory of general TS projects. You can also actively generate it through the command of npx tsc --init.

Then we need to open the "strictNullChecks": true configuration item of the file. Why? This is mainly because null and undefined are the basic types in TS and have the values null and undefined respectively. By default, they are subtypes of all types, that is, they can be assigned to any type; When we open this option, they cannot be assigned to other types at will, but only to their own types.

Before the configuration item is opened:

After the configuration item is opened:

As shown in the figure above, nickname may be undefined, so it cannot be directly assigned to realname, otherwise an error will be reported. However, if the nickname is determined to have a value after some operations, for example:

How do we tell TS? Let it not report an error? At this time, you can use the non null assertion operator.

This is OK. It should be relatively simple and easy to understand...

Optional properties (?:)

Optional attribute can be said to be a property of interface. Interface is a very flexible concept. We can usually use it to limit an object, for example:

A human interface is defined in the figure, including name, age and hobby. It can be used to restrict objects, but sometimes, some objects have no hobby (well, it's too bad if a person has no hobby)), just like Xiao Ming in the figure, they can only report errors. What should we do?

At this time, you can use the optional attributes of the interface, such as:

Chain judgment operator (.?)

This operator has the same effect as the optional chain operator in JS version. In fact, it is only the implementation of TS version. It also determines whether the object on the left is null or undefined. If yes, it will no longer operate down, but return undefined

var obj: any = undefined;
if(obj?.name) {}
// Equivalent to
if(obj && obj.name) {}

The purpose of this writing is to prevent the console from reporting errors, because the obj type may be uncertain.

Null judgment operator (?)

The effect of this operator is the same as that of the null value judgment operator in JS version, and it is only the implementation of TS version. Similarly, only when the value on the left of the operator is null or undefined, the value on the right will be returned. Otherwise, the value on the left will be returned.

var value: string | undefined | null = '';
console.log(value ?? 'value Null value'); // When value is null, the default value is taken

Cross type operator (&)

This is like an operator and (& &), but it works on the type definition, and it has only a single symbol (&). Using the cross type operator, multiple types can be superimposed to form a new type. The new type will contain all the required types of properties, and none of them is indispensable.

It can also be used on interfaces:

There is nothing to say about this. Of course, there are also some points to pay attention to in its use, such as this:

When the two intersecting types have the same attributes, but the attribute type definitions are different, the resulting type will become the never type. This is because the type string & number obviously does not exist, so it will become a never type.

Union type separator (|)

This is also very simple. It has the same function as or (|). It is the same as before. It has only a single symbol (|), but don't write it wrong.

These examples are very simple, so I won't say more. However, many times, when using the union type separator, you will encounter some problems, such as:

In the figure, whether we read name or age is not allowed in TS, and TS cannot determine whether these two attributes exist on obj; There are many ways to solve such problems. We collectively call these methods "type protection". In fact, it is easy to understand that we first determine the type of obj object in the diagram, and then carry out subsequent operations.

(by the way, "type protection" is mentioned here. For more information, you can check the relevant materials by yourself. We'll stop here.)

Type Asserts

Type assertion is a bit like type conversion, but it's not. After all, it didn't convert, but "deceived" the type check of TS. It can be written in two ways. Let's take a look at it respectively.

TS does not allow us to assign an unknown type or other types to a variable of an explicit type, but sometimes we have to do that. As in the figure, what should we do?

as syntax

< > syntax

Is it easy to understand?

Decorator (@ xxx)

What is a decorator? Remember, the essence is to call a function, which is a syntax sugar. It's no big deal. Its function is to allow adding new functions to an existing object without changing its structure.

To use it, you still have to open its related configuration items in tsconfig.json, and open "experimentalDecorators": true and "emitDecoratorMetadata": true.

Without much to say, let's write a small example to observe:

function classFn(target: any) {
  console.log('Class decorator:', target);
}

@classFn
class Person{
  constructor() {
    console.log('instantiation ');
  }
}

var p = new Person(); 

This is the result of executing the rear console print:

Er... Isn't it also very interesting? Little friends who have studied Java may be familiar with it, which is similar to the decorator mode of Java... (⊙o⊙)

Decorators can also pass parameters and are executed sequentially:

function classFn(target: any) {
  console.log('Class decorator:', target);
  return function(params: any) {
    console.log('Custom class decorator parameters:', params)
  }
}

@classFn('Orange sb-1')
@classFn('Xiao Ming-2')
class Person{
  constructor() {
    console.log('instantiation ');
  }
}

var p = new Person();

Decorators are not only class decorators, but also classified as follows:

  • Class decorators
  • Property decorators
  • Method decorators
  • Parameter decorators

Em... There seems to be a lot of content involved in this thing. I won't introduce it one by one here. I don't want to annoy you with too much content. The purpose of this chapter is to take you through various symbols so as not to be completely blind when taking over the project. It's almost enough to know the grammar. (hahaha, finally the water is over... Escape, everyone is happy)





So far, this article has been written, sprinkle flowers.

I hope this article is helpful to you. If you have any questions, look forward to your message.
As usual, like + comment = you will, collection = you are proficient.
The original was first published in Nuggets , welcome to step on it.

Keywords: Javascript Front-end TypeScript

Added by Daveyz83 on Mon, 01 Nov 2021 10:50:54 +0200