10种必会JavaScript语法让你:爽快编码

155 阅读11分钟

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(12101520);

🔸 Spread语法

Rest参数的使用方法与Spread语法看起来是一样的,一回事,然而他们的不同之处就在于使用方式的不同。

在函数中调用

我们还用getSum这个方法来举例子,此时该方法通过扩展运算传递一个数组,其本质上是会将此参数扩展为一个列表,并且它接受无限量的参数。

如果我们需要传递一个数组时,我们会这样做:

function getSum(...numbers: Array<number>): number {
    return numbers.reduce((acc, current) => acc + current);
}

const numberArrayArray<number> = [135];

console.warn('No. 01 = ' + getSum(...numberArray));

// 执行上面的操作,相当于扩展运算符做了如下操作,把数组全部展开
console.info('No. 02 = ' + getSum(numberArray[0], numberArray[1], numberArray[2]));

浅拷贝

我们也可以使用扩展运算,对任何变量进行浅克隆,并且这样的操作并不会影响原始变量的数据。

const numberArrayArray<number> = [135];
const cloneNumberArrayArray<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 = 这里会报错');

当处理不可靠、不可预测的数据时,可选链?.就派上用场了。

可选链表示:只有当调用链不是无效的(即不是nullundefined)时,运算才会进行更深一层的调用。

否则直接中断操作并返回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.sexnullundefined,表达式就会进行短路计算中断访问,直接返回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 valuenumber = 100000000;

数字分隔符很类似于价格分隔符,比如我们常见的银行存款,主要以,号为分隔符,为了更易读与理解。

而数字分隔符的出现,允许在数字文字中使用下划线作为分隔符,例如:可以编写10000为10_000

我们可以这样写:

const valuenumber = 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 graphstring = `我只是一段平凡的文字 /$%^&$ ,花里胡哨的。`;

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);

 

最后

感谢您抽出宝贵的时间阅读本文,希望对您有所帮助。

如果您遇到什么疑问或者建议,欢迎多多交流,大家共同进步。

在阅读过程中,如果有不正确的地方,希望您能提出来,我会努力改正并提供更优质的文章。

 

结语

「鲸选派,一个专注于使用白话讲解技术的公众号,关注公众号,更多干货等着你喔!」

  • 关注后回复资料免费领取学习资料
  • 关注后回复进群拉你进技术交流群
  • 关注后回复福利查看最新、即将开展的福利活动,先到先得
  • 欢迎关注鲸选派,更多「福利干货」及时推送