关于 ES6 在《ECMAScript6 入门》中已经有很详细的教程了,这里主要再举些例子
ES6
,全称ECMAScript 6.0
,是 JavaScript 的第六个版本标准
一、var、let 和 const 的区别
1、ES6
用let
和const
声明变量和常量,ES5
用var
声明。
2、let
和const
作用域只局限于当前代码块,是块级作用域;var
的作用域是方法作用域,整个方法内的定义变量后的代码都可以使用。
3、使用let
和const
声明变量作用域不会被提升,在变量未声明前直接使用会报错;var
具有变量提升,在变量未赋值时,变量 undefined。
{
var str;
console.log(str); // undefined
str = "张三"; // var 这样是可行的
}
4、在相同作用域下let
和const
不能重复声明同一个变量,var
可以重复声明同一个变量。
5、const
定义的对象,属性可以改变
因为对象是引用类型的,const
仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。
6、for循环
体现let
的父子作用域
例子:点击按钮
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.querySelectorAll("button");
for (var i=0;i<btns.length;i++) {
btns[i].onclick = function () {
alert("点击了第"+ i +"个按钮");
}
}
//上面这样点击每个按钮都是 i=3
//改为闭包的方式可以解决
var btns = document.querySelectorAll("button");
for (var i=0;i<btns.length;i++) {
(function (i) {
btns[i].onclick = function () {
alert("点击了第"+ i +"个按钮");
}
})(i);
}
//ES6 用 let,使代码更简洁
let btns = document.querySelectorAll("button");
for (let i=0;i<btns.length;i++) {
btns[i].onclick = function () {
alert("点击了第"+ i +"个按钮");
}
}
</script>
</body>
二、箭头函数
1.基本格式:(参数)=>{ }
// 使用箭头函数对数组排序
'use strict'
var arr = [10, 20, 1, 2];
arr.sort((x, y) => {
return x-y;
});
console.log(arr); // [1, 2, 10, 20]
2.简写:
(1)有且仅有一个参数,()可以不写
(2)如果有且仅有一个语句并且是return
,{}可以不写
3.作用:修复 this 指向
let json={
a: 12,
fn: function (){
alert(this.a);
}
}
json.fn(); //12
let json={
a: 12,
fn: function (){
alert(this.a);
}
}
let x = new Date();
x.fn = json.fn;
x.fn(); //undefined
用箭头函数改写:
let json={
a: 12,
fn: ()=>{
alert(this.a); //this 指向当前环境,此处是 window
}
}
json.fn(); //结果是 undefined,因为 window 对象中没有 a
使用构造函数:
class Json{
constructor(){
this.a = 12;
this.fn = ()=>{
alert(this.a);
}
}
}
let json = new Json();
json.fn(); //12,因为当前环境是 Json 对象,this 指向 Json 对象
let x = new Date();
x.fn = json.fn;
x.fn(); //12
三、函数的参数扩展(…)
(1)参数展开
function show(a, b, ...c){
console.log(a,b,c); //a=1,b=3,c=[4,2,6,8]
}
show(1,3,4,2,6,8);
let arr = [1,5,8];
function show(a,b,c){
alert(a+b+c);
}
show(...arr); // 14
(2)数组展开 合并数组
let arr1 = [1,5,8];
let arr2 = [4,5,6];
let arr = [...arr1, ...arr2];
alert(arr); // [1,5,8,4,5,6]
(3)JSON展开
let json = {a: 1, b: 5, c: 8};
let json2 = {
...json,
d: 7
};
四、解构赋值
- 数组的解构赋值
[a, b] = [10, 20]; // a=10,b=20
[a, b, ...rest] = [1, 2, 3, 4, 5]; // a=1,b=2,rest=[3,4,5]
// 嵌套数组解构
[a, [[b], c], d] = [1, [[2], 3], 4];
// 不需要匹配的位置可以置空
[,,c] = [1, 2, 3]; // c=3
1、默认值
# 为了防止从数组中取出一个值为 undefined 的对象,可以在表达式左边的数组中为任意对象预设默认值
[a=5, b=7] = [1]; // a=1,b=7
2、交换变量
# 在一个解构表达式中可以交换两个变量的值
[a, b] = [b, a];
3、解析一个从函数返回的数组
# 可以使用解构在一行内完成解析
function f() {
return [1, 2];
}
var a, b;
[a, b] = f(); // a=1,b=2
- 对象的解构赋值
// 变量必须与属性同名
let { a, b } = { a: 1, c: 2 }; // a=1,b=undefined
1、无声明赋值
# 如果在解构之前就已经定义了对象
let obj;
{obj}={obj:'James'}; //报错
# 大括号{}位于行首,JS 引擎就会认为 { obj } 是一个代码块,所以等号就出问题了。
# 解决方式是在包裹一层括号()
let obj;
({obj}={obj:'James'});
# 括号的出现,让整个解构赋值的结构被看做一个代码块,而内部的 { obj } 模式则可以正常匹配到。
2、对任意深度的嵌套对象进行解构
let obj = { arr: [1, {a: 2}] };
let { arr: [x, {y}] } = obj;
3、自定义属性名称
var { name, id: ID } = { name: 'jack', id: 1 }; // ID=1
4、for of
迭代和解构
var people = [{
name: 'Mike Smith',
family: {
mother: 'Jane Smith',
father: 'Harry Smith',
},
age: 35
},
{
name: 'Tom Jones',
family: {
mother: 'Norah Jones',
father: 'Richard Jones',
},
age: 25
}];
for (var {name: n, family: {father: f}} of people) {
console.log('Name: ' + n + ', Father: ' + f);
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
5、解构对象时会查找原型链(如果属性不在对象自身,将从原型链中查找)
// 声明对象 和 自身 self 属性
var obj = {self: '123'};
// 在原型链中定义一个属性 prot
obj.__proto__.prot = '456';
const {self, prot} = obj;
// self "123"
// prot "456"(访问到了原型链)
- 字符串解构
const [a,b,c,d,e] = 'hello';
五、Object.assign 的理解
1、作用:Object.assign
可以实现对象的合并。
2、语法:Object.assign(target, ...sources)
3、解析:
(1)Object.assign
会将源对象source
里面的可枚举属性复制到目标对象target
,如果和target
有同名属性值,后面的属性值会覆盖前面的属性值。
(2)后面的source
会覆盖前面的source
的同名属性。
(3)如果源对象source
的属性值为基本类型,通过Object.assign
得到的新对象为深拷贝;如果源对象source
的属性值为引用类型如对象,那对于这个对象而言是浅拷贝。
即:第一级属性深拷贝,从第二级属性开始就是浅拷贝。
let obj = { name: "Tom", age: 11, job: ["web"] };
let copyObj = Object.assign({}, obj);
// 情况一:改变 copyObj 的整个 job 数组,obj 的 job 不变
copyObj.job = ["IT"];
console.log(obj); // { name: "Tom", age: 11, job: ["web"] }
console.log(copyObj); // { name: "Tom", age: 11, job: ["IT"] }
// 情况二:通过数组下标改变 copyObj 中 job 数组中的某个值,obj 的 job 跟着改变
copyObj.job[0] = "IT";
console.log(obj); // { name: "Tom", age: 11, job: ["IT"] }
console.log(copyObj); // { name: "Tom", age: 11, job: ["IT"] }
六、原生对象扩展
1.Array 扩展:map、reduce、filter、forEach
- map 映射:一一对应
let arr = [68,53,12,98,65];
let arr2 = arr.map(function (item){
if(item>=60){
return '及格';
}else{
return '不及格';
}
});
console.log(arr);
console.log(arr2);
使用箭头函数:
let arr = [68,53,12,98,65];
let arr2 = arr.map(item=>item>=60?'及格':'不及格');
map
创建的对象常用方法:set
、get
、delete
、has
、clear
、keys
、values
、entries
。
- reduce n=>1
let arr = [68,53,12,98,65];
let result = arr.reduce(function (tmp, item, index){
//alert(index+':'+tmp+','+item);
return tmp+item; //总和
});
alert(result);
// 求平均值
let arr = [68,53,12,98,65];
let result = arr.reduce(function (tmp, item, index){
if(index==arr.length-1){
return (tmp+item)/arr.length; //平均值
}else{
return tmp+item; //总和
}
});
alert(result);
- filter 过滤
// 获取一个数组中的偶数
let arr = [68,53,12,98,65,83,16];
let arr2 = arr.filter(item=>item%2==0);
- forEach 遍历
let arr = [68,53,12,98,65,83,16];
arr.forEach((item,index)=>{
//alert('第'+index+'个:'+item);
//使用模板字符串
alert(`第${index}个:${item}`);
});
2.模板字符串
3.JSON
七、异步操作——Ajax,Promise
ajax('http://aaa.com/a/1.txt',function(){});
异步操作:同时进行多个操作,用户体验好,代码混乱
同步操作:一次只能进行一个操作,用户体验差,代码清晰
当许多功能需要连续调用,Ajax 代码就会一层一层嵌套,产生回调地狱
😱
回调地狱:函数作为参数层层嵌套
回调函数:一个函数作为参数需要依赖另一个函数执行调用
例子:
ajax('url1',function (data1){
ajax('url2',function (data2){
ajax('url3',function (data3){
//数据处理
},function(){alert("error3!");})
},function(){alert("error2!");})
},function(){alert("error1!");})
使用 ES6 提供的 Promise 对象解决异步编程
例子:获取文件1.txt
中的一个数组,运行环境类似这样localhost/demo/1.html
// 创建一个 Promise 对象
let p = new Promise(function (resolve,reject){
$.ajax({
url:'data/1.txt',
dataType:'json',
success(arr){
resolve(arr);
},
error(res){
reject(res);
}
})
});
// then 方法指定 resolve 状态和 reject 状态的回调函数
// p.then(function(){},function(){});
p.then(function(arr){
console.log(arr+'success');
},function(res){
console.log(res+'error');
});
JQuery 中的$.ajax
会返回一个结果,也是一个 Promise
// 验证 $.ajax 的结果
let res = $.ajax({
url:'data/1.txt',
dataType:'json'
});
console.log(res);
因此可以调用 then 方法:
$.ajax({
url:'data/1.txt',
dataType:'json'
}).then(arr=>{
alert(arr);
},res=>{
alert('失败');
});
使用Promise.all()
方法同时处理多个请求
Promise.all([
$.ajax({url:'data/1.txt',dataType:'json'}),
$.ajax({url:'data/2.txt',dataType:'json'}),
$.ajax({url:'data/3.txt',dataType:'json'}),
]).then(arr=>{
console.log(arr); //arr 是一个数组
let [data1,data2,data3] = arr; //解构赋值,也可直接在参数里解构
console.log(data2);
},res=>{
alert('失败');
});
注意:使用Promise.all()
需要几个请求全部成功才行
Promise.race()
只要有一个实例状态率先改变状态,Promise 实例就跟着改变
八、async/await 函数
- generator/yield 已经被 async/await 取代。
async
放在函数前面,表示异步执行,该函数的执行不阻塞后面代码的执行。await
表示等待 await 后面的函数执行完毕,并且有了返回结果之后,才能继续执行下面的代码。
async function show(){
let a=12;
let b=5;
try {
let data = await $.ajax({url:'data/1.txt',dataType:'json'});
alert(a+b+data[0]);
} catch(e) {
alert("读取失败");
}
}
show();
九、面向对象、模块化
1.类的构造函数
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
showName(){
alert(this.name);
}
showAge(){
alert(this.age);
}
}
let p = new Person('xl',18);
p.showName();
p.showAge();
2.类的继承
class Worker extends Person{
constructor(name,age,job){
super(name,age);
this.job = job;
}
showJob(){
alert(this.job);
}
}
let p = new Person('xl',18,'Java');
p.showName();
p.showAge();
p.showJob();
3.模块化
(1)导入
(2)导出
(3)webpack 编译:因为浏览器不支持 ES6 模块
//modle.js - 定义模块
export let a = 12;
//index.js - 使用模块
import * as modle from './modle';
alert(modle.a);
//index.html
<script src="index.js"></script>
//webpack.config.js - 编译配置
const pathlib = require('path');
module.exports={
mode:"production",
entry:"./js/index.js",
output:{
path: pathlib.resolve(__dirname,'build'),
filename:'bundle.js'
}
}
使用 cmd 命令行窗口运行命令webpack
,就会在目录下生成 build 文件夹,里面有文件bundle.js
然后 index.html 中引入bundle.js
(4)默认成员
export default 9;
import modle from './modle';
console.log(modle); // 9
(5)几种引入的写法
import * as mod from 'xxx'; //引入所有成员
import mod from 'xxx'; //引入 default 成员
import {a,b as c} from 'xxx';
import 'xxx';
let p = import("./mod"); //异步引入