学习文档:http://es6.ruanyifeng.com/
var的问题:可以重复声明,无法限制修改,没有块级作用域,只能用函数包裹限制作用域。
let和const:
1、不能重复声明。
2、都是有块级作用域,在{ }块内声明的,块外无效。
3、let是变量,可以修改。
4、const是常量,不能修改。
写法:
1、只有一个参数,( ) 可以省略。
2、只有一个return,{ } 可以省略。
function name() { } //普通函数
() => { } //箭头函数,去掉function,加上 =>
作用:
1、简写函数。
2、把父级的this传进里面,里面使用this就是在调用父级的this。
注意:ES6默认采用严格模式,因此this不会自动指向window对象,而箭头函数本身并没有this,所以this是undefined。如果还想用this,就不要用使用箭头函数。
收集不定数目的参数,必须放在最后一个参数位置。
名字可随便起,但必须要有 … 表示这是剩余参数。
这个rest参数提供的剩余参数是个数组。
function show(a, b, ...args){
console.log(args); //输出 3, 4, 5
}
show(1, 2, 3, 4, 5);
以前的不定参arguments 不要再用了,因为只能提供类似数组的对象。
而剩余参数能显式的表明你想要获取参数,并提供一个真正的数组。
一种简写,比如在数组前面加三个点,效果和直接写数组内容一样。
let arr = [1, 2, 3];
console.log(...arr); //输出 1,2,3
let arr2 = [...arr]; //...arr等价于1,2,3,所以这里数组arr2和arr相等
let arr3 = [...arr, 4, 5, ...arr2]; //可以随便组合写
对象也能展开。
还可以转换字符串为数组。
[...'hello'] // [ "h", "e", "l", "l", "o" ]
function show(a, b = 5){ //直接在此设置默认参数
console.log(a, b);
}
show(1); //输出 1,5
show(1, 2); //输出 1,2
let [a, b, c] = [1, 2, 3];
console.log(a, b, c); //输出 1, 2, 3
let {x, y, z} = {x: 1, y: 2, z: 3};
console.log(x, y, z); //输出 1, 2, 3
1、左右结构必须一样,比如左右都是数组,参数和赋值的个数能对应。
2、右边必须是个合法的东西。
3、声明和赋值不能分开,必须写在一句话中。
解构的粒度可以随意调控,如下
let [json, arr, num, str] = [{a: 1, b: 2}, [1, 2, 3], 8, 'abc'];
console.log(json, arr, num ,str); //输出 {a: 1, b: 2}, [1, 2, 3], 8, "abc"
let [{a, b}, [n1, n2, n3], num, str] = [{a: 1, b: 2}, [1, 2, 3], 8, 'abc'];
console.log(a, b, n1, n2, n3, num, str); //输出 1, 2, 1, 2, 3, 8, "abc"
新增了一些数组的方法。
1、 map( ) 映射
能遍历数组中的每一个元素,并对每一个元素做操作。
可以在map函数的参数中定义一个函数,写要对数组中的每一个元素做什么操作,最后可以return返回结果。
let arr = [1, 2 , 3]
let double = arr.map(function(item){
return item*2
}
let double2 = arr.map(item => item*2) //上面函数的简写
console.log(double) //输出 [2, 4, 6]
let score = [22, 70, 61, 45]
let result = score.map(item => item >= 60? '及格' : '不及格')
console.log(result) //输出 ['不及格', '及格', '及格', '不及格']
2、reduce( ) 汇总
使每一个元素依次顺序相邻的运算求出一个值。
第一参数是保存临时的运算结果,第二参数是当前元素,第三参数是运算了第几次(从1开始算)。
比如下面求和。
let arr = [1, 2, 3, 4]
let sum = arr.reduce((tmp, item, index) => {
console.log(tmp, item, index) //依次输出(1,2,1), (3,3,2), (6,4,3)
return tmp + item
})
console.log(sum) //输出 10
第一次运算时没有临时结果,tmp的值是数组中第一个元素1,和第二个元素2相加等于3。
第二次运算时tmp就保存上一次运算结果3,和数组中第三个元素3相加,如此下去。
下面是求平均数例子。
let arr = [1, 2, 3, 4]
let sum = arr.reduce((tmp, item, index) => {
if(index != arr.length - 1){ //如果不是最后一次就相加,否则除以数组长度求平均值。
return tmp + item
}else{
return (tmp + item)/arr.length
}
})
console.log(sum) //输出 2.5
3、filter( ) 过滤器
过滤数组中的元素。
可以在filter函数的参数中定义一个函数,写对数组的筛选条件,最后可以return返回过滤后结果。
let arr = [12, 4, 8, 9]
let result = arr.filter(item => (item % 3 === 0) ? true : false) //ture表示保留
//let result = arr.filter(item => item % 3 === 0) 这样简写也行
console.log(result) //输出 [ 12, 9 ]
let arr = [
{ name: '张三', age: 10 },
{ name: '李四', age: 20 },
]
let result = arr.filter(json => json.age >= 20)
console.log(result) //输出 [ { name: '李四', age: 20 } ]
4、forEach( ) 循环迭代
迭代循环数组中的元素。
可以在forEach函数的参数中定义一个函数,写对数组中每个元素的遍历操作。
let arr = [12, 2, 7]
let result = arr.forEach((item, index) => console.log(item, index)) //依次输出(12, 0), (2, 1), (7, 2)
5、set( )、Array.from( ) 去重
set( ) 可以接收一个数组或者是类数组对象,自动去除其中的重复元素。
Array.from( ) 可以把类数组对象、可迭代对象转化为数组。
因为Set去重后返回的是一个对象,所以要配合Array.from转换为数组。
let arr = [1, 1, '1', '1', null, null , undefined, undefined, NaN, NaN]
console.log(Array.from(new Set(arr))) //输出 [ 1, '1', null, undefined, NaN ]
6、keys( )、values( )、entries( )、for…of、for…in
keys( )遍历下标,values( )遍历值,entries( )同时遍历下标和值。
for … in 遍历数组的下标 key,可以遍历对象,但不能与上面那三个方法一起使用。
for … of 遍历数组的值 value,不能遍历对象,常用于与上面三个方法一起使用。
let arr = ['a', 'b', 'c']
for (let i in arr) {
console.log(i) //输出 0 1 2
}
for (let i of arr) {
console.log(i) //输出 a b c
}
for (let i of arr.keys()) {
console.log(i) //输出 0 1 2
}
for (let i of arr.values()) {
console.log(i) //输出 a b c
}
for (let [key, value] of arr.entries()) {
console.log(key + value) //输出 0a 1b 2c
}
//使用for in遍历对象
let json = { a: 12, b: 5, c: 7 }
for (let i in json) {
console.log(i) //输出 a b c
}
检测字符串开头或结尾是否包含指定字符串,返回布尔值。
let str = 'abcd'
console.log(str.startsWith('ab')) //输出 true
console.log(str.endsWith('d')) //输出 true
console.log(str.endsWith('b')) //输出 false
使用反引号 `,${变量},可以拆行。
let a = 1
let str = `abc${a}`
console.log(str) //输出 abc1
和Java很像,有class,extend,super,构造函数等之类的。
class User {
constructor(name, pass) {
this.name = name
this.pass = pass
}
showName() {
console.log(this.name)
}
showPass() {
console.log(this.pass)
}
}
let u1 = new User('abc', 111) //创建User的实例
u1.showName() //输出 "abc"
u1.showPass() //输出 111
//继承
class VipUser extends User {
constructor(name, pass, level) {
super(name, pass) //必须写,表示继承父类
this.level = level
}
showLevel(){ //子类新增的方法
console.log(this.level)
}
showPass(){ //子类重写父类的方法(覆盖)
console.log(123)
}
showPass(level){ //测试能不能重载
console.log('这是子类重写父类的方法'+ level)
}
}
let v1 = new VipUser('red', 123, 1) //创建VipUser的实例
v1.showLevel() //输出 1
v1.showName() //调用继承父类的方法,输出 "red"
v1.showPass(2) //输出 这是子类重写父类的方法2
v1.showPass() //输出 这是子类重写父类的方法undefined,证明不能重载。
和JAVA的区别之一是不能重载函数,即调用相同函数名的函数时,根据你传递参数不同,调用相应的函数。
1、简写对象的字面量
键值一样时可以简写,里面的函数可以简写, 去掉 :function
var a = 1, b = 2
let obj = {a: a, b: b, show:function(){}}
let obj = {a, b, show(){}} //简写
2、属性名的定义
obj.foo = true; // 常见
obj['a' + 'bc'] = 123; //ES6
var obj = { // 常见
foo: true,
abc: 123
};
var obj = { // ES6
[foo]: true,
['a' + 'bc']: 123
};
例子:
var obj = {
id0: '00',
id1: '11',
name0: '张三',
name1: '李四'
};
// 把扁平化的对象结构化为两组对象,每组对象包含两组键值对
var arr = [];
for (let i = 0; i < 2; i ++) {
const item = {
id: obj[`id${i}`],
name: obj[`name${i}`]
};
arr.push(item);
}
console.log(arr); //输出 [{id:'00',name:'张三'},{id:'11',name:'李四'}]
3、Object.is( ) 比较
相等运算符(==
)和严格相等运算符(===
)都有缺点。前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。
JS缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。该函数就是来弥补这一缺点。
console.log(Object.is('foo', 'foo')) // true
console.log(Object.is(+0, -0)) // false
console.log(Object.is(NaN, NaN)) // true
console.log(Object.is(null, null)) //true
console.log(Object.is(undefined, undefined)) //true
console.log(Object.is({ }, { })) // false,特殊例子
console.log(Object.is([ ], [ ])) // false,特殊例子
只有空对象或空数组间比较是false,不是同值相等的情况。
4、Object.assign( ) 合并
如果有同名属性,后面会覆盖前面。
const obj1 = { a: 1, b: 1 };
const obj2 = { b: 2, c: 2 };
const obj3 = { c: 3 };
const obj = Object.assign(obj1, obj2, obj3);
console.log(obj) //输出 { a: 1, b: 2, c: 3 }
5、Object.keys( ),Object.values( ),Object.entries( ) 对象转为数组
我都放在这里一起记。
这三个函数的分别是:
ES5方法,把对象的键返回成一个数组。
ES8方法,把对象的值返回成一个数组。
ES8方法,把对象自身返回成一个数组。
const obj = { a: 'str', b: 1 }
Object.keys(obj) // ["a", "b"]
Object.values(obj) // ["str", 1]
Object.entries(obj) // [ ["a", "str"], ["b", 1] ]
新增的两个方法的作用是方便把对象转成数组后,对值或者键值对遍历,例子如下:
let obj = {a: 1, b: 2, c: 3};
Object.keys(obj).forEach((key) => { //以前只有key方法
console.log(key + ": " + obj[key]); //输出 a: 1, b: 2, c: 3
})
Object.entries(obj).forEach(([key, value]) => { //现在entries方法
console.log(key + ": " + value); //输出 a: 1, b: 2, c: 3
})
7、Object.fromEntries( ) 数组转为对象
ES8方法,把键值对数组转为对象。
Object.fromEntries([ ["a", "str"], ["b", 1] ]) // { a: "str", b: 1}
引入:
let { stat, exists, readFile } = require('fs') //以前
import { stat, exists, readFile } from 'fs' //ES6
暴露:
module.exports = { stat, exists, readFile } //以前
export default { stat, exists, readFile } //ES6
Promise用来解决地狱回调问题,能把异步操作代码写成同步样子。
Promise对象是个构造函数,通过new创建一个Promise实例,在这实例中有个then方法,看下面例子使用。
// 传统写法
step1( value1 => {
step2( value1, value2 => {
step3(value2, value3 => {
step4(value3, value4 => {
// ...
});
});
});
});
// Promise 的写法
new Promise(step1)
.then(step2)
.then(step3)
.then(step4);
简单来说就是这样:
function f1(resolve, reject) {
// 异步代码...
}
var p1 = new Promise(f1);
p1.then(f2).then(f3).then(f4)
// f1的异步操作执行完成,就会执行f2,依次下去
每当new一个Promise实例时,就会立刻执行实例中的异步操作,所以要用函数包裹new的过程,来控制什么时候执行。
Promise实例中还自带两个函数,resolve为成功回调函数,reject为失败回调函数。
读取文件例子如下:
const fs = require('fs') //引入读取文件模块
getFile = path => { //把创建Promise过程放在函数里,按需执行
return new Promise( (resolve, reject) => {
fs.readFile(path, 'utf-8', (err, data) => {
if(err) return reject(err) //如果有err就返回执行失败回调函数
resolve(data) //否则就执行失败回调函数
})
})
}
getFile('1.txt') //调用读取文件函数,由上面函数代码可知,生成第一个promise实例
.then( data => { //这里.then就是运行上面生成的第一个promise实例
console.log('成功回调函数' + data)
return getFile('2.txt') //下面的.then就是在运行这里return的第二个promise实例
}, err => {
console.log('失败回调函数' + err)
return getFile('2.txt') //如果前面的promise出错,想让后面的promise继续运行,就在成功和失败回调函数都返回promise实例
})
.then( data => {
console.log('成功回调函数' + data)
return getFile('3.txt')
})
.then( data => {
console.log('成功回调函数' + data)
})
如果前面的promise出错,不想让后面的promise运行,就不要写失败回调函数,同时在最后面加如下代码:
.catch( err => {
console.log(err) //promise如果出错,会直接跳到这里运行出错处理代码
})
Promise.all(promiseArray)方法
1、将多个Promise对象实例包装,生成并返回一个新的Promise实例。
2、promise数组中所有的promise实例都变为resolve(成功)的时候,该方法才会返回,并将所有结果传递results数组中。
3、promise数组中任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。
var p1 = Promise.resolve(1)
var p2 = Promise.resolve(2)
var p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then( results => {
console.log(results) //输出 [1, 2, 3]
}, err => {
console.log(err)
})
Promise.all(promiseArray)方法
1、用法和上面一模一样。
2、哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
3、了解即可。
普通函数,一路到底。
1、 generator函数,中间可以停,到哪停呢,遇到yield就会停。
2、调用generator函数,会生成一个generator对象,此时函数还未运行。
**3、**generator对象有个next()方法,调用该方法就会运行generator函数。
4、generator函数遇到 yield 会停止时,想要运行就继续用next()启动执行。
5、 在函数名前加个 *
就是generator函数,不能使用箭头函数。
背后实际上生成多个小函数,实现走走停停。
function *show() {
console.log('1')
yield
console.log('2')
}
let genObj = show2() //用变量接住generator函数创建的generator对象
genObj.next() //输出 1
genObj.next() //输出 2
genObj.next() //最后了,没有结果输出
6、yield 只能在Generator函数内使用。
7、yield 既可传参,又可以返回。
8、第一个next()只用来启动,传参无效。
传参例子如下
function * show() {
console.log('1')
var a = yield
console.log('2')
console.log(a)
}
var gen = show()
gen.next() //输出 1
gen.next() //输出 2 undefined,因为没有传参,yield没有返回值
var gen = show()
gen.next(10) //输出 1 第一个next只用来启动,传参没用
gen.next(20) //输出 2 20
返回例子如下
function* show() {
yield 10
yield 20
}
var gen = show()
console.log(gen.next()) //输出 { value: 10, done: false }
console.log(gen.next()) //输出 { value: 20, done: false }
console.log(gen.next()) //输出 { value: undefined, done: true }
value是每一段小函数的yield值,done表示整个函数完成了没有。
因为最后一段函数是没有yield的,所以输出为undefined。
如果想要最后有值输出,在函数最后return一个值就行。
generator函数的作用:把异步操作写成同步代码,如下所示:
runner(function * () {
let data1 = yield $.ajax({url: 'data1'})
let data2 = yield $.ajax({url: 'data2'})
let data2 = yield $.ajax({url: 'data3'})
})
利用yield会暂停,等上一次请求完,下一次才能请求。写成同步代码的样子,非常好看。
promise 只适合一次读一组数据,不适合有逻辑性的异步操作。要是一次读了一组数据,还要进行逻辑判断再读不同的数据,就比回调写法还糟糕。
generator适合有逻辑性的异步操作,这是比promise 好的地方是。例子如下,写法完全和平时普通写代码一样。
runner(function * () {
let data = yield $.ajax({url: 'data '})
if (data .type == 'VIP') {
let items = yield $.ajax({url: 'dataVip '})
} else {
let items = yield $.ajax({url: 'dataNormal '})
}
})
在Node.js中,用同步方式写异步,请求数据库:
server.use(function * () {
let data = yield db.query(`select * from user_table`)
this.body = data
})
用法:3 ** 2 ,这是3的2次方。
由于是运算符,所以可以和 +=一样的用法
let b = 2
b **= 7 //2的7次方,9
检测数组是否包含某个东西。第一参数是要找的内容,第二参数是找的起点位置,没有第二参数就默认从头开始找。
let arr = ['a', 'b', 'c']
console.log(arr.includes('a')) //输出 true
console.log(arr.includes('a', 1)) //输出 false
字符串补全长度的功能,如果某个字符串不够指定长度,会在头部或尾部补全。
第一参数是要补至多少位,第二参数是如果不够位数要补什么,可以不写。
console.log('ab'.padStart(4, '0')) //输出 00ab
console.log('ab'.padEnd(4, '0')) //输出 ab00
如果不写第二参数,可以用来做对齐的作用,例子如下:
console.log('0.00')
console.log('10,000.00')
console.log('250,000.00')
输出结果如下:
0.00
10,000.00
250,000.00
使用padStart( )在字符串前面填充至指定位数20:
console.log('0.00'.padStart(20))
console.log('10,000.00'.padStart(20))
console.log('250,000.00'.padStart(20))
输出结果如下,全部右对齐了:
0.00
10,000.00
250,000.00
使用padEnd()可以在字符串后面填充至指定位数20:
console.log('0.00'.padEnd(20) + '0.00' )
console.log('10,000.00'.padEnd(20) + '10,000.00' )
console.log('250,000.00'.padEnd(20) + '250,000.00')
输过结果如下,因为前面全部是20位,所以后面的数字对齐了:
0.00 0.00
10,000.00 10,000.00
250,000.00 250,000.00
Async函数是用来替代Generator函数的,所以和Generator函数非常相似。
1、 async可以使用箭头函数,generator却不可以。
2、async放在函数名最前面来修饰,这generator的 * 类似。
3、await只能用在async函数中,道理和yield只能在generator中使用一样。
4、直接调用就行,async并不是像generator 一样走走停停,所以和普通函数使用方式一样。
最基础的例子,如图所示,和以前几乎一样:
async function show() {
console.log(1)
await
console.log(2)
}
show() //输出 1 2
async函数执行异步操作写法:
async () => {
let data1 = await $.ajax({url: 'data1'})
let data2 = await $.ajax({url: 'data2'})
let data2 = await $.ajax({url: 'data3'})
}
5、不用依赖外部,虽然和以前Generator函数使用方式几乎一样,但不用自己写一个runner函数来使用了,已经原生封装写好了。
例子,异步操作2需要异步操作1的结果,异步操作3需要异步操作1和2的结果:
const makeRequest = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}