本文主要记录常见的es6基础知识
目录
- es6是什么
- let和const
- 字符串模板
- 解构赋值
- set和map
- 箭头函数
- class类和extends
- promise
- async/await
- export和import
1. es6是什么
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015(简称ES2015)。虽然浏览器在不断更新,但并不是所有用户的电脑浏览器都支持ES6,所以在使用的过程中建议还是转成es5,保证代码的可执行性。至于转换的方式大家可以用Babel或者Traceur转码器。
ECMAScript的几次大版本:es3-->es5-->es6(es7相对于es6变动不大)
2. let 和 const
let
在es5里,定义变量用var,即相当于在全局声明一个变量,由于js没有块级作用域,会导致在使用变量的过程中存在变量提升的概念。
console.log(a); // undefined
var a = 10;
console.log(a) //10
function fn () {
console.log(a); // undefined
var a = 'aaa';
console.log(a); // aaa
}
fn();
在C,Java等语言中,变量必须先申明在使用,否则会报错,但是在js中由于变量提升的原因不会报错。
关于var定义变量还有一个经典的实列:
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i)
},1000)
}
//5 5 5 5 5
结果并不是预期的 0,1,2,3,4。这是因为由于js内部的运行机制,遇到定时器任务以及异步请求时其回调函数会先放到任务队中,优先执行主进程任务,待主进程执行完毕再执行任务队列中的代码。es5中用闭包来解决这个问题,使用了立即执行函数:
for(var i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log(i)
},1000)
})(i)
}
// 0 1 2 3 4
当然,也可以使用es6中的let,简洁方便,直接将var改为let即可。
下面的列题方面理解,点击标签弹出对应索引:
window.onload = function(){
var aInput = document.getElementsByTagName("input");
// 传统解决办法
for(var i=0;i<aInput.length;i++){
(function(i){
// 函数闭包自执行来解决i索引的问题
aInput[i].onclick = function(){
alert(i);
};
})(i);
}
// let变量的出现相当于给你加了一个封闭空间来极度简化了i值索引的问题
// let大家可以看成是匿名函数立即调用(IIFE)
for(let i=0;i<aInput.length;i++){
aInput[i].onclick = function(){
alert(i);
};
}
};
const
es6中使用const来定义常量,顾名思义即不变的量,不可修改(这里指的是普通常量,比如字符串,数字,boolean),当对一个对象常量修改其属性则是允许的。
const a = 10;
a = 20
//报错: Uncaught SyntaxError: Identifier 'a' has already been declared
const obj = {
a: 1,
name: 'hello'
}
obj.a = 2
obj.name = 'world'
console.log(obj)
//{a: 2,name: 'world'}
关于let和const的使用会存在 暂死性区域
3. 字符串模板
在es5中使用字符串拼接常这样来做:
let name = 'wjb'
console.log('i am ' + name)
当遇到字符串比较长,涉及到的变量比较多的时候就显得不那么好看和使用了,es6新增字符串拼接方法,使用``包裹内容,${}描述变量:
let name = 'wjb'
console.log(`i am ${name}`)
4.解构赋值
// 以前我们给变量赋值,只能直接指定值
var a = 1;
var b = 2;
var c = 3;
console.log(a,b,c); // 1 2 3
// 现在用解构赋值的写法就变得简单了,只要模式匹配上了就行了,如下
// 注意数组是有顺序的
var [a,b,c] = [11,22,33];
console.log(a,b,c); // 11 22 33
var [b,a,c] = [11,22,33];
console.log(a,b,c); // 22 11 33
// 当然解构赋值还有嵌套比较复杂的写法,如下
let [foo,[[bar],[baz]]] = [111,[[222],[333]]];
console.log(foo,bar,baz); // 111 222 333
let [head,...foot] = [1,2,3,4];
console.log(head,foot); // 1 [2,3,4]
// 如果解构不成功,变量的值就等于undefined,如下
var [bar3,foo3] = [1000];
console.log(bar3,foo3); // 1000 undefined
// 另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功
let [x,y] = [10000,20000,30000];
console.log(x,y); // 10000 20000
// 默认值可以引用解构赋值的其他变量,但该变量必须已经声明
let [a=1,b=a] = [2,3];
console.log(a,b); // 2 3
// 对象的解构也可以指定默认值
var {x,y=5} = {x:1};
console.log(x,y); // 1 5
//对象的解构赋值解构不仅可以用于数组,还可以用于对象(json)
//对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;
//而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
var {a,b} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana
var {b,a} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana
// 如果变量名与属性名不一致,必须写成下面这样
let obj = {first:'hello',last:'world'};
// first ---> f,那么此时f就是first,而不是undefined了,有点类似别名的概念
let {first:f,last} = obj;
console.log(f,last); // hello world
//1.也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。 真正被赋值的是后者,而不是前者
//2.v是匹配的模式,n才是变量。真正被赋值的是变量n,而不是模式v。
//注意,采用这种写法时,变量的声明和赋值是一体的
// v ---> n,那么此时n就是vue,而不是undefined了
var {v:n} = {v:'vue',r:'react'};
console.log(n); // vue
console.log(v); // Uncaught ReferenceError: v is not defined
console.log(r); // Uncaught ReferenceError: r is not defined
5.set和map
有待完善
es6新增的两种数据结构
filter: 过滤掉不符合条件的元素
forEach: 遍历
reduce: 汇总
6.箭头函数
改变了this指向,使this指向当前调用的对象,简化了函数书写的方式:
let a = {
name: 'wjb',
fn: function(){
return this.name
}
}
a.fn()
// wjb
let a = {
name: 'wjb',
fn: () =>{
return this.name
}
}
a.fn()
// undefined
常用形式:
//箭头函数写法 function(){} 变为 ()=>{}
window.onload = () => {
var oBox = document.getElementById("box");
oBox.onclick = () => {
oBox.style.backgroundColor = '#ff0000';
};
};
//注意this指向会有问题
var json = {
a:1,
b:2,
showName:() => {
return this.a;
}
};
// 因为使用了箭头函数this指向了object window 所以result:undefined
console.log(json.showName());
7. class类和extends
es6新增了类的概念,即像C,Java一样声明类,在class类中定义方法和属性。
//传统面向对象写法
function Person(name,age){ // 类、构造函数
this.name = name;
this.age = age;
}
Person.prototype.showName = function(){
return this.name;
};
Person.prototype.showAge = function(){
return this.age;
};
//ES6面向对象写法
class Person{
// 构造器(在构造器中也可以设置默认参数)
constructor(name='default',age=10){
this.name = name;
this.age = age;
}
showName(){
return this.name;
}
showAge(){
return this.age;
}
}
//面向对象class给默认值
class Person{
// 构造器
constructor(name='default',age=0){
this.name = name;
this.age = age;
}
showName(){
return this.name;
}
showAge(){
return this.age;
}
}
js实现继承的方式有很多种,本文主要讲解原型继承以及类继承
原型继承: 通过指定对象的prototype属性,来继承目标构造函数的方法和属性。
类继承 : 通过es6提供的extends方法来实现类的继承。
如下所示:
//传统写法原型继承
function Person(name,age){ // 类、构造函数
this.name = name;
this.age = age;
}
Person.prototype.showName = function(){
return this.name;
};
Person.prototype.showAge = function(){
return this.age;
};
// 工人类
function Worker(name,age){
// 属性继承过来
Person.apply(this,arguments);
}
// 原型继承
Worker.prototype = new Person();
var p1 = new Person('allen',28);
var w1 = new Person('worker',1000);
console.log(w1.showName()); // 确实继承过来了 result:worker
//ES6中面向对象实现类继承
class Person{
// 构造器
constructor(name,age){
this.name = name;
this.age = age;
}
showName(){
return this.name;
}
showAge(){
return this.age;
}
}
class Worker extends Person{
constructor(name,age,job='拖地的'){
// 继承超父类的属性
super(name,age);
this.job = job;
}
showJob(){
return this.job;
}
}
var p1 = new Person('aaa',18);
var w1 = new Person('www',36);
var w2 = new Worker('wwwwwwww',90);
console.log(w1.showName()); // www
console.log(w2.showJob()); // 默认给的值 ‘拖地的’
8. promise
promise主要是用来解决异步操作,它存在三种状态: peding(等待),fulfilled(已完成),rejected(已拒绝),关于promise的用法,需要注意:
- 一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
- promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
- then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
//基本用法
let p = new Promise((resolve, reject) => {
$.ajax({
url: '1.txt',
dataType: 'json',
success(json){
resolve(json);
},
error(err){
reject(err);
}
})
});
p.then(json=>{
console.log('成功',json);
}, err=>{
console.log('获取失败');
})
promise.all
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promse.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})
promise.race
将多个promise实列打包成一个新的实列发送,并返回获得结果最快的回调。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})
更多的promise访问 www.jianshu.com/p/7e60fc1be…
9.async/await
在进行异步请求时,往往会存在一个请求里嵌套着多个请求,后续的请求需要用到之前的请求回调,当请求嵌套过多时,则会出现回调地狱,不利于代码的开发和维护,因此需要使用async/await来处理嵌套异步请求。
let value
getInfo(){
let promise = new Promise(function(resolve,reject){
resolve('成功了');
});
promise.then(function(res){
return res
}).catch(function(err){
return err
});
}
ajax(){
getInfo().then((res)=>{
value = res //2-后执行
})
console.log(res) //1-先执行
}
ajax() // undefined
使用async/await后
let value
getInfo(){
let promise = new Promise(function(resolve,reject){
resolve('成功了');
});
promise.then(function(res){
return res
}).catch(function(err){
return err
});
}
async ajax(){
await getInfo().then((res)=>{
value = res //1-回调限制性
})
console.log(res) //2-后执行
}
ajax() // res
10. export和import
export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口
import用于在一个模块中加载另一个含有export接口的模块。
也就是说使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块(文件)。
export和export default的区别
- export与export default均可用于导出常量、函数、文件、模块等
- 你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用
- 在一个文件或模块中,export、import可以有多个,export default仅有一个
- 通过export方式导出,在导入时要加{ },export default则不需要
let name1="李四";
let name2="张三";
export { name1 ,name2 } //导出
//导入
import { name1 , name2 } from "/.a.js" //路径根据实际情况填写
let name="李四";
export default name
import name from "/.a.js" 这里name不需要大括号