JavaScript语言的出现,在最开始的只能做一些简单的交互,到现在可以进行各种复杂的逻辑处理,期间经历了很多变革与升级,从那之后,JavaScript语言进入了众多开发者的视眼。
NodeJs的出现给前端开发人员带来了基于JS语言的服务器解决方案,还有WebPack、Grunt、Gulp等构建的采用,使JavaScript的前景更加无限可能。
与此同时,任何开发语言都会经历:开发过后的代码,如何才能具有可维护性、易理解等优点,对于任何一个开发者而言,都是一个挑战。
不久之后便出现了ES6语法,它让JavaScript的编码方式变得更加优雅而高效。
本文则重点介绍10种最新的JavaScript语法,它们的出现无疑对开发者的开发效率提高不少,大家拭目以待。
JS的任何语法都是已封装好的,小鲸建议:我们不能只做到会用的程度,如果有可能,希望大家要理解其语法的实现原理,这样可以帮助大家能更快的吸收此知识点,举一反三。
👨💻 用白话讲技术的打工人 - 小鲸
1. 字符串填充
🔸 String.prototype.padEnd()
此方法会将一个传入的字符串填充到当前字符串(可以多次重复添加),直至达到指定长度为止。
在下面的例子中,JavaScript字符串将会被#号多次添加,直至长度达到15个为止。
const str: String = 'JavaScript';
// 不够15的话,会重复在当前字符串后面添加#号,直至长度达到15位
console.warn('No. 01 = ' + str.padEnd(15, '#'));
// 如果只传入填充长度,不传入添加字符串,则默认按空格来填补,直至长度达到30位
console.info('No. 02 = ' + str.padEnd(30));
// 如果传入的长度小于当前字符串长度,则不处理,并且返回当前字符串
console.warn('No. 03 = ' + str.padEnd(-10, '?'));
// 重复添加字符串,直至达到规定长度
console.info('No. 04 = ' + str.padEnd(15, 'love'));
🔸 String.prototype.padStart()
此语法与String.prototype.padEnd()完全相反,看字面意思大家就不难看出:是在当前字符串的前面追加,而不是末尾。
此时大家应该能想到,有很多使用场景会用到它,比如:屏蔽电子邮件地址或者手机号码,姓名也可以,如果你愿意的话。
const str: string = '15034225566';
const strLength: number = str.length;
const sliceStr: string = str.slice(-4);
const nameStr: string = '我是一个最长的名字,叫:王小五';
const nameLength: number = nameStr.length;
const sliceName: string = nameStr.slice(-1);
// 在当前字符串前面追加指定字符串,直至达到规定长度为止
console.warn('No. 01 = ' + sliceStr.padStart(strLength, '*'));
// 换成汉字也可以,比如编辑注册信息时,不想让对方看到名字全称
console.info('No. 02 = ' + sliceName.padStart(nameLength, '*'));
2. Array.prototype.includes()
该方法判断目标元素是否存在于数组中,如果存在,则返回true;否则返回false。
其中该方法的第二个参数fromIndex为可选参数,代表从指定索引开始搜索数组。(默认值:0)
PS:如果fromIndex的值为负数,则按升序规则,从array.length + fromIndex的索引开始搜索。
举个例子:
const arr: Array<string> = ['Apple', 'Banana', 'Cherry'];
// false
console.warn('No. 01 = ' + arr.includes('Peach'));
// true
console.info('No. 02 = ' + arr.includes('Banana'));
// true,从索引为1的值开始搜索,代表:Banana
console.warn('No. 03 = ' + arr.includes('Banana', 1));
// false,从索引为2的值开始搜索,代表:Cherry
console.info('No. 04 = ' + arr.includes('Banana', 2));
// false,-1代表:arr.length + formIndex = 4 + -1 = 3
console.warn('No. 05 = ' + arr.includes('Banana', -1));
// true,-3代表:arr.length + formIndex = 4 + -3 = 1
console.info('No. 06 = ' + arr.includes('Banana', -3));
3. Rest参数 & Spread语法
🔸 Rest参数
当我们尝试创建一个参数数量不定的函数时,Rest参数就会显得特别有用。(也称为扩展运算符)
它的表达方式很简单,只需要:英文输入法方式下的三个点...。
在函数定义时,只需要在参数名称前面添加一个...,此时将创建一个数组列表,该数组将收集所有其他参数。
举例子吧:
function getSum(...numbers: Array<number>) {
return numbers.reduce((acc: number, current: number) => acc + current);
}
console.warn('No. 01 = ' + getSum(15, 20));
console.info('No. 02 = ' + getSum(1, 5, 10, 30));
console.warn('No. 03 = ' + getSum(100, 200, 300, 1001));
我们也可以只定义前几个参数,让剩余的参数收集在扩展数组中。
function getSum(one: number, two: number, ...numbers: Array<number>) {
console.warn('No. 01 = ' + one);
console.info('No. 02 = ' + two);
console.warn('No. 03 = ' + numbers);
}
getSum(1, 2, 10, 15, 20);
但有一点需要注意:它只能从正面进行解析,却不能反着来。
换句话说,最后一个参数只能、仅可以是rest参数。
function getSum(...numbers: Array<number>, one: number, two: number) {
console.warn('No. 01 = ' + one);
console.info('No. 02 = ' + two);
console.warn('No. 03 = ' + numbers);
}
getSum(1, 2, 10, 15, 20);
🔸 Spread语法
Rest参数的使用方法与Spread语法看起来是一样的,一回事,然而他们的不同之处就在于使用方式的不同。
在函数中调用
我们还用getSum这个方法来举例子,此时该方法通过扩展运算传递一个数组,其本质上是会将此参数扩展为一个列表,并且它接受无限量的参数。
如果我们需要传递一个数组时,我们会这样做:
function getSum(...numbers: Array<number>): number {
return numbers.reduce((acc, current) => acc + current);
}
const numberArray: Array<number> = [1, 3, 5];
console.warn('No. 01 = ' + getSum(...numberArray));
// 执行上面的操作,相当于扩展运算符做了如下操作,把数组全部展开
console.info('No. 02 = ' + getSum(numberArray[0], numberArray[1], numberArray[2]));
浅拷贝
我们也可以使用扩展运算,对任何变量进行浅克隆,并且这样的操作并不会影响原始变量的数据。
const numberArray: Array<number> = [1, 3, 5];
const cloneNumberArray: Array<number> = [...numberArray];
cloneNumberArray.push(10);
console.warn('No. 01 = ' + numberArray);
console.info('No. 02 = ' + cloneNumberArray);
numberArray.push(1000);
console.warn('No. 03 = ' + numberArray);
console.info('No. 04 = ' + cloneNumberArray);
cloneNumberArray.shift();
console.warn('No. 05 = ' + numberArray);
console.info('No. 06 = ' + cloneNumberArray);
如果目标变量是对象的情况下使用该方法,则会变得非常有用,简单。
interface User {
name: string;
sex: string;
age: number;
sexual_orientation?: string;
}
const userInfo: User = {
name: '小明',
sex: '中性',
age: 22
};
// 对原始数据进行浅克隆
const cloneUserInfo = { ...userInfo };
// 对克程对象添加一个自定义属性后,并不会影响原始对象数据
cloneUserInfo.sexual_orientation = 'woman';
console.warn('No. 1 = ' + JSON.stringify(userInfo));
// 单独解构sex变量,剩下所有变量通过扩展运算解构
const { sex, ...otherUserInfo } = userInfo;
console.info('No. 2 = ' + sex);
console.warn('No. 3 = ' + JSON.stringify(otherUserInfo));
// 改变原始数据的年龄
const updateUserInfo = {
age: 18,
...userInfo
};
console.info('No. 4 = ' + JSON.stringify(updateUserInfo));
// 需要把改变的值放在扩展运算符后面,而不是前面,上面的操作其实进行了下面的扩展
// 后面的值会把前面的覆盖掉
// const updateUserInfo = {
// name: '小明',
// sex: '中性',
// age: 22,
// age: 18,
// };
// 增加其它扩展属性的写法
const expendUserInfo = {
weight: '50kg',
height: '170cm'
};
const newUserInfo = {
...userInfo,
...expendUserInfo
};
console.warn('No. 5 = ' + JSON.stringify(newUserInfo));
4. 对象迭代器
对象迭代器下面有三个很有用的方法,分别是:keys()、values()、entries()。
keys()方法返回对象的所有键名列表。
values()方法返回对象的所有值列表。
entries()方法返回对象所有的key和value,以[key, value]的键值对形式展现。
interface User {
name: string;
sex: string;
age: number;
}
const userInfo: User = {
name: '小明',
sex: '中性',
age: 22
};
console.warn('No. 1 = ' + JSON.stringify(Object.keys(userInfo)));
console.info('No. 2 = ' + JSON.stringify(Object.values(userInfo)));
console.warn('No. 3 = ' + JSON.stringify(Object.entries(userInfo)));
5. 可选链操作符
我们经常会遇到这样的场景:
判断当一个属性存在时,我们才会去调用它,或者当前属性下的挂载方法,要不然会报错:
Error: Cannot read property 'children' of undefined.
undefined下怎么会有children属性呢......
interface User {
name: string;
age: number;
}
const userInfo: User = {
name: '小白',
age: 18
};
// 当我们需要判断更深一层的属性时,也许会这样做
if (userInfo.sex) {
if (userInfo.sex.children) {
console.log('NO. 1 = 存在这个属性,1');
}
}
// 或者简单点这样写
if (userInfo.sex && userInfo.sex.children) {
console.log('No. 2 = 存在这个属性,2');
}
// 但难免会直接调用,遇到这样的错误
console.log(userInfo.sex.children);
console.log('No. 3 = 这里会报错');
当处理不可靠、不可预测的数据时,可选链?.就派上用场了。
可选链表示:只有当调用链不是无效的(即不是null或undefined)时,运算才会进行更深一层的调用。
否则直接中断操作并返回undefined,此过程不会报错。
interface User {
name: string;
age: number;
sex?: string;
}
const userInfo: User = {
name: '小白',
age: 18
};
// 当我们需要判断更深一层的属性时,也许会这样做
if (userInfo.sex) {
if (userInfo.sex.children) {
console.log('NO. 1 = 存在这个属性,1');
}
}
// 或者简单点这样写
if (userInfo.sex && userInfo.sex.children) {
console.log('No. 2 = 存在这个属性,2');
}
// 换成可选链
console.warn(userInfo.sex?.children);
console.info('No. 3 = 这里不会报错');
// 其实可选链和上面定义User的时候,使用的?:可选参数,是大同小异啦。
可选链会在尝试访问userInfo.sex.children之前,先隐式地检查并确定userInfo.sex既不是null也不是undefined。
如果userInfo.sex是null或undefined,表达式就会进行短路计算中断访问,直接返回undefined。
这等价于以下表达式:
return ((userInfo.sex === null || userInfo.sex === undefined) ? undefined : userInfo.sex.second);
更多参考,请到这里查看:developer.mozilla.org/zh-CN/docs/…
6. 空值合并运算符
在说空值合并运算符之前,先来说一下||运算符(也称或者运算符),对于前端开发者来说,应该不会陌生吧。
||运算符: 如果你的变量为假('', 0, undefined, null, false),则会执行后面的默认值。
空值合并运算符使用??来表达,与||运算符非常相似,但一些区别:
??运算符: 如果你的变量为假(null, undefined),则会执行后面的默认值。
通过下面的例子看,会更直观明了一些:
const defaultVariable1: string = 0 || 'default';
const defaultVariable2: string = null || 'default';
const defaultVariable3: string = false || 'default';
const defaultVariable4: string = undefined || 'default';
const defaultVariable5: string = 'apple' || 'banana';
console.log(defaultVariable1, defaultVariable2, defaultVariable3, defaultVariable4, defaultVariable5);
const normalVariable1: number | string = 0 ?? 'normal';
const normalVariable2: string = null ?? 'normal';
const normalVariable3: boolean | string = false ?? 'normal';
const normalVariable4: string = undefined ?? 'normal';
const normalVariable5: string = 'boy' ?? 'girl';
console.log(normalVariable1, normalVariable2, normalVariable3, normalVariable4, normalVariable5);
7. 逻辑赋值运算符
逻辑与赋值(即&&=)运算符仅在变量为真时赋值,而逻辑或赋值(即||=)运算符将在变量为假时赋值。
interface User {
name: string,
age: number,
sex: string
}
const newUser: User = {
name: '小明',
age: 22,
sex: ''
};
newUser.name ||= '小花';
console.warn('No. 1 = ' + newUser.name);
newUser.sex ||= 'boy';
console.info('No. 2 = ' + newUser.sex);
newUser.name &&= '王重阳';
console.warn('No. 3 = ' + newUser.name);
newUser.age &&= 20;
console.info('No. 4 = ' + newUser.age);
当我们不确定对象中是否存在某个属性,并且需要以某种方式去操作它的情况下,逻辑或赋值会更容易处理这种情况。
举个例子:
我们需要查找一个或多个元素在数组中出现的重复次数,我们可以这样做:
const tempArray: number[] = [1, 2, 3, 3, 5, 6, 1, 6, 7, 8, 3];
const tempMap: any = {};
tempArray.forEach(item => {
tempMap[item] ||= 0;
tempMap[item] ++;
});
console.warn(JSON.stringify(tempMap));
8. 数字分隔符
当我们在处理数字或者金钱的时候,往往会因为值太大,可能很难读出并理解此数字,比如下面:
const value: number = 100000000;
数字分隔符很类似于价格分隔符,比如我们常见的银行存款,主要以,号为分隔符,为了更易读与理解。
而数字分隔符的出现,允许在数字文字中使用下划线作为分隔符,例如:可以编写10000为10_000。
我们可以这样写:
const value: number = 100_000_000;
数字分隔符也适用于八进制、十六进制与二进制数:
const octalNumber: number = 0o32_12;
const hexNumber: number = 0xff_55_00;
const binaryNumber: number = 0b1010_1011_1111;
console.log(octalNumber, hexNumber, binaryNumber);
9. 模板字符串
在模板字符串出现前,我们在写代码拼接字符串时,会不会这样:
class Item {
id: number;
img: string;
username: string;
level: number;
}
const results: Item[] = [
{ id: 1002, img: 'https://www.baidu.com/1.jpg', username: '小明', level: 100 },
{ id: 50, img: 'https://www.163.com/love.png', username: '凡死了', level: 50 }
];
let html: string = '';
results.forEach(item => {
html += '<div class="item"><img src="'+ item.img +'" /><div class="user"><span class="user-name">'+ item.username +'</span><i>'+ item.level +'</i></div></div>';
// ...伪代码
});
或者是这样:
results.forEach(item => {
html += '<div class="item">\
<img src="'+ item.img +'" />\
<div class="user">\
<span class="user-name">'+ item.username +'</span>\
<i>'+ item.level +'</i>\
</div>\
</div>';
// ...伪代码
});
如果代码量大、字段多,那整个人是不是就疯了......
模板字符串使处理字符串变得更加容易,我们可以去掉转义字符,得到与预期代码完全一样的输出。
方法是用反引号(即`, 一般在键盘ESC的下面那个按键)包裹它。
const graph: string = `我只是一段平凡的文字 /$%^&$ ,花里胡哨的。`;
console.info(graph);
模板字符串还具有另一个非常有用的特性:表达式插值。
大多数时候,我们希望在字符串之间添加某种表达式,并且代码的可读性不会受到+字符串拼接的重大影响,更好的办法是将表达式${expression}包含在模板中。
const userName: string = '小明';
const userAge: number = 18;
const message: string = `
1. 大家好,我是一名普通的开发程序员。
2. 我的名字叫:${userName}。
3. 我今年:${userAge}。
`;
console.log(message);
10. 默认参数
在定义函数的时候,我们可以使用默认值来初始化参数。
这在我们需要创建一个可以带或者不带任何参数调用的函数时,显得很有用。
相比之前的用法不仅更加直观,也更语义化。
之前:
function sayJs(code) {
var type = code || 'JavaScript';
return 'Hi, I Love ' + type;
}
const defaultResult = sayJs();
console.warn('No. 1 = ' + defaultResult);
const normalResult = sayJs('Python');
console.info('No. 2 = ' + normalResult);
现在:
function sayJs(code: string = 'JavaScript'): string {
return `Hi, I Love ${code}`;
}
const defaultResult = sayJs();
console.warn('No. 1 = ' + defaultResult);
const normalResult = sayJs('Python');
console.info('No. 2 = ' + normalResult);
最后
感谢您抽出宝贵的时间阅读本文,希望对您有所帮助。
如果您遇到什么疑问或者建议,欢迎多多交流,大家共同进步。
在阅读过程中,如果有不正确的地方,希望您能提出来,我会努力改正并提供更优质的文章。
结语
「鲸选派,一个专注于使用白话讲解技术的公众号,关注公众号,更多干货等着你喔!」
- 关注后回复
资料免费领取学习资料 - 关注后回复
进群拉你进技术交流群 - 关注后回复
福利查看最新、即将开展的福利活动,先到先得 - 欢迎关注
鲸选派,更多「福利干货」及时推送