let与const

声明let与const都是块级作用域,所以不能想var声明一样,没有变量声明提升。
let是声明变量,const是常量声明,不能修改。

1
2
let a = 5;
const b = 10;

块级作用域代替函数作用域

1
2
3
4
5
6
7
8
9
10
11
(function(){
// 函数作用域
var a = "ccc";
})()
console.log(a);

// 块级作用域
{
let b = 1000;
}
console.log(b);

使用块级作用域就可以避免如循环后只能获取到最后一个值的情况。

箭头函数

1
2
3
4
let arr = [1,2,3];
arr.map((item, index)=>{
return item+index;
})

使用箭头函数可以简化代码,而且this的指向问题也出现了变化,箭头函数的this上下文环境为定义函数的环境,而普通函数的this上下文环境为调用对象决定的环境。
如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let obj = {
a(){
setTimeout(()=>{
console.log(this);
})
}
}
obj.a();
// obj对象
------------
let obj = {
a(){
setTimeout(function(){
console.log(this);
})
}
}
obj.a();
// window对象

所以需要注意像事件调用的函数与一些对调用环境决定this指向时不要使用箭头函数,不是会出现this指向错误,
如:

1
doc.addEventListener((e)=>{console.log(this)});

这是错误的。

字符串模板

模板字符串使用反引号 () 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(${expression})的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来,如果一个模板字符串由表达式开头,则该字符串被称为带标签的模板字符串,该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前,你都可以通过该函数来对模板字符串进行操作处理。在模版字符串内使用反引号(`)时,需要在它前面加转义符(\)。

1
2
3
4
5
6
7
let aa = "abc cc";

let str =`
哈哈 ${aa} 一起走
`;

console.log(str);

带标签的模板字符串

就是使用反引号 () 调用函数,然后传入多个参数,而第一个参数为把含有表达式的部分为分割点切割成多个值存放到数组里,后续的参数为表达式的第一个值、第二个值。。。依次传入,
如:

1
2
3
4
5
6
7
8
9
10
11
12
let p = 'mike',
age = 25;
function temp(str, ...key){
console.log(str);
console.log(key);
return str + key;
}
let op = temp`that is ${p} is a ${age}`;

// ["that is ", " is a ", ""]
// ['mike', 25]
`

解构

结构可以让我们用一个更简便的语法从一个数组或者对象(即使是深层的)中分离出来值,并存储他们。

结构数组

1
2
3
4
5
6
7
8
9
10
11
let [a,b,c,d] = [1,2,3,4]
let [v, ...k] = [0,1,2,3,4,'b']
// 不完全解构
let [a,b] = [7,8,9,1]
// 深度解构
let [[[a=',b='']='']='']=[[['g','v']],['c']]

// 数组合并
let arr1 = ['ss','rr','vvv'];
let arr2 = ['ccc','kkk','hhh'];
let cArr = [...arr1, ...arr2];

解构对象

1
2
3
4
5
6
7
8
9
let {a} = {a:1,b:2}
let {a:{b:{c=''}=''}='',a} = {a:{b:{c:10}}}

// 对象合并
let obj1 = {p:"vvv",cc:"ccc"}
let obj2 = {o:"dddd"}
let obj = { ...obj1, ...obj2 }
// 变量重命名
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }

对象解构,左边的变量名对应右边的键值名

增强的对象字面量

1
2
3
4
5
6
7
8
9
10
let aa = 'ooo';
let obj = {
aa
};
// 动态
let tt = 'cc';
let ff = 'rr';
let obj = {
[tt+"#"+ff]: 'ccc'
}

数组的拓展方法

1、keys

1
2
3
let arr = ["a", , "c"];
console.log([...arr.keys()]);
console.log(Object.keys(arr));

2、entries

1
2
3
4
5
6
7
8
9
10
let arr = ['r','p','c'];
let itr = arr.entries();
for(let a of itr){
console.log(a);
}
/**
* [0, "r"]
* [1, "p"]
* [2, "c"]
**/

3、values

1
2
3
let arr = ["a", , "c"];
console.log([...arr.values()]);
console.log(Object.values(arr));

4、includes

1
2
3
4
5
6
7
8
9
10
11
12
var array1 = [1, 2, 3];

console.log(array1.includes(2));
// expected output: true

var pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false

5、find

1
2
3
4
5
6
7
8
var array1 = [5, 12, 8, 130, 44];

var found = array1.find(function(element) {
return element > 10;
});

console.log(found);
// expected output: 12

6、findIndex

1
2
3
4
5
6
7
8
var array1 = [5, 12, 8, 130, 44];

function findFirstLargeNumber(element) {
return element > 13;
}

console.log(array1.findIndex(findFirstLargeNumber));
// expected output: 3

函数

函数的默认值

给函数参数设置默认值的意思是:当没有传参的时候 ,参数取设定的默认值,即传入值为undefined

1
2
3
4
5
6
7
function log(x, y = 'World'){
console.log(x, y);
}

console.log('Hello'); // Hello World
console.log('Hello', 'China'); // Hello China
console.log('Hello', null); // Hello null

rest参数

1
2
3
4
5
6
7
function f(a, ...arr, b) {
// ...
} // 报错
function f(a, b, ...arr) {
console.log(arr);
}
f("c","b","d","x","c");// ["d","x","c"]

name属性

1
2
3
4
5
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"

对象函数简易写法

1
2
3
4
5
6
7
8
9
10
11
12
let obj = {
func(){
return 'xxxx';
}
}
等同于
var obj = {
func: function(){
let cs = 'xx';
return cs;
}
}

双冒号运算符

双冒号运算符用来取代 call, apply, bind 调用

1
2
3
4
5
6
7
obj::fun;
// 等同于
obj.bind(fun);

obj::fun(...arguments);
// 等同于
obj.apply(fun, arguments);

如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。

1
2
3
4
5
6
7
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

let log = ::console.log;
// 等同于
var log = console.log.bind(console);

SetMap

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 本身是一个构造函数,用来生成 Set 数据结构。
Set 函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let set = new Set(['red', 'green', 'blue']);
let arr = [...set];
// ['red', 'green', 'blue']

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。
Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let map0 = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');

let map1 = new Map(
[...map0].filter(([k, v]) => k < 3)
);
// 产生Map结构 {1 => 'a', 2 => 'b'}

let map2 = new Map(
[...map0].map(([k, v]) => [k * 2, '_' + v])
);
// 产生Map结构 {2 => '_a', 4 => '_b', 6 => '_c'}

for..of

Iterator遍历器,它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
而Iterator为for..of提高消费,既可带有遍历器的对象都可被for…of遍历。

for…of循环可以使用的范围包括数组、Set和Map结构、某些类似数组的对象(比如arguments对象、DOM NodeList对象)、Generator对象,以及字符串。

1
2
3
4
5
6
7
8
9
let map = new Map();
map.set('a1','b')
.set('v2','cr')
.set('a3','br')
.set('v4','cr');
for(let [key, value] of map){
console.log(key);
console.log(value);
}

importexport
import语句用于导入由另一个模块导出的绑定。无论是否声明了 strict mode ,导入的模块都运行在严格模式下。在浏览器中, import语句只能在声明了type=”module”的script的标签中使用。
此外还有一个类似函数的动态import(),它不需要依赖type=”module”的script标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 默认导入与命名导入一起导入
// 第一个参数为默认导入,第二个命名导入
import _,{* as names} from 'a.js';

// 动态导入
import('a.js').then((module)={
// 对导入模块操作
});

aynsc function func(){
let module = await import('a.js');
return module;
}

export语句用于在创建JavaScript模块时,从模块中导出函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。
``` bash
// 不能使用var,let或const用于导出默认值export default。
// 只能有一个默认的导出
// 命名导出对导出多个值很有用。在导入期间,必须使用相应对象的相同名称。
export let a = {"a":10};
export let b = {"b":10};
let bb = 20;
export default bb;

Generators 生成器

generator也是为了解决地狱回调问题的,本质还是各种回调。
在某些时候也可以认为generator为es6中新定义的数据类型,这种数据类型和函数很像,每个函数只能返回一个结果,即只能return一次,如果在某些函数中没有看到return,其实质在函数结尾是存在一个隐藏的return undefined 的,而generator不同,可以返回多次

Generators生成器对象是由一个 generator function 返回的,并且它符合可迭代协议和迭代器协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function* gen(a){
let b = 9 * (yield(10 + a));
let c = 100 - (yield(b / 2));
let d = c / 8;
return d;
}

let func = gen(20);
console.log(func); // Generator {_invoke: ƒ}
let ii = func.next(9);
console.log(ii); // {value: 30, done: false}
let ii2 = func.next(40);
console.log(ii2); // {value: 180, done: false}
let ii3 = func.next(5);
console.log(ii3); // {value: 11.875, done: true}

当done不为false时可以继续调用next方法,而返回值为yield的结果值,而传入值会替换yield的部分继续走下去。
如第一个next传入的值为9,而变量b = 9 * 9。

generator function 返回的,并且它符合可迭代协议和迭代器协议,所以可以被for…of遍历,而当遇到return后不会继续遍历,return值不会被返回出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* gen(){
yield 10;
yield 20;
yield 30;
yield 40;
return 50;
yield 100;

}

for(let v of gen()){
console.log(v);
}
// 10 20 30 40

return和throw在生成器中起到了终止的作用,迭代器it可以直接调用throw方法,传入的参数就是catch中的err,然后在调用it的next方法时,done已经被设置成true了,就是说生成器终止了。这个异常其实是在生成器内部产生的,只是我们没有捕获,所以继续往外抛了出来,如果在内部try-catch这个异常也是可以的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var g = function* () {
while(true) {
yield;
}
};

var i = g();
i.next();

try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}

Promise

Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。
Promise 优缺点
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 使用
let p = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('success');
},1000);
});

p.then(d=>{
console.log(d);
},d=>{
console.log(d);
});

// 链式使用
p.then(d=>{
console.log(d);
return new Promise((resolve, reject)=>{
resolve(100);
});
},d=>{
console.log(d);
}).then(d=>{
console.log(d);
});

资料

Promise 的api 
1、Promise.resolve()
2、Promise.reject()
3、Promise.prototype.then()
4、Promise.prototype.catch()
5、Promise.all() // 所有的都有完成,相当于 且
6、Promise.race() // 完成一个即可,相当于 或
7、Promise.resolve()的作用将现有对象转为Promise对象resolvedl;Promise.resolve(‘test’)==new Promise(resolve=>resolve(‘test’))
8、Promise.reject()也是返回一个Promise对象,状态为rejected;
9、then方法上边已经做介绍,这里就不再介绍。
10、catch():发生错误的回调函数。

ayncs与await

async await可以用更少的处理实现 Promise 和 Generators 所实现的异步处理,避免多层嵌套,是一种多个Promise同步的优雅的写法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let a = function ii(x){
let p = new Promise((res, rej)=>{
setTimeout(()=>{
res('success '+x);
},1000);
});
return p;
}

async function ayn(){
let x = await a("zzc");
console.log(x);
let b = await a(x+"zhi");
console.log(b);
let c = await a(b+"sss");
console.log(c);
let d = await a(c+"xx");
return d;
}

ayn().then(d=>{
console.log(d);
});

class

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 使用
class Abc{
constructor(){
this.a = 'abc';
}
getA(){
return this.a;
}
setA(a){
this.a = a;
}
}
let abcx = new Abc();
console.log(abcx.getA()); // abc

// 继承
class Ext extends Abc{
constructor(){
super();
super.getA();
}
getSA(){
return this.a;
}
}
let abcx = new Ext();
console.log(abcx.getA()); // abc

继承时一定要调用super(),因为需要初始化父类,把父类的this指向子类的this,若不调用会报错。而父类的方法的使用可以使用super。

后续会持续更新…