ES6、7新特性

ES6、7新特性

  • 1 兼容性
  • 2 变量
  • 3 函数
  • 4 数组
    • 4.1 sort 排序
    • 4.2 map 一个对一个
    • 4.3 reduce 合并
    • 4.4 filter 过滤
    • 4.5 forEach 迭代
    • 4.6 concat 连接
  • 5 解构赋值
  • 6 字符串 连接
  • 7 面向对象
  • 8 Promise
  • 9 generator,yield
    • 9.1 传参
    • 9.2返回值
  • 10、模块化
  • 11 手写promise
  • 12 ES7新特性
    • 12.1.async/await 基础
    • 12.2.async 关键字
    • 12.3.await 关键字

1 兼容性

babel是react团队选择的在使用react过程中转换es*和jsx为es5语句的工具。
babel可将es6和es7语句转换为es5语句。
编译转换:(1)在线转换(2)提前编译

<script src='browser.js' charset='utf-8'></script>
jsxtransformer.js
babel.js
browser.js

将es6语法在线转换为es5语法:

<script src="browser.js" charset="UTF-8"></script>
<script type="text/babel">
	let a = 1;
	let b = 2;
	console.log(a + b)
</script>

2 变量

var 可以重复声明定义,无法限制修改,没有块级作用域
let 不可重复声明,变量->可以修改,块级作用域
const 不可重复声明,常量->不能修改,块级作用域
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script type="text/javascript">
			window.onload = () => {
				var btns = document.getElementsByTagName('input');
				for (var i = 0; i < btns.length; i++) {
					btns[i].onclick = () => {
						console.log(i) // var定义的变量,会导致全部打印3
					}
				}
			}
		</script>
	</head>
	<body>
		<input type="button" value="btn1" />
		<input type="button" value="btn2" />
		<input type="button" value="btn3" />
	</body>
</html>
<script type="text/javascript">
	window.onload = () => {
		var btns = document.getElementsByTagName('input');
		for (var i = 0; i < btns.length; i++) {
			((i) => { //这个i是一个形参,其实可以不叫i,任何一个变量均可
				btns[i].onclick = () => {
					console.log(i)  // 函数分别打印0,1,2
				}
			})(i) //这个i是一个实参
		}
	}
</script>
<script type="text/javascript">
	window.onload = () => {
		var btns = document.getElementsByTagName('input');
		for (let i = 0; i < btns.length; i++) {
			btns[i].onclick = () => {
				console.log(i) // let定义的变量,分别打印0,1,2
			}
		}
	}
</script>

3 函数

(1)如果函数只有一个参数,()可省略;
(2)如果函数只有一条语句,{}可省略;

<script type="text/javascript">
 // window.onload = () => {
 // 	console.log('测试')
 // }
 window.onload = () => console.log('测试'); //第二步:打印 测试

 let plusFun = params => params * 2;
 console.log(plusFun(2)) //第一步:打印 4
</script>

函数参数:
(1)收集剩余的参数 …xxx
let printlog = (p1 = 9, p2, ...args, p3) => { }
Uncaught SyntaxError: Rest parameter must be last formal parameter
(2)默认参数
let printlog = (p1 = 9, p2, ...args) => {}

<script type="text/javascript">
	let printlog = (p1, p2, ...args) => {
		console.log(p1)
		console.log(p2)
		console.log(args)
	}
	printlog(1)
	// 1
	// undefined
	// []
	printlog(1, 2, 3, 4, 5, 6)
	// 1
	// 2
	// [3, 4, 5, 6]
</script>

(3)展开数组 …xxx

<script type="text/javascript">
	let inner = (p1, p2) => {
			console.log(p1 + p2) // 4
	}
	((inner, ...args) => {
		inner(...args)
	})(inner, 1, 3)//将inner函数传入
</script>

4 数组

4.1 sort 排序

<script type="text/javascript">
	let arr = [2, 19, 1, 13, 4];
	console.log(arr); //[2, 19, 1, 13, 4]
	arr.sort();
	console.log(arr); //[1, 13, 19, 2, 4]
	arr.sort((p1, p2) => {
		return p1 - p2;
	})
	console.log(arr); //[1, 2, 4, 13, 19]
</script>

4.2 map 一个对一个

<script type="text/javascript">
	let arr = [1, 4, 3, 2];
	console.log(arr.map(item => item >= 3 ? '优' : '良')) //["良", "优", "优", "良"]
	let rslt1 = arr.map((item, idx, arr) => {
		return item * 2;
	})
	let rslt2 = arr.map(item => item * 3)
	console.log(arr); //[1, 4, 3, 2]
	console.log(rslt1); //[2, 8, 6, 4]
	console.log(rslt2) //[3, 12, 9, 6]
</script>

4.3 reduce 合并

<script type="text/javascript">
	let arr = [1, 4, 3, 2];
	/* 求和 */
	let rslt = arr.reduce((temp,item,idx)=>{
		console.log(temp,item,idx)
		// 1 4 1
		// 5 3 2
		// 8 2 3
		return temp + item;
	})
	console.log(rslt); // 10
	/* 求平均数 */
	rslt = arr.reduce((temp, item, idx) => {
		return arr.length - 1 == idx ? (temp + item) / arr.length : temp + item;;
	})
	console.log(rslt); // 2.5
</script>

4.4 filter 过滤

<script type="text/javascript">
	let arr = [{name:'李白',score:66},{name:'李清照',score:88},{name:'李贺',score:99}];
	let rslt = arr.filter((item,idx)=>{
		return item.score > 80;
	});
	console.log(rslt);//[{name: "李清照", score: 88},{name: "李贺", score: 99}]
</script>

4.5 forEach 迭代

<script type="text/javascript">
	let arr = [{name:'李白',score:66},{name:'李清照',score:88},{name:'李贺',score:99}];
	arr.forEach((item,idx)=>{
		console.log(this)//this是window对象,不是arr对象
		console.log(item,idx)
	})
</script>

4.6 concat 连接

concat连接两个或多个数组。该方法不改变原有数组,仅仅返回被连接数组的一个副本。
let rslt = arr.concat(arr1, arr2, arr3, …, arrX)

5 解构赋值

<script type="text/javascript">
let arr = [1,2,3]
const [x,y,z] = arr;// x,y,z将与arr中的每个位置对应来取值
console.log(x,y,z); // 1 2 3
const [a] = arr; //只匹配1个参数
console.log(a); // 1

--------------------------------
const person = {
	name:"jack",
	age:21,
	language: ['java','js','css']
}
// 解构表达式获取值
const {name,age,language} = person;
console.log(name);
console.log(age);
console.log(language);

--------------------------------
let arr1 = [1, 2]
let arr2 = [3, 4]
console.log([...arr1, ...arr2]) // [1, 2, 3, 4]
let [p1, p2] = [1, 2]
let [json1, arr1, str1, {p3, p4}] = 
	[{name: 'Michael'},
	 [1, 2], 
	 'HelloWorld', 
	 {id: '101', age: 99}
	]


//大括号括起来和不用大括号有什么区别?
let aaa = ({ p1, p2, p3 = null }) => {
	console.log(p1, p2, p3)
};
aaa({ p1: '1111', p2: '2222', p3: '33333' }) //1111 2222 33333
aaa(1111, 2222, 33333) //undefined undefined null

let bbb = (p1, p2, p3 = null) => {
	console.log(p1, p2, p3)
};
bbb({ p1: '1111', p2: '2222', p3: '33333' }) //{p1: "1111", p2: "2222", p3: "33333"} undefined null
bbb(1111, 2222, 33333) //1111 2222 33333
</script>
<script>
	// 常规的JS给函数传递一个对象参数
	let sayName = (person) => {
		if (Object.prototype.toString.call(person) == '[object Object]') {
			console.log(`${person.firstName}----${person.lastName}`);
		}
	}
	sayName({ firstName: 'Michael', lastName: 'Li' });//Michael----Li

	// ES6的对象可以实现简写
	let firstName = 'Michael';
	let lastName = 'Li';
	let person = { firstName, lastName }//等价于 let person = {firstName: firstName, lastName: lastName}
	console.log(`${person.firstName}----${person.lastName}`); //Michael----Li
	
	// 使用解构给变量赋值
	// 解构会在右侧对象中找到和左侧对象相同的属性名,以该属性值为对应变量赋值;若没找到,则该变量值为undefined
	// 解构赋值可以有默认值
	let personTmp = {firstNameTmp:"Michelle", lastNameTmp:"Li"}
	let {firstNameTmp, lastNameTmp, p1, p2='你好世界'} = personTmp;
	console.log(`${firstNameTmp}---${lastNameTmp}---${p1}---${p2}`);//Michelle---Li---undefined---你好世界

	// 注意这里变量虽然有默认值,但是函数参数是没有默认值的
	function sayNameNew({ firstName = 'Bob', lastName = 'Li'}) {
	    console.log( firstName, lastName );
	}
	sayNameNew({})//Bob Li
</script>

6 字符串 连接

使用 反单引号 ` ;1)可占位 2)可折行

<script type="text/javascript">
	let name = '萧红';
	let borthAndDeath = '1911年6月1日-1942年1月22日';
	let rslt = `${name}${borthAndDeath}),
	中国近现代女作家,“民国四大才女”之一,被誉为“20世纪30年代的文学洛神”。
	乳名荣华,本名张秀环,后由外祖父改名为张廼莹。
	笔名${name}、悄吟、玲玲、田娣等。`
	console.log(rslt)
	// 萧红(1911年6月1日-1942年1月22日),
	// 中国近现代女作家,“民国四大才女”之一,被誉为“20世纪30年代的文学洛神”。
	// 乳名荣华,本名张秀环,后由外祖父改名为张廼莹。
	// 笔名萧红、悄吟、玲玲、田娣等。
</script>

7 面向对象

关键字:class,extends

ES6以前的写法:

<script type="text/javascript">
	/* ES6以前 类的写法 */
	function Student(id, name, age) {
		this.id = id;
		this.name = name;
		this.age = age;
	}
	Student.prototype.showName = function() {
		console.log(this.name)
	}
	Student.prototype.showAge = function() {
		console.log(this.age)
	}
	var student1 = new Student(101, '萧红', 18);
	student1.showName(); //萧红
	student1.showAge(); //18

	/* ES6以前 继承的写法 */
	function VipStudent(id, name, age, level) {
		Student.call(this, id, name, age)
		this.level = level
	}
	VipStudent.prototype = new Student();
	VipStudent.prototype.constructor = VipStudent;
	VipStudent.prototype.showLevel = function() {
		console.log(this.level)
	}
	let student2 = new VipStudent(102, '李清照', 16, 5);
	student2.showName(); //李清照
	student2.showAge(); //16
	student2.showLevel() //5
</script>

ES6的写法:

<script type="text/javascript">
	/* ES6定义类写法 */
	class Student {
		constructor(id, name, age) {
			this.id = id;
			this.name = name;
			this.age = age;
		}
		showName() {
			console.log(this.name)
		}
		showAge() {
			console.log(this.age)
		}
		static helloWorld() {
			console.log("你好世界!你好中国!")
		}
	}

	Student.helloWorld();
	
	let student1 = new Student(101, '萧红', 18);
	student1.showName(); //萧红
	student1.showAge(); //18
	
	/* ES6 继承写法 */
	class VipStudent extends Student {
		constructor(id, name, age, level) {
			super(id, name, age);
			this.level = level;
		}
		showLevel() {
			console.log(this.level)
		}
	}
	let student2 = new VipStudent(102, '李清照', 16, 5);
	student2.showName(); //李清照
	student2.showAge(); //16
	student2.showLevel() //5
</script>

babel

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
		<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
		<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script> -->
		<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
		<script type="text/babel">
			let demo = document.getElementById('demo')
			ReactDOM.render(<h2>你好世界</h2>,demo);
		</script>
	</head>
	<body>
		<div id="demo"></div>
	</body>
</html>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
		<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
		<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
		<script type="text/babel">
			class Item extends React.Component{
				constructor(...args) {
				    super(...args);
				}
				render(){
					return <li>{this.props.initVal}</li>;
				}
			}
			class List extends React.Component{
				constructor(...args) {
				    super(...args);
				}
				render(){
					let items = this.props.arr.map((item)=><Item initVal={item}></Item>)
					return <ul>{items}</ul>;
				}
			}
			window.onload = function(){
				let demo = document.getElementById('demo')
				ReactDOM.render(
				<List arr={['你好中国','你好世界','你好银河','你好宇宙']}></List>,
				demo);
			}
		</script>
	</head>
	<body>
		<div id="demo"></div>
	</body>
</html>

json相关:

<script type="text/javascript">
	// json字符串标准写法:
	// 1)只能用双引号
	// 2)所有key都必须用引号包起来
	let json = {name:'Michael',age:18}
	console.log(typeof JSON.stringify(json), JSON.stringify(json)) 
	// string {"name":"Michael","age":18}
	
	let str = '{"name":"Michael","age":18}';
	json = JSON.parse(str);
	
	// json简写
	let name = "萧红";
	json = {
				name, // key和value相同简写
				key1:'value1',
				show(){ // 方法简写
					console.log("show method run...")
				}
			}
</script>

8 Promise

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
	// Promise--承诺		同步		异步
	let p = new Promise((resolve, reject) => {
		$.ajax({
			url: 'http://127.0.0.1:8848/test2/data.json',
			dataType: 'json',
			success(res) {
				resolve(res);
			},
			error(err) {
				reject(err)
			}
		});
	});

	p.then((res) => {
		console.log('resolve'+JSON.stringify( res))
	}, (err) => {
		console.log('reject'+JSON.stringify( err))
	})
</script>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
	// Promise--承诺		同步		异步
	let p1 = new Promise((resolve, reject) => {
		$.ajax({
			url: 'http://127.0.0.1:8848/test2/data.json',
			dataType: 'json',
			success(res) {
				resolve(res);
			},
			error(err) {
				reject(err)
			}
		});
	});
	
	let p2 = new Promise((resolve, reject) => {
		$.ajax({
			url: 'http://127.0.0.1:8848/test2/data2.json',
			dataType: 'json',
			success(res) {
				resolve(res);
			},
			error(err) {
				reject(err)
			}
		});
	});

	// p1.then((res) => {
	// 	console.log('resolve'+JSON.stringify( res))
	// 	p2.then((res) => {
	// 		console.log('resolve'+JSON.stringify( res))
	// 	}, (err) => {
	// 		console.log('reject'+JSON.stringify( err))
	// 	});
	// }, (err) => {
	// 	console.log('reject'+JSON.stringify( err))
	// });

	Promise.all([p1,p2]).then((res)=>{
		let [rslt1,rslt2] = res; //解构赋值
		console.log("success rslt1:",JSON.stringify(rslt1));
		console.log("success rslt2:",JSON.stringify(rslt2));
	},(err)=>{
		console.log("fail:",JSON.stringify(err));
	})
</script>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
	// Promise--承诺		同步		异步
	function createPromise(url){
		return new Promise((resolve, reject) => {
			$.ajax({
				url, // url: url,
				dataType: 'json',
				success(res) {
					resolve(res);
				},
				error(err) {
					reject(err)
				}
			});
		});
	}
	let p1 = createPromise('http://127.0.0.1:8848/test2/data.json');
	let p2 = createPromise('http://127.0.0.1:8848/test2/data2.json');
	Promise.all([p1,p2]).then((res)=>{
		let [rslt1,rslt2] = res; //解构赋值
		console.log("success rslt1:",JSON.stringify(rslt1));
		console.log("success rslt2:",JSON.stringify(rslt2));
	},(err)=>{
		console.log("fail:",JSON.stringify(err));
	})
</script>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
	// Promise--承诺		同步		异步
	let p1 = $.ajax({url: 'http://127.0.0.1:8848/test2/data.json', dataType: 'json'});
	let p2 = $.ajax({url: 'http://127.0.0.1:8848/test2/data2.json', dataType: 'json'});
	Promise.all([p1,p2]).then((res)=>{
		let [rslt1,rslt2] = res; //解构赋值
		console.log("success rslt1:",JSON.stringify(rslt1));
		console.log("success rslt2:",JSON.stringify(rslt2));
	},(err)=>{
		console.log("fail:",JSON.stringify(err));
	});
	
	// Promise.race([p1,p2]).then((res)=>{
	// 	let [rslt1,rslt2] = res; //解构赋值
	// 	console.log("success rslt1:",JSON.stringify(rslt1));
	// 	console.log("success rslt2:",JSON.stringify(rslt2));
	// },(err)=>{
	// 	console.log("fail:",JSON.stringify(err));
	// });
</script>

9 generator,yield

// 普通函数 --执行期间不能中止
// generator --执行期间 可中止
// yield --放弃执行代码; 功能:(1)传参 (2)返回值

<script type="text/javascript">
	function * printLog(){//*可以贴着function,也可以贴着函数名,也可以单独放置
		console.log("aaa");
		yield;
		console.log("bbb");
	}
	
	let genObj = printLog();//此时不会执行函数
	genObj.next(); // aaa	//执行到第一个yield处,暂停等待next()调用
	genObj.next(); // bbb
	//generator 相当于将一个函数 生产 多个小函数,一个一个的单独执行
</script>

<script type="text/javascript">
function* helloMethod () {
	yield "hello";
	yield "world";
	return "done";
}
let h = helloMethod();

console.log(h);//helloMethod {}
console.log(h.next()); //{value: "hello", done: false}
console.log(h.next()); //{value: "world", done: false}
console.log(h.next()); //{value: "done", done: true}

console.log(h.next()); //{value: undefined, done: true}


let h2 = helloMethod();
for(let obj of h2){
	console.log(obj)
}
// hello
// world
</script>

9.1 传参

<script type="text/javascript">
	function* printLog() {
		console.log("aaa");
		let params = yield;
		console.log(params);
		console.log("bbb");
	}

	let genObj = printLog();
	genObj.next(12); // aaa //第一个next中的实参无法传递给yield;若要在第一步传参可通过函数传值
	genObj.next(13); // 13 //传参打印
					 // bbb
</script>

9.2返回值

<script type="text/javascript">

	function* printLog() {
		console.log("aaa");
		let params = yield 100;
		console.log(params);
		console.log("bbb");;
		// return "结束了";
	}
	
	let genObj = printLog();
	let rslt1 = genObj.next(12); // aaa //第一个next中的实参无法传递给yield;若要在第一步传参可通过函数传值
	console.log(rslt1 );		 // { value: 100, done: false } // rslt1 由yield返回(100返回给了rslt1)
	let rslt2 = genObj.next(13); // 13 //传参打印 //(13传递给了params)
	console.log(rslt2);			 // bbb
								 // { value: undefined, done: true } // 若函数有return语句则:{ value: "结束了", done: true }
</script>
<script type="text/javascript">

</script>
<script type="text/javascript">

</script>

10、模块化

模块化就是把代码进行拆分,方便重复利用。类似java中的导包:要使用一个包,必须先导包。
而JS中没有包的概念,换来的是 模块。
模块功能主要由两个命令构成: export 和 import 。

  • export 命令用于规定模块的对外接口,
  • import 命令用于导入其他模块提供的功能。
// Util.js 
class Util {
	static sum = (a, b) => a + b;
}
//导出该类
export default Util;
//Index.js
//导入Util类
import Util from './Util'
//使用Util中的sum方法
console.log(Util.sum(1, 2));

11 手写promise

//Promise函数
function Promise(executor) {
    let self = this; //保留this。防止后面方法出现this只想不明的问题
    self.status = 'pending'; //promise的默认状态是pending
    self.success = undefined; //保存成功回调传递的值
    self.error = undefined; //保存失败回调传递的值

    self.onSuccessCallbacks = []; //存放成功的回调
    self.onErrorCallbacks = []; //存放失败的回调

    function resolve(success) {
        if (self.status === 'pending') {
            self.status = 'resolved'; //成功函数将其状态修改为resolved
            self.success = success; //将成功的值保存起来
            self.onSuccessCallbacks.forEach(element => {
                element();
            });
        }
    }

    function reject(error) {
        if (self.status === 'pending') {
            self.status = 'rejected'; //失败函数将其函数修改为rejected
            self.error = error; //将失败的值保存起来
            self.onErrorCallbacks.forEach(element => {
                element();
            })
        }
    }
    try {
        executor(resolve, reject);
    } catch (err) {
        reject(err);
    }
}

//then函数
Promise.prototype.then = function (onResolved, onRejected) {
    let self = this;
    let promiseAgain = new Promise((resolve, reject) => {
        if (self.status === 'pending') {
            self.onSuccessCallbacks.push(() => {
                let x = onResolved(self.success); //将resolve函数保留的成功值传递作为参数
                resolvePromise(promiseAgain, x, resolve, reject);
            })
            self.onErrorCallbacks.push(() => {
                let x = onRejected(self.error); //将reject函数保留的失败值传递作为参数
                resolvePromise(promiseAgain, x, resolve, reject);
            })
        }
        if (self.status === 'resolved') {
            let x = onResolved(self.success); //将resolve函数保留的成功值传递作为参数
            resolvePromise(promiseAgain, x, resolve, reject);
        }
        if (self.status === 'rejected') {
            let x = onRejected(self.error); //将reject函数保留的失败值传递作为参数
            resolvePromise(promiseAgain, x, resolve, reject);
        }
    })
    return promiseAgain;
}
//resolvePromise函数
function resolvePromise(promiseAgain, x, resolve, reject) {
    if (promiseAgain === x) {
        return reject(new TypeError("循环调用"));
    }
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, (y) => {
                    resolvePromise(promiseAgain, y, resolve, reject);
                }, (e) => {
                    reject(e);
                })
            } else {
                resolve(x);
            }
        } catch (error) {
            reject(error);
        }
    } else {
        resolve(x);
    }
}

module.exports = Promise;

12 ES7新特性

12.1.async/await 基础

ES8中把async和await变得更加方便,它其实就是Generator的语法糖。
在代码中使用 async / await 有两个部分。

12.2.async 关键字

/**
*将 async 关键字加到函数申明中,可以告诉它们返回的是 promise,而不是直接返回值。
*此外,它避免了同步函数为支持使用 await 带来的任何潜在开销。
*在函数声明为 async 时,JavaScript引擎会添加必要的处理,以优化你的程序。爽!
*/

function hello1() { return "Hello 1" };
console.log(hello1()); // 返回值:Hello 1

async function hello2() { return "Hello 2" };
console.log(hello2()); // 返回值:Promise {: 'Hello 2'}
let hello3 = async function() { return "Hello 3" };
console.log(hello3()); // 返回值:Promise {: 'Hello 3'}
let hello4 = async () => { return "Hello 4" };
console.log(hello4()); // 返回值:Promise {: 'Hello 4'}

hello4().then((value) => console.log(value)) // Hello 4
hello4().then(console.log) // 返回值:Hello 4

12.3.await 关键字

// 当 await 关键字与异步函数一起使用时,它的真正优势就变得明显了 —— 事实上, await 只在异步函数里面才起作用。它可以放在任何异步的,基于 promise 的函数之前。它会暂停代码在该行上,直到 promise 完成,然后返回结果值。在暂停的同时,其他正在等待执行的代码就有机会执行了。

// 您可以在调用任何返回Promise的函数时使用 await,包括Web API函数。

let hello5 = async () => await hello4(); // 和下面的写法效果是相同的
// let hello5 = async () => {
// 	let greeting = await hello4();
// 	return greeting;
// };
hello5().then(alert); // Hello 4

你可能感兴趣的:(大前端,js,es6,es6新特性)