ES6:也就使es2015,这一版更新了非常重要的知识点,也是目前前端面试内容占比最多的一部分
1、let,const.
1.11块级作用域:见到这种变量首先想到的就是es6新添了一种作用域,块级作用域。而生效过程即使在有let和const存在就会有会计作用域,顾名思义就是在大括号里有作用域,即for,if等语句中也存在作用域
1.12不存在变量提升:传统的会有变量提升,先var v1;console.log(v1);v1=2;let不存在变量提升就会报错
1.13统一块级作用域不允许重复声明
1.13const声明时必须赋值 ,否则报错,且const a=引用类型数据时改变不会报错
1.14暂时性死区:一个块级作用域内部如果定义了局部变量,就不会使用外部变量。
2、结构赋值,其实很简单,就是一一对应对象或数组中的值进行赋值(介绍的比较简单,其实很复杂,字符串也可以结构赋值等等...结构赋值的知识点比较多,也很杂,有兴趣的可以单独了解一下,这里介绍最常用的)
let [a, [[b], c]] = [1, [[2], 3]];
console.log(a, b, c); // 1, 2, 3
let [ , , c] = [1, 2, 3];
console.log(c); // 3
let [a, , c] = [1, 2, 3];
console.log(a,c); // 1, 3
let [a, ...b] = [1, 2, 3];
console.log(a,b); // 1, [2,3]
let [a, b, ..c.] = [1];
console.log(a, b, c); // 1, undefined, []
3、字符串函数的拓展,在String.prototype上添加了一些方法
repeat
方法返回一个新字符串,表示将原字符串重复n
次。第一个 参数是查询的值,第二个参数是开始查询的下标,允许负数,及倒数。。
includes和indexOf十分相似,但注意的是includes支持识别NaN等
4、模板字符串、用了这个就能减少字符串的拼接了,最讨厌的就是字符串拼接,现在这种十分牛X的功能解决一项心腹大患
// ES6之前
let a = 'abc' + v1 + 'def';
// ES6之后
let a = `abc${v1}def`
5、箭头函数,箭头函数没有作用域,this指向外部作用域,箭头函数是函数的简化
var fn=x=>x;fn(5)
function fn(x){//不用箭头函数
return x
}
6、Array.from方法用于将类似数组的对象或可遍历的对象转为真正的数组
7、参数默认值
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
log('Hello', undefined) // Hello World
8、
ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
rest 参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用 rest 参数改写数组push方法的例子。
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
//注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
9、Symbol类型,表示独一无二的值。它是 JS 的第七种数据类型,前六种是:undefined、null、Boolean、String、Number、Object。
var s = Symbol();
typeof(s);
//产生的原因:为了防止重名造成的冲突,所以引入了Symbol。
10、ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
const s = new Set();
// 将数组中的每个元素,通过add方法,添加到set结构中
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
console.log(Array.from(s));
console.log(s.size); // 获取长度
console.log(s.has(3)); // 是否存在元素3
console.log(s.delete(2));// 删除元素2,如果删除成功,则返回真
console.log(Array.from(s));
//s.clear(); // 清除所有元素,无返回值
//console.log(Array.from(s));
//遍历:
let set = new Set(["ab", "xx", "123"]);
set.forEach((value, key, arr) => console.log(value, key, arr) ) // 注意:value和key相同
//forEach支持第二个参数,用来绑定this,但箭头函数中,this永远指向window
const obj = {
fn: function(key, value) {
console.log(key, value);
}
};
let set = new Set(["ab", "xx", "123"]);
set.forEach(function(value, key, arr){
this.fn(key, value);
}, obj)
11、map结构:
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用
字符串当作键。这给它的使用带来了很大的限制。
const data = {};
const element = document.getElementById('myDiv');
data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"
上面代码原意是将一个 DOM 节点作为对象data的键,但是由于对象只接受字符串作为键名,
所以element被自动转为字符串[object HTMLDivElement]。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,
但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,
Object 结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更
完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
const m = new Map();
const o = {p: 'Hello World'};
console.log(o); // {p: 'Hello World'}
m.set(o, 'content')
console.log(m.get(o)); // "content"
console.log(m.has(o)); // true
console.log(m.delete(o)); // true
console.log(m.has(o)); // false
上面代码使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键。
12、generators生成器函数,现在已经逐渐被async函数取代,但是redux-saga还是基于它实现的
注意:
1. 与普通函数相比,生成器函数需要在 关键字function 和 函数名之间,加一个星号,星号前后是否写空格无所谓,带有星号的函数,就是生成器函数,即 Generator函数。
2. yield类似return,中文为产出的意思,表示程序执行到 yield时,程序暂停,产出一个结果,这个结果为对象 {value: 值, done:[false| true]},value指的就是yield后面跟的值,done指程序是否已经完成。
3. 生成器函数本身只返回一个对象,这个对象具有next方法,执行next方法时,函数体内容碰到yield才会暂停
Generator 函数,它最大的特点,就是可以交出函数的执行权,即暂停执行。
function * fn(num1, num2){
var a = num1 + num2;
yield a;
var b = a - num2;
yield b;
var c = b * num2;
yield c;
var d = c / num2;
yield d;
}
var f = fn(1, 2);
console.log( f.next() ); // {value:3, done:false}
console.log( f.next() ); // {value:1, done:false}
console.log( f.next() ); // {value:2, done:false}
console.log( f.next() ); // {value:1, done:false}
console.log( f.next() ); // {value:undefined, done:true}
es7:es7的内容就很少了,严格来说就两点
includes()
用于查找一个值是否在数组中,如果在返回true
,否则返回false
。
['a', 'b', 'c'].includes('a'); // true
['a', 'b', 'c'].includes('d'); // false
let a = 3 ** 2 ; // 9
// 等效于
Math.pow(3, 2); // 9
es8:es8主要更新了一个受人吹捧的async函数,非常好用,但还是基于promise的,所以promise还是很重要的!!!
ES8引入async
函数,是为了使异步操作更加方便,其实它就是Generator函数的语法糖。async
函数使用起来,只要把Generator函数的**(*)**号换成async
,yield
换成await
即可。对比如下:
// Generator写法
const fs = require('fs');
const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
// async await写法
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
对比Genenrator有四个优点:
async
函数自带执行器,即async
函数与普通函数一模一样:async f();
async
和await
,比起星号
和yield
,语义更清楚了。async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果。yield
命令后面只能是 Thunk 函数或 Promise 对象,而async
函数的await
命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。async
函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then
方法指定下一步的操作。进一步说,async
函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await
命令就是内部then
命令的语法糖。
async
函数返回一个Promise对象,可以使用then
方法添加回调函数,函数执行时,遇到await
就会先返回,等到异步操作完成,在继续执行。
async function f(item){
let a = await g(item);
let b = await h(item);
return b;
}
f('hello').then(res => {
console.log(res);
})
async
表明该函数内部有异步操作,调用该函数时,会立即返回一个Promise对象。
另外还有个定时的案例,指定时间后执行:
function f (ms){
return new Promise(res => {
setTimeout(res, ms);
});
}
async function g(val, ms){
await f(ms);
console.log(val);
}
g('leo', 50);
async
函数还有很多使用形式:
// 函数声明
async function f (){...}
// 函数表达式
let f = async function (){...}
// 对象的方法
let a = {
async f(){...}
}
a.f().then(...)
// Class的方法
class c {
constructor(){...}
async f(){...}
}
// 箭头函数
let f = async () => {...}
返回Promise对象
async
内部return
返回的值会作为then
方法的参数,另外只有async
函数内部的异步操作执行完,才会执行then
方法指定的回调函数。
async function f(){
return 'leo';
}
f().then(res => { console.log (res) }); // 'leo'
async
内部抛出的错误,会被catch
接收。
async function f(){
throw new Error('err');
}
f().then (
v => console.log(v),
e => console.log(e)
)
// Error: err
await命令
通常await
后面是一个Promise对象,如果不是就返回对应的值。
async function f(){
return await 10;
}
f().then(v => console.log(v)); // 10
我们常常将async await
和try..catch
一起使用,并且可以放多个await
命令,也是防止异步操作失败因为中断后续异步操作的情况。
async function f(){
try{
await Promise.reject('err');
}catch(err){ ... }
return await Promise.resolve('leo');
}
f().then(v => console.log(v)); // 'leo'
使用注意
await
命令放在try...catch
代码块中,防止Promise返回rejected
。await
后面的异步操作不存在继发关系,最好让他们同时执行。// 效率低
let a = await f();
let b = await g();
// 效率高
let [a, b] = await Promise.all([f(), g()]);
// 或者
let a = f();
let b = g();
let a1 = await a();
let b1 = await b();
await
命令只能用在async
函数之中,如果用在普通函数,就会报错。// 错误
async function f(){
let a = [{}, {}, {}];
a.forEach(v =>{ // 报错,forEach是普通函数
await post(v);
});
}
// 正确
async function f(){
let a = [{}, {}, {}];
for(let k of a){
await post(k);
}
}
2、Object原型上加了一些方法,Object.values(),Object.entries()
我们知道Object.keys(),是把对象的key放到一个数组中,那么values是把value放到数组中,entries是把key,value都放到数组中
var a = { f1: 'hi', f2: 'leo'};
Object.keys(a); // ['f1', 'f2']
let a = { f1: 'hi', f2: 'leo'};//如果不是对象返回空数组
Object.values(a); // ['hi', 'leo']
let a = { f1: 'hi', f2: 'leo'};
Object.entries(a); // [['f1','hi'], ['f2', 'leo']]
3、字符串填充 padStart和padEnd
用来为字符串填充特定字符串,并且都有两个参数:字符串目标长度和填充字段,第二个参数可选,默认空格。
'es8'.padStart(2); // 'es8'
'es8'.padStart(5); // ' es8'
'es8'.padStart(6, 'woof'); // 'wooes8'
'es8'.padStart(14, 'wow'); // 'wowwowwowwoes8'
'es8'.padStart(7, '0'); // '0000es8'
'es8'.padEnd(2); // 'es8'
'es8'.padEnd(5); // 'es8 '
'es8'.padEnd(6, 'woof'); // 'es8woo'
'es8'.padEnd(14, 'wow'); // 'es8wowwowwowwo'
'es8'.padEnd(7, '6'); // 'es86666'
从上面结果来看,填充函数只有在字符长度小于目标长度时才有效,若字符长度
已经等于或小于目标长度时,填充字符不会起作用,而且目标长度如果小于字符
串本身长度时,字符串也不会做截断处理,只会原样输出。