The purpose of this paper is to help you more skillfully use JavaScript language for development.
if statement with multiple conditions
Put multiple values in an array, and then call the array. includes method.
// bad 👴 if (x === "abc" || x === "def" || x === "ghi" || x === "jkl") { //logic } // better 👶 if (["abc", "def", "ghi", "jkl"].includes(x)) { //logic }
Simplify using conditional expressions if true...else
// bad 👴 let test: boolean; if (x > 100) { test = true; } else { test = false; } // better 👶 let test = x > 10 ? true : false; //Or so let test = x > 10; console.log(test);
False value (undefined, null, 0, false, NaN, empty string) check
When we create a new variable, we sometimes want to check whether the referenced variable is null or undefined or empty string Equal false value. JavaScript does have a good shortcut to implement this check - logic or operator (|)
||Returns the right operand when the left operand is false
Only if the left side is:
-
Empty string: '' Or``
-
NaN
-
0
-
null
-
undefined
-
false
Logical or operator (|) The value with the right side will be returned
// bad 👴 if (test1 !== null || test1 !== undefined || test1 !== "") { let test2 = test1; } // better 👶 let test2 = test1 || "";
// bad 👴 if (test1 === true) or if (test1 !== "") or if (test1 !== null) // better 👶 if (test1){ // do some }else{ // do other }
Note: if test1 If there is a value, it will be executed if After the logic, this operator is mainly used for Null, undefined, empty string Check.
Use the null merge operator -??
Only if the left side is
-
null
-
undefined
Null merge operator (?) The value on the right is returned
const baz = 0 ?? 42; console.log(baz); // expected output: 0
Note: unlike the logical or operator (|), |) returns the right operand when the left operand is false
Only if the left side is:
-
Empty string: '' Or``
-
NaN
-
0
-
null
-
undefined
Logical or operator (|) The value with the right side will be returned
var a = "" || 1; // output one console.log(a);
null Check and default assignment
let test1 = null; let test2 = test1 ?? ""; console.log("null check", test2); // Output empty string ""
undefined Check and default assignment
const test = undefined ?? "default"; console.log(test); // expected output: "default"
Return after comparison
// bad 👴 let test; function checkReturn() { if (!(test === undefined)) { return test; } else { return callMe("test"); } } // better 👶 function checkReturn() { return test ?? callMe("test"); }
Use the optional chain operator -
?. Also called chain judgment operator. It allows developers to read property values deeply nested in the object chain without having to validate each reference. When the reference is empty, the expression stops evaluating and returns undefined
const travelPlans = { destination: "DC", monday: { location: "National Mall", budget: 200, }, }; // bad 👴 const res = travelPlans && travelPlans.tuesday && travelPlans.tuesday.location && travelPlans.tuesday.location.href; // better 👶 // output undefined const res1 = travelPlans?.tuesday?.location?.href;
& & operator for multiple conditional judgments
If the function is called only when the variable is true, you can use && Operator.
// bad 👴 if (test1) { callMethod(); } // better 👶 test1 && callMethod();
When you want to conditionally render a component in React, this is more useful than (& &) short circuit. For example:
<div> {this.state.isLoading && <Loading />} </div>
switch simplification
We can save the conditions in the key value object and call them according to the conditions.
// bad 👴 switch (data) { case 1: test1(); break; case 2: test2(); break; case 3: test(); break; // And so on... } // better 👶 var data = { 1: test1, 2: test2, 3: test, }; // If type stay data exists, Then execute the corresponding function data[type] && data[type]();
Default parameter value
// bad 👴 function add(test1, test2) { if (test1 === undefined) test1 = 1; if (test2 === undefined) test2 = 2; return test1 + test2; } // better 👶 add = (test1 = 1, test2 = 2) => test1 + test2; add(); //output: 3
Condition finding simplification
If we want to call different methods based on different types, we can use multiple else if statements or switches, but is there a better simplification technique than this? In fact, the previous switch is simplified in the same way!
// bad 👴 if (type === "test1") { test1(); } else if (type === "test2") { test2(); } else if (type === "test3") { test3(); } else if (type === "test4") { test4(); } else { throw new Error("Invalid value " + type); } // better 👶 var types = { test1, test2, test3, test4, }; types[type] && types[type]();
Object attribute assignment
let test1 = "a"; let test2 = "b"; // bad 👴 let obj = { test1: test1, test2: test2 }; // better 👶 let obj = { test1, test2 };
Destructuring assignment
// bad 👴 const test1 = this.data.test1; const test2 = this.data.test2; const test2 = this.data.test3; // better 👶 const { test1, test2, test3 } = this.data;
Template string
If you're tired of using + to connect multiple variables into a string, this simplification technique will stop you from having a headache.
// bad 👴 const welcome = "Hi " + test1 + " " + test2 + "."; // better 👶 const welcome = `Hi ${test1} ${test2}`;
Cross line string
// bad 👴 const data = "abc abc abc abc abc abc\n\t" + "test test,test test test test\n\t"; // better 👶 const data = `abc abc abc abc abc abc test test,test test test test`;
Simplified bitwise operation of indexOf
When looking up a value of an array, we can use indexOf() method. But there is a better way. Let's take a look at this example.
// bad 👴 if (arr.indexOf(item) > -1) { // item found } if (arr.indexOf(item) === -1) { // item not found } // better 👶 if (~arr.indexOf(item)) { // item found } if (!~arr.indexOf(item)) { // item not found }
The bitwise (~) operator returns true (- 1 excluded), reverse operation only needs! ~. Alternatively, you can use includes() Function.
if (arr.includes(item)) { // true if the item found }
Convert string to number
There are built-in methods, such as parseInt and parseFloat, that can be used to convert strings to numbers. We can also simply provide a unary operator (+) before the string to achieve this.
// bad 👴 let total = parseInt("453"); let average = parseFloat("42.6"); // better 👶 let total = +"453"; let average = +"42.6";
Execute promise sequentially
What if you have a bunch of asynchronous or ordinary functions that return promise and ask you to execute one by one?
async function getData() { const promises = [fetch("url1"), fetch("url2"), fetch("url3"), fetch("url4")]; for (const item of promises) { // Print promise console.log(item); } // better 👶 for await (const item of promises) { // Print out the result of the request console.log(item); } }
Wait for all promise s to complete
The Promise.allSettled() method accepts a set of Promise instances as parameters and wraps them into a new Promise instance. Only wait until all these parameter instances return results, whether or not fulfilled still The wrapper instance will not end until it is rejected
Sometimes, we don't care about the result of asynchronous requests, only whether all requests have ended. At this point, the Promise.allSettled() method is useful
const promises = [fetch("index.html"), fetch("https://does-not-exist/")]; const results = await Promise.allSettled(promises); // Filter out successful requests const successfulPromises = results.filter((p) => p.status === "fulfilled"); // Filter out the failed requests and output the reason const errors = results .filter((p) => p.status === "rejected") .map((p) => p.reason);
Swap positions of array elements
// bad 👴 const swapWay = (arr, i, j) => { const newArr = [...arr]; let temp = newArr[i]; newArr[i] = list[j]; newArr[j] = temp; return newArr; };
Starting with ES6, it became much easier to exchange values from different locations in the array
// better 👶 const swapWay = (arr, i, j) => { const newArr = [...arr]; const [newArr[j],newArr[i]] = [newArr[i],newArr[j]]; return newArr; };
Use variables as object keys
Use it when you have a string variable and want to use it as a key in an object to set a value
let property = "a"; const obj = { b: "b", }; property = "name"; obj[property] = "This is A"; // {b: "b", name: "This is A"} console.log(obj);
Random number generator with range
Sometimes you need to generate random numbers, but if you want these numbers to be within a certain range, you can use this tool.
function randomNumber(max = 1, min = 0) { if (min >= max) { return max; } return Math.floor(Math.random() * (max - min) + min); }
Generate random colors
function getRandomColor() { const colorAngle = Math.floor(Math.random() * 360); return `hsla(${colorAngle},100%,50%,1)`; }
Get the last item in the list
In other languages, this function is made into a method or function that can be called in an array, but in JavaScript, you have to do some work yourself.
let array = [0, 1, 2, 3, 4, 5, 6, 7]; console.log(array.slice(-1)) >>> [7]; console.log(array.slice(-2)) >>> [6, 7]; console.log(array.slice(-3)) >>> [5, 6, 7];
function lastItem(list) { if (Array.isArray(list)) { return list.slice(-1)[0]; } if (list instanceof Set) { return Array.from(list).slice(-1)[0]; } if (list instanceof Map) { return Array.from(list.values()).slice(-1)[0]; } }
Lazy loading of pictures
In the implementation of lazy loading, there are two key values: one is the height of the current visual area, and the other is the height of the element from the top of the visual area.
The height of the current viewing area can be obtained with the window.innerHeight attribute in modern browsers and browsers above IE9. In the standard mode of low version ie, you can use document.documentElement.clientHeight to obtain it. Here we are compatible with two cases:
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
For the height of the element from the top of the visual area, we use the getBoundingClientRect() method to obtain the size of the returned element and its position relative to the viewport. MDN gives a very clear explanation for this:
The return value of this method is a DOMRect object, which is a collection of rectangles returned by the getClientRects() method of the element, that is, a collection of CSS borders related to the element.
The DOMRect object contains a set of read-only properties -- left, top, right, and bottom -- that describe the border in pixels. All attributes except width and height are relative to the upper left corner of the viewport.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Lazy-Load</title> <style> .img { width: 200px; height: 200px; background-color: gray; } .pic { // Required img styles } </style> </head> <body> <div class="container"> <div class="img"> // Note that we did not introduce real src for it <img class="pic" alt="Loading" data-src="./images/1.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/2.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/3.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/4.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/5.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/6.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/7.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/8.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/9.png" /> </div> <div class="img"> <img class="pic" alt="Loading" data-src="./images/10.png" /> </div> </div> </body> </html>
<script> // Get all picture labels const imgs = document.getElementsByTagName('img') // Gets the height of the visible area const viewHeight = window.innerHeight || document.documentElement.clientHeight // num is used to count which picture is currently displayed to avoid checking whether it is exposed from the first picture every time let num = 0 function lazyload(){ for(let i=num; i<imgs.length; i++) { // Subtract the height of the top of the element from the top of the visible area by the height of the visible area let distance = viewHeight - imgs[i].getBoundingClientRect().top // If the height of the visible area is greater than or equal to the height from the top of the element to the top of the visible area, the element is exposed if(distance >= 0 ){ // Write real src to the element and show the image imgs[i].src = imgs[i].getAttribute('data-src') // The first i pictures have been loaded. Next time, check whether they are exposed from i+1 num = i + 1 } } } // Listen for Scroll events window.addEventListener('scroll', lazyload, false); </script>
Picture preload
class PreLoadImage { constructor(imgNode) { // Get the real DOM node this.imgNode = imgNode; } // src attribute of operation img node setSrc(imgUrl) { this.imgNode.src = imgUrl; } } class ProxyImage { // url address of bitmap static LOADING_URL = "xxxxxx"; constructor(targetImage) { // The target Image is the PreLoadImage instance this.targetImage = targetImage; } // This method mainly operates the virtual Image to complete the loading setSrc(targetUrl) { // When the real img node is initialized, a bitmap is displayed this.targetImage.setSrc(ProxyImage.LOADING_URL); // Create a virtual Image instance to help us load pictures const virtualImage = new Image(); // Listen to the loading of the target image, and set the src attribute of the real img node on the DOM as the url of the target image when it is completed virtualImage.onload = () => { this.targetImage.setSrc(targetUrl); }; // Set the src property and the virtual Image instance starts loading pictures virtualImage.src = targetUrl; } }
ProxyImage helps us schedule the work related to preloading. Through ProxyImage, we can achieve indirect access to real img nodes and get the desired effect.