帶有多個條件的 if 語句
把多個值放在一個數(shù)組中,然后調(diào)用數(shù)組的 includes 方法。
// bad
if (x === "abc" || x === "def" || x === "ghi" || x === "jkl") {
//logic
}
// better
if (["abc", "def", "ghi", "jkl"].includes(x)) {
//logic
}
使用條件表達式簡化 if true...else
// bad
let test: boolean;
if (x > 100) {
test = true;
} else {
test = false;
}
// better
let test = x > 10 ? true : false;
//或者這樣
let test = x > 10;
console.log(test);
假值(undefined、null、0、false、NaN、空字符串)檢查
當我們創(chuàng)建了新變量,有時候想要檢查引用的變量是不是null 或 undefined或空字符串 等假值。JAVAScript 確實有一個很好的快捷方式來實現(xiàn)這種檢查-邏輯或操作符(||)
||會在左側(cè)操作數(shù)為假值時返回右側(cè)操作數(shù)
只有當左側(cè)為:
- 空字符串: ''或``
- NaN
- 0
- null
- undefined
- false
邏輯或操作符(||) 會返回有右側(cè)的值
// 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
}
注意:如果 test1 有值,將執(zhí)行 if 之后的邏輯,這個操作符主要用于 null,undefinded,空字符串 檢查。
使用空值合并操作符-??
只有當左側(cè)為
- null
- undefined
空值合并操作符(??) 會返回右側(cè)的值
const baz = 0 ?? 42;
console.log(baz);
// expected output: 0
注意:與邏輯或操作符(||)不同,||會在左側(cè)操作數(shù)為假值時返回右側(cè)操作數(shù)
只有當左側(cè)為:
- 空字符串: ''或``
- NaN
- 0
- null
- undefined
邏輯或操作符(||) 會返回有右側(cè)的值
var a = "" || 1;
// 輸出 1
console.log(a);
null 檢查和默認賦值
let test1 = null;
let test2 = test1 ?? "";
console.log("null check", test2); // 輸出空字符串 ""
undefined 檢查和默認賦值
const test = undefined ?? "default";
console.log(test);
// expected output: "default"
比較后返回
// bad
let test;
function checkReturn() {
if (!(test === undefined)) {
return test;
} else {
return callMe("test");
}
}
// better
function checkReturn() {
return test ?? callMe("test");
}
使用可選鏈操作符-?.
?. 也叫鏈判斷運算符。它允許開發(fā)人員讀取深度嵌套在對象鏈中的屬性值,而不必驗證每個引用。當引用為空時,表達式停止計算并返回 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
// 輸出 undefined
const res1 = travelPlans?.tuesday?.location?.href;
用于多個條件判斷的 && 操作符
如果只在變量為 true 時才調(diào)用函數(shù),可以使用 && 操作符。
// bad
if (test1) {
callMethod();
}
// better
test1 && callMethod();
當你在 React 中想要有條件地渲染某個組件時,這個與 (&&)短路寫法比較有用。例如:
<div> {this.state.isLoading && <Loading />} </div>
switch 簡化
我們可以將條件保存在鍵值對象中,并根據(jù)條件來調(diào)用它們。
// 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,
};
// 如果type 在 data中存在, 則執(zhí)行對應的函數(shù)
data[type] && data[type]();
默認參數(shù)值
// 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
條件查找簡化
如果我們要基于不同的類型調(diào)用不同的方法,可以使用多個 else if 語句或 switch,但有沒有比這更好的簡化技巧呢?其實是前面的 switch 簡化方式一樣!
// 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]();
對象屬性賦值
let test1 = "a";
let test2 = "b";
// bad
let obj = { test1: test1, test2: test2 };
// better
let obj = { test1, test2 };
解構(gòu)賦值
// bad
const test1 = this.data.test1;
const test2 = this.data.test2;
const test2 = this.data.test3;
// better
const { test1, test2, test3 } = this.data;
模板字符串
如果你厭倦了使用 + 將多個變量連接成一個字符串,那么這個簡化技巧將讓你不再頭痛。
// bad
const welcome = "Hi " + test1 + " " + test2 + ".";
// better
const welcome = `Hi ${test1} ${test2}`;
跨行字符串
// bad
const data =
"abc abc abc abc abc abcnt" + "test test,test test test testnt";
// better
const data = `abc abc abc abc abc abc
test test,test test test test`;
indexOf 的按位操作簡化
在查找數(shù)組的某個值時,我們可以使用 indexOf() 方法。但有一種更好的方法,讓我們來看一下這個例子。
// 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
}
按位 (~) 運算符將返回 true(-1 除外),反向操作只需要!~。另外,也可以使用 includes() 函數(shù)。
if (arr.includes(item)) {
// true if the item found
}
字符串轉(zhuǎn)成數(shù)字
有一些內(nèi)置的方法,例如 parseInt 和 parseFloat 可以用來將字符串轉(zhuǎn)為數(shù)字。我們還可以簡單地在字符串前提供一個一元運算符 (+) 來實現(xiàn)這一點。
// bad
let total = parseInt("453");
let average = parseFloat("42.6");
// better
let total = +"453";
let average = +"42.6";
順序執(zhí)行 promise
如果你有一堆異步或普通函數(shù)都返回 promise,要求你一個接一個地執(zhí)行,怎么辦?
async function getData() {
const promises = [fetch("url1"), fetch("url2"), fetch("url3"), fetch("url4")];
for (const item of promises) {
// 打印出promise
console.log(item);
}
// better
for await (const item of promises) {
// 打印出請求的結(jié)果
console.log(item);
}
}
等待所有 promise 完成
Promise.allSettled()方法接受一組 Promise 實例作為參數(shù),包裝成一個新的 Promise 實例。只有等到所有這些參數(shù)實例都返回結(jié)果,不管是 fulfilled 還是 rejected,包裝實例才會結(jié)束
有時候,我們不關(guān)心異步請求的結(jié)果,只關(guān)心所有的請求有沒有結(jié)束。這時,Promise.allSettled()方法就很有用
const promises = [fetch("index.html"), fetch("https://does-not-exist/")];
const results = await Promise.allSettled(promises);
// 過濾出成功的請求
const successfulPromises = results.filter((p) => p.status === "fulfilled");
// 過濾出失敗的請求,并輸出原因
const errors = results
.filter((p) => p.status === "rejected")
.map((p) => p.reason);
交換數(shù)組元素的位置
// bad
const swapWay = (arr, i, j) => {
const newArr = [...arr];
let temp = newArr[i];
newArr[i] = list[j];
newArr[j] = temp;
return newArr;
};
ES6 開始,從數(shù)組中的不同位置交換值變得容易多了
// better
const swapWay = (arr, i, j) => {
const newArr = [...arr];
const [newArr[j],newArr[i]] = [newArr[i],newArr[j]];
return newArr;
};
使用變量作為對象鍵
當你有一個字符串變量,并想將其用作對象中的鍵以設置一個值時可以用它
let property = "a";
const obj = {
b: "b",
};
property = "name";
obj[property] = "這是A";
// {b: "b", name: "這是A"}
console.log(obj);
帶有范圍的隨機數(shù)生成器
有時你需要生成隨機數(shù),但希望這些數(shù)字在一定范圍內(nèi),那就可以用這個工具。
function randomNumber(max = 1, min = 0) {
if (min >= max) {
return max;
}
return Math.floor(Math.random() * (max - min) + min);
}
生成隨機顏色
function getRandomColor() {
const colorAngle = Math.floor(Math.random() * 360);
return `hsla(${colorAngle},100%,50%,1)`;
}
獲取列表最后一項
其他語言里這個功能被做成了可以在數(shù)組上調(diào)用的方法或函數(shù),但在 JavaScript 里面,你得自己做點工作。
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];
}
}
圖片懶加載
在懶加載的實現(xiàn)中,有兩個關(guān)鍵的數(shù)值:一個是當前可視區(qū)域的高度,另一個是元素距離可視區(qū)域頂部的高度。
當前可視區(qū)域的高度, 在和現(xiàn)代瀏覽器及 IE9 以上的瀏覽器中,可以用 window.innerHeight 屬性獲取。在低版本 IE 的標準模式中,可以用
document.documentElement.clientHeight 獲取,這里我們兼容兩種情況:
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
而元素距離可視區(qū)域頂部的高度,我們這里選用 getBoundingClientRect() 方法來獲取返回元素的大小及其相對于視口的位置。對此 MDN 給出了非常清晰的解釋:
該方法的返回值是一個 DOMRect 對象,這個對象是由該元素的 getClientRects() 方法返回的一組矩形的集合, 即:是與該元素相關(guān)的 css 邊框集合 。
DOMRect 對象包含了一組用于描述邊框的只讀屬性——left、top、right 和 bottom,單位為像素。除了 width 和 height 外的屬性都是相對于視口的左上角位置而言的。
<!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 {
// 必要的img樣式
}
</style>
</head>
<body>
<div class="container">
<div class="img">
// 注意我們并沒有為它引入真實的src
<img class="pic" alt="加載中" data-src="./images/1.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/2.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/3.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/4.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/5.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/6.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/7.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/8.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/9.png" />
</div>
<div class="img">
<img class="pic" alt="加載中" data-src="./images/10.png" />
</div>
</div>
</body>
</html>
<script>
// 獲取所有的圖片標簽
const imgs =
document.getElementsByTagName('img')
// 獲取可視區(qū)域的高度
const viewHeight = window.innerHeight ||
document.documentElement.clientHeight
// num用于統(tǒng)計當前顯示到了哪一張圖片,避免每次都從第一張圖片開始檢查是否露出
let num = 0
function lazyload(){
for(let i=num; i<imgs.length; i++) {
// 用可視區(qū)域高度減去元素頂部距離可視區(qū)域頂部的高度
let distance = viewHeight - imgs[i].getBoundingClientRect().top
// 如果可視區(qū)域高度大于等于元素頂部距離可視區(qū)域頂部的高度,說明元素露出
if(distance >= 0 ){
// 給元素寫入真實的src,展示圖片
imgs[i].src = imgs[i].getAttribute('data-src')
// 前i張圖片已經(jīng)加載完畢,下次從第i+1張開始檢查是否露出
num = i + 1
}
}
}
// 監(jiān)聽Scroll事件
window.addEventListener('scroll', lazyload, false);
</script>
圖片預加載
class PreLoadImage {
constructor(imgNode) {
// 獲取真實的DOM節(jié)點
this.imgNode = imgNode;
}
// 操作img節(jié)點的src屬性
setSrc(imgUrl) {
this.imgNode.src = imgUrl;
}
}
class ProxyImage {
// 占位圖的url地址
static LOADING_URL = "xxxxxx";
constructor(targetImage) {
// 目標Image,即PreLoadImage實例
this.targetImage = targetImage;
}
// 該方法主要操作虛擬Image,完成加載
setSrc(targetUrl) {
// 真實img節(jié)點初始化時展示的是一個占位圖
this.targetImage.setSrc(ProxyImage.LOADING_URL);
// 創(chuàng)建一個幫我們加載圖片的虛擬Image實例
const virtualImage = new Image();
// 監(jiān)聽目標圖片加載的情況,完成時再將DOM上的真實img節(jié)點的src屬性設置為目標圖片的url
virtualImage.onload = () => {
this.targetImage.setSrc(targetUrl);
};
// 設置src屬性,虛擬Image實例開始加載圖片
virtualImage.src = targetUrl;
}
}
ProxyImage 幫我們調(diào)度了預加載相關(guān)的工作,我們可以通過 ProxyImage 這個代理,實現(xiàn)對真實 img 節(jié)點的間接訪問,并得到我們想要的效果。
https://www.ixiera.com