【ES6_01】不得不了解的一些ES6写法

295 阅读8分钟

模块化

本文主要记录了如下内容:

  • JS模块化
  • 扩展符
  • ES6解构赋值

本文适用于刚接触ES6的新手小白, 很基础,前端和全栈大佬勿喷!

JavaScript模块化有好几种,如 ES6 支持的模块化、CommandJS 或者AMD 等,这里介绍的是 ES6 的模块化。

在 ES6 中,模块化相关的指令有 importexport 这两个指令,export 主要用于规定模块的对外接口,提供给其他模块使用;import 主要用于在需要使用的引入需要用的接口功能。

模块:通常指的是一个独立的文件,该文件内部的变量,函数不export则其他文件无法使用。就像在Java中需要import其他的包名或者类的作用一致。只不过Java不需要export

如在 UserService.js 中,通过输出一个insertNewUser给其他模块使用。

export const insertNewUser = (user) => {
    return axiosUtil.post('/manage/user/insert.do', user)
}

在需要使用的地方如UserAdd.vue中引入即可使用。

import { insertNewUser } from '../../service/UserService'

同样的,我们也可以抛出一个成员属性如export const m = 1;这样也是可以的,但是如下的写法是会出错的。

// 方式一
export 1;
// 方式二
const n = 1
export n;

正确的写法如下:

const n =1
export n;
export {
	n
}
// eg2
const age = 10
export {
	age as year
}

以第二例子为例说明,ageage变量同名,向外提供一个名为year的接口[相当于取别名],供外部访问。同样的上面的函数也可以改写为如下方式:

const insertNewUser = (user) => {
    return axiosUtil.post('/manage/user/insert.do', user)
}
export {
	insertNewUser as addUser
}

需要说明的是,export 输出的接口和其值是动态绑定的即源文件中的age发生变化之后,其他模块也能访问到变化的值。这一点与CommonJS不同。后者导出的是值的缓存。

上述的export导出的方式,在使用import命令导入时,需要我们知道导出的名称才可以。如果将新增用户的函数改名为addUser之后,就需要改变其他文件引入的函数名称,否则将会出现错误。

为了解决这个问题,我们可以使用提供的export default作为模块的默认输出,这样在import时就可以使用任意名称命名,书写方式如下:

// 错误写法 [原因见后文]
export default const insertNewUser = (user) => {
    return axiosUtil.post('/manage/user/insert.do', user)
}
// 正确写法 01
export default function insertNewUser (user)  {
    return axiosUtil.post('/manage/user/insert.do', user)
}
// 正确写法 02						  
var insertNewUser = "";							  
export default insertNewUser = (user) => {
    return axiosUtil.post('/manage/user/insert.do', user)
}
							  
import addUser from '../../service/UserService'

这样引入时,也就不需要添加{},直接引入即可。

使用 export default 有利就有弊,既然作为一个模块的默认输出,也就代表这一模块文件里面只能有一个export default。本质上,export default就是输出一个叫做default的变量或方法,然后系统允许我们取任意名字。正因为如此在default 不能跟着变量申明语句。

扩展符

扩展运算符

扩展运算符是三个点...【类比于Java中的不定长参数】,可以用来打开一个数组或者一个对象,这样在进合并对象时,或者对象赋值时,就可以省略掉一个属性一个属性的赋值,就可以快速操作!PS:Object.assign()常用来这么干。写在这里主要是在看别人代码的一些骚操作时,能看懂。

const obj  ={
	name :123,
	age:10
}
const obj2 = {
	grade:10,...obj
}
// name age
console.log(obj)
// name age grade
console.log(obj2)

const obj3 = {
	...obj
}
// false
console.log(obj === obj3)

可以看到 obj3是对obj的值的赋值,而不是引用的赋值。同样也适用于数组如

const array = [1,2,3];
const array2 = [...array]
array2[1] = 4
// output 4
console.log(array2[1])
// output 2
console.log(array[1])

对象的扩展

简洁表达式

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法【建议对比看一下es5的写法】。如

const name = '壹贰是只猫';
const cat = {name};
cat // {name: "壹贰是只猫"}

这样就避免了在赋值对象是需要这样写

const name = '壹贰是只猫';
const cat = {
	name:name
}

简化了代码。除了属性名之外,也支持方法的简化书写:

const user = {
    getUserName(){
	retun "username";
    }
}
// 等价于
const user = {
    getUserName: function(){
	retun "username";
    }
}

属性名表达式

JavaScript定义对象属性时,可以通过如下方式进行属性定义:

user.name = "壹贰是只猫";
user['gender'] = "雌"

在上面的例子中,中括号里面可以接受一个表达式,将该表达式的运算的结果作为对象的key

let name =  e.target.dataset.name;
user[name] = "xxxx"

这里就会将name变量的值运算出来作为user的属性,其值为xxxx

这样的写法简化了很多写法如:当有很多的输入控件需要监听时,我们就可以写一个通用的监听输入函数,而不必一个一个的去写。ps:现在基本上都提供了双向绑定语法糖。一般用不到如下写法。

onChangeInput(e){
	// 控件上的name属性
	let name = e.target.dataset.name;
	// 他的值
	let value = Number(e.detail);
	this.setData({
	  [name]:value
	})
},

链式判断符和Null值判断符

链式判断运算符主要用在需要读取嵌套对象属性的时候,如在写Java开发时会遇到如下情况

if(user != null){
	if(user.address != null){
		if(user.address.detail != null){
			System.out.println(user.address.detail.code);
		}
	}
}

此种情况下就可以使用链式运算判断符

user?.address?.detaill?.code

这样就简化了很多代码,?.的运算逻辑就是左侧对象是否为null或者undefined。如果是则不继续运行,如果不是则执行后续的代码【与Kotlin中的?.操作符类似】。

Null判断运算符主要用于在读取对象属性时,如果某个属性是null 或者 undefined,需要为他们指定默认值。在ES5的写法是有时候会见到如下的代码:

const msg = responseData?.data?.message || "操作失败!";
messageUtil.errorMessage(msg);

在这里我们通过||指定了默认值,这样是能够实现我们的需求的,但是这样的写法是有问题的,假定我们只需要在undefined或者null的情况下指定默认值,但是上述的写法会在左侧的值为 0、false、空字符串也会指定默认值。这就出现了预期之外的错误。而Null判断运算符??就可以避免这种情况。

const msg = responseData?.data?.message ?? "操作失败!";

Null判断运算符也可以用于函数内部参数赋值:

function successMessage(prop){
	const msg = prop.data.message ?? "操作成功"; 
}

解构赋值

ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值,这个过程为解构,常使用的数组解构,对象解构,函数参数解构。【感觉像高中数学里面的等价替换】

数组解构

数组结构,只要左右两边的模式相同,左边的变量就会被赋予对应的值。如简单数组的解构赋值

// a = 1,b = 2 c = 3
let [a,b,c] = [1,2,3]

嵌套数组的解构赋值

// a = 1,b=2,d=4
let [a, [b], d] = [1, [2, 3], 4]

这里b = 2就进行了不完全解构,只匹配一部分的数组。在进行解构时,也可以指定默认值,因为解构失败时,左边的值会有undefined,这样我们可以通过指定默认值的情况去避免。

let [a,b,c = 4] = [1,2]

在上面的例子中c就会解构失败,如果不指定默认值,其值为就为undefined

对象解构

数组解构时,是按照元素位置来进行匹配的,而对象解构,则是根据变量名进行模式匹配的

 let { name, age } = { name: '对象解构', age: 35 };
 name // "对象解构"
 age // 35

如果没有相同的变量名,则值为undefined

 let { address } = { name: '对象解构', age: 35 };
 address // undefined

如果变量名与属性名不一致,又要解构成功就必须指定属性名,写法如下

let {address:addressInfo} = {address:"成华大道"}
addressInfo // 成华大道

左边的address和右边对象的address属性一致,然后将值赋值给addressInfo 实际上在第一个对象解构的例子中,可以看作如下代码:

 let { name:name, age:age } = { name: '对象解构', age: 35 };

只不过是将name:name,age:age进行了简写!

函数参数的默认值

在ES6之前,并不能直接给函数的形参指定默认值,只能以如下的方式指定。

function getFullName(firstName,lastName){
	firstName = firstName || "Tom";
	lastName = lastName || "Jerry"
	return firstName " & " + lastName;
}

这样写法的缺点在前面已经描述过了,如果为了更好的判断,还需要添加typeof firstName == 'undefined',增加额外的代码书写。为了更好的处理这种情况,ES6提供了简便的写法:

function getFullName(firstName = "Tom",lastName = "Jerry"){
	return firstName " & " + lastName;
}

但值得注意的是,ES6这种写法不能有同名参数,且不能用let/const再次声明。

函数参数+对象解构赋值这样在进行AJAX请求时,就可以指定默认的请求参数:

// 获取用户列表
export const fetchUserList = ({ currentPage = 1, currentSize = 10, username = '', } = {}) => {
    const params = {
        currentPage,
        currentSize,
    }
    
    ···
    return axiosUtil.get('/manage/user/list.do', { params })
}

如按照上面的请求封装,我们就可以不传递参数进行数据请求。

参考资料: