ES6是下一代JavaScript语言标准的统称,每年6月发布一次修订版,迄今为止已经发布了3个版本,分别是ES2015、ES2016、ES2017。
最近一边玩linux一边玩网络安全,今天突然闲下来更新一下blog,写一下ES6。ES6(ECMAScript6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了。Mozilla公司将在这个标准的基础上,推出JavaScript 2.0。
ECMAScript和JavaScript到底是什么关系?
简单来说,ECMAScript是JavaScript语言的国际标准,JavaScript是ECMAScript的实现。
let
允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var关键字不同的是,它声明的变量只能是全局或者整个函数块的。
let与var的区别1
2
3
4
5
6
7{
var a = 100;
let b = 200;
}
console.log(a); //100
console.log(b); //b
is not defined -- Error
let不存在变量提升1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//ES5****************************
var a = [];
for (var i = 0; i < 10; i++) {
var c = i;
a[i] = function () {
console.log(c);
};
};
a[5](); //9
//ES6**************************
var b = [];
for (var j = 0; j < 10; j++) {
let d = j;
b[j] = function () {
console.log(d);
};
};
b[5](); //5
let不受外界影响1
2
3
4
5
6var a = 100;
{
console.log(a); //undefined
let a = 100;
console.log(a); //100
}
let不允许重复声明,模块内也不允许重复声明1
2
3
4
5
6
7
8
9
10{
var a = 100;
var a = 200;
console.log(a); //200
}
{
let a = 100;
let a = 200;
console.log(a); //报错
}
块级作用域1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 console.log("ES5:");
function fun() {
var num = 100;
if (true) {
var num = 200;
};
console.log(num);
};
fun(); //200
**********************************************
console.log("ES6:");
function fun() {
let num = 100;
if (true) {
let num = 200;
};
console.log(num);
};
fun(); //100
立即执行函数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 console.log("ES5:");
function fun() {
console.log("I am outside!");
};
(function () {
if (false) {
function fun() {
console.log("I am inside!");
};
};
fun();
}());
******************************************
console.log("ES6:");
function fun() {
console.log("I am outside!");
};
(function () {
if (false) {
function fun() {
console.log("I am inside!");
};
};
fun();
}());
立即执行函数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 console.log("ES5:");
function fun() {
console.log("I am outside!");
};
(function () {
if (false) {
function fun() {
console.log("I am inside!");
};
};
fun();
}());
******************************************
console.log("ES6:");
function fun() {
console.log("I am outside!");
};
(function () {
if (false) {
function fun() {
console.log("I am inside!");
};
};
fun();
}());
const
const Pi = 3.1415926535;
声明创建一个常量,其作用域可以是全局或本地声明的块。 与var变量不同,全局常量不会变为窗口对象的属性。需要一个常数的初始化器;也就是说,必须在声明的同一语句中指定它的值。
const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。例如,在引用内容是对象的情况下,这意味着可以改变对象的内容(例如,其参数)。另外常量不能和它所在作用域内的其他变量或函数拥有相同的名称。
const块级作用域1
2
3
4if (true) {
const Pi = 3.14159265;
};
console.log(Pi); //Pi is not defined
const暂时性死区1
2
3
4if (true) {
console.log(Pi); //undefined
const Pi = 3.14159265;
};
const不可重复声明1
2
3
4
5{
var a = 100;
const a = 200;
console.log(a);
}
const对象1
2
3
4
5
6const person = {};
person.name = "hhardyy";
person.age = 22;
console.log(person.name); //hhardyy
console.log(person.age); //22
console.log(person); //Object {name: "hhardyy", age: 22}
const对象错误方法1
2
3
4
5
6
7const person = {};
person.name = "hhardyy";
person.age = 22;
console.log(person.name);
console.log(person.age);
console.log(person);
person = {}; //person is read-only
const数组1
2
3
4
5
6
7
8
9
10
11
12 const arr = [];
console.log(arr);
console.log(arr.length);
**********************************
arr.push("Hello world!");
console.log(arr);
console.log(arr.length);
**********************************
arr.length = 0;
console.log(arr);
console.log(arr.length);// 错误用法
arr = ["Hello Everyone!"];
const对象冻结1
2
3
4
5
6const person = Object.freeze({});
person.name = "hhardyy";
person.age = 22;
console.log(person.name); //undefined
console.log(person.age); //undefined
console.log(person); //Object
使用const对象冻结1
2
3
4
5
6
7const person = Object.freeze({
name: "hhardyy",
age : 22
});
console.log(person.name); //hhardyy
console.log(person.age); //22
console.log(person); //Object
跨模块常量
1 | // module.js |
全局变量属性
1 | var varName = "varValue"; |
数组的解构赋值
数组解构1
2
3
4var [a, b, c] = [1, 2, 3];
console.log(a); //1
console.log(b); //2
console.log(c); //3
对应位置1
2
3
4
5
6
7
8
9
10
11
12let [foo, [[bar], base]] = [1, [[2], 3]];
console.log(foo); //1
console.log(bar); //2
console.log(base); //3
let [, , third] = ["first", "second", "third"];
console.log(third); //third
let [one, , three] = ["One", "Two", "Three"];
console.log(one); //One
console.log(three); //Three
let [head, ...tail] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(head); //0
console.log(tail); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
数组解构不成功1
2
3
4
5 var [temp] = [];
console.log(temp); //undefined
var [first, second] = [100];
console.log(first); //100
console.log(second);//undefined
不完全解构1
2
3
4
5
6
7let [x, y] = [1, 2, 3];
console.log(x); //1
console.log(y); //2
let [a, [b], c] = [1, [2, 3], 4];
console.log(a); //1
console.log(b); //2
console.log(c); //4
制定默认值1
2
3
4
5
6
7
8
9
10
11
12
13var [temp = "string"] = [];
console.log(temp); //string
var [temp = "string"] = ["tempString"];
console.log(temp); //tempString
var [x = "aaa", y] = ["bbb"];
console.log(x); //bbb
console.log(y); //undefined
var [m, n = "aaa"] = ["bbb"];
console.log(m); //bbb
console.log(n); //aaa
var [p, q = "aaa"] = ["bbb", undefined];
console.log(p); //bbb
console.log(q); //aaa
非遍历结构–报错1
2
3
4
5var [temp] = 1; //1[Symbol.iterator] is not a function at eval
var [temp] = false; //false[Symbol.iterator] is not a function at eval
var [temp] = NaN; //NaN[Symbol.iterator] is not a function at eval
var [temp] = undefined; //Cannot read property 'Symbol(Symbol.iterator)' of undefined at eval
var [temp] = null; //Cannot read property 'Symbol(Symbol.iterator)' of null at eval
Iterator接口1
2
3
4
5
6
7
8
9
10
11
12
13
14let [a, b, c] = new Set(["a", "b", "c"]);
console.log(a); //a
console.log(b); //b
console.log(c); //c
function* fibs() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
};
var [first, second, third, fourth, fifth, sixth] = fibs();
console.log(sixth); //5
对象的解构赋值
对象的解构赋值不需要按照顺序1
2
3var { name, age } = { name: "Conan", age: 28 };
console.log(name); //Conan
console.log(age); //28
变量名与属性名不一致1
2
3
4
5
6
7
8
9
10
11
12// var { person_name, person_age, person_id } = { id: "007", name: "Conan", age: 28 };
// console.log(person_name); //undefined
// console.log(person_age); //undefined
// console.log(person_id); //undefined
var { name: person_name, age: person_age, id: person_id } = { id: "007", name: "Conan", age: 28 };
console.log(person_name); //Conan
console.log(person_age); //28
console.log(person_id); //007
let object = { first: "Hello", last: "World" };
let { first: firstName, last: lastName} = object;
console.log(firstName); //Hello
console.log(lastName); //World
对象解构默认值1
2
3
4
5
6
7var { x = 3 } = {};
console.log(x); //3
var { x, y = 5 } = { x: 1 };
console.log(x); //1
console.log(y); //5
var { message: msg = "You Are A Person!" } = {};
console.log(msg); //You Are A Person!
对象解构默认值条件1
2
3
4var { x = 3 } = { x: undefined };
console.log(x); //3
var { y = 3 } = { y: null };
console.log(y); //null
已声明变量的解构赋值1
2
3var x;
({x} = { x: 1 });
console.log(x); //1
现有对象的方法1
2
3console.log(Math.sin(Math.PI/6)); //0.49999999999999994
let { sin, cos, tan, log } = Math;
console.log(sin(Math.PI/6)); //0.49999999999999994
字符串的解构赋值
1 | const [ a, b, c, d, e ] = "Hello"; |
字符串的属性解构1
2
3
4const { length: len } = "Hello";
console.log(len); //5
const { length } = "Hello World!";
console.log(length);//12
函数参数的解构赋值
1 | function sum([x, y]) { |
函数参数解构赋值的默认值1
2
3
4
5
6
7function fun ({x = 0, y = 0} = {}) {
return [x, y];
};
console.log(fun({x: 100, y: 200})); //[100, 200]
console.log(fun({x: 100})); //[100, 0]
console.log(fun({})); //[0, 0]
console.log(fun()); //[0, 0]
函数参数解构赋值的默认值undefined1
2
3
4
5
6
7function fun ({x, y} = { x: 0, y: 0 }) {
return [x, y];
};
console.log(fun({x: 100, y: 200})); //[100, 200]
console.log(fun({x: 100})); //[100, undefined]
console.log(fun({})); //[undefined, undefined]
console.log(fun()); //[0, 0]
箭头函数
Arrow Function(箭头函数)。
为什么叫Arrow Function?因为它的定义用的就是一个箭头:1
x => x * x
相当于ES5的1
2
3function (x) {
return x * x;
}
有些浏览器不支持箭头函数,测试方法
1 | //使用严格模式 ; |
普通函数中的this:
1.this总是代表它的直接调用者(js的this是执行上下文), 例如 obj.function ,那么function中的this就指向obj
2.在默认情况(非严格模式下,未使用 ‘use strict’),没找到直接调用者,则this指的是 window
3.在严格模式下,没有直接调用者的函数中的this是undefined
4.使用call,apply,bind(ES5新增)绑定的,this指的是绑定的对象
箭头函数中的this
没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是window;
ES6加入了新的数据类型Symbol与新的数据结构set、map。
symbol最大的特点是唯一性,symbol不能隐式的转换类型,所以和其他数类型数据做运算时会报错。但是,symbol可以显式地转为字符串和布尔类型。对象object,在添加symbol类型属性时,必须用[]外扩symbol,不能用点式法对其进行操作。如同其他类型数据一般,symbol也有一些内置方法。1
2let a=Symbol();
console.log(typeof a);//symbol,js语言的数据类型再添一员
Symbol不是一个构造函数,如果new Symbol会报错1
2
3
4
5
6
7
8
9
10
11
12var sym1 = Symbol();
var sym2 = Symbol("foo");
var sym3 = Symbol("foo");
console.log(sym1, sym2, sym3) //输出Symbol() Symbol(foo) Symbol(foo)
用同样的参数创建两次, 也是不相等的:
Symbol("foo") === Symbol("foo"); //输出:false
let a=Symbol();
let b=Symbol();
console.log(a);//Symbol()
console.log(b);//Symbol(),a与b虽然不相等,但是打印出的数据却是一样的,为了区分他们,可以传参:
let c=Symbol('ccc');//Symbol(ccc),传入的参数仅做标记,没有任何实际意义
symbol类型的数据不能与其他类型的数据进行运算,它可以转为string与boolean类型,但是不能转换为number类型,
数据结构Set。
类似于数组,只不过其成员值都是唯一的,没有重复的值,向Set加入值的时候,不会发生类型转换。1
2
3
4
5
6
7
8
9
10
11Set本身是一个构造函数,用来生成Set数据结构。
var s = new Set();
// 通过add方法向Set结构中加入成员
[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x))
for (i of s) { console.log(i) }
// 2 3 4 5 (表明set结构不会添加重复的值)
Set结构接收一个数组作为参数,用来初始化。
var items = new Set([1, 2, 3, 4, 5, 5, 5]);
items.size
// 5
set的四个方法:
add(value) : 添加value。
delete(value) : 删除value。
has(value) : 返回一个布尔值,表示该值是否为Set的成员。
clear() : 清除所有成员。
数据结构Map
Js对象本质上是键值对的集合。但是只能使用字符串充当键。ES6为了解决这个问题提供了Map结构。它类似对象,也是键值对集合,但是”键”的范围不限于字符串,对象也可以当作键。1
2
3
4
5
6
7
8
9
10var data = {};
var el = document.getElementById("Div");
// 将DOM节点el当做对象data的键,但是对象只接受字符串作为键名,所以el被自动转换为字符串"[Object HTMLDivElement]"
data[el] = metadata;
//map下
var m = new Map();
o = {p: "hello world"};
m.set(o, "content");//o作为m的一个键
console.log( m.get(o) ); // content
Map函数可接收一个数组进行初始化。1
var map = new Map([["name", "hhardyy"], ["title", "hhardyytitle"]]);
Map的6种方法:
size : 返回成员总数。
set(key, value) : 设置一个键值对。
get(key) : 读取一个键。
has(key) : 返回一个布尔值,表示某个键是否在Map结构中。
delete(key) : 删除某个键。
clear() : 清除所有成员。
Map原生提供三个遍历器。
key() : 返回键名的遍历器。
values() : 返回键值的遍历器。
entries() : 返回所有成员的遍历器。1
2
3
4
5
6
7
8
9
10
11
12for ( let key of map.key() ) {
console.log("key: %s", key);
}
for ( let value of map.value() ) {
console.log("value: %s", value);
}
for ( let item of map.entries() ) {
console.log("Key: %s, Value: %s", item[0], item[1]);
}
for (let item of map) {
console.log("Key: %s, Value: %s", item[0], item[1]);
}
Map还有一个forEach方法,与数组中的forEach方法类似,也可以实现遍历。1
2
3map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});
forEach方法还可接受第二个参数,用来绑定this。1
2
3
4
5
6
7
8
9
10var reporter = {
report: function(key, value) {
console.log("key: %s, Value: %s", key, value);
}
};
map.forEach(function(value, key, map) {
this.report(key, value)
}, reporter);
forEach()方法的回调函数中的this, 就指向reporter
promise
举个栗子,有5个互相嵌套的异步请求
第一种实现方式,callback回调,这种方法如果想更改一些业务需求,还是比较麻烦的,而且如果在没有注释的情况下,去看代码可能有点头大1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20function func(cb){
setTimeout(()=>{
cb&&cb()
},1000)
}
func(()=>{
console.log(1);
func(()=>{
console.log(2);
func(()=>{
console.log(3)
func(()=>{
console.log(4)
func(()=>{
console.log(5)
})
})
})
})
})
第二种实现方式,Promise,可以做成像无脑安装软件一样一直下一步1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23function func2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve()
},1000)
})
}
func2().then(()=>{
console.log(11)
return func2()
}).then(()=>{
console.log(22)
return func2()
}).then(()=>{
console.log(33)
return func2()
}).then(()=>{
console.log(44)
return func2()
}).then(()=>{
console.log(55)
return func2()
})
什么是Promise?Promise就是用同步的方式,书写异步代码,用它原生的写法有点繁琐,因为要new多个Promise对象,然后每个发送ajax,然后再每个then(resolve,reject),这里就直接写个处理过的1
Promise.all([ajax1,ajax2,ajax3,ajaxn]).then(res=>{成功},err=>{失败})
还有个叫 promise.race
promise.all是与的关系,promise.race中的race=竞速的意思,也就是哪个先到先请求哪个1
2
3
4
5
6
7promise.race([
$.ajax({url:'hhardyy.com/data/1'})
$.ajax({url:'hhardyy.com/data/2'})
$.ajax({url:'hhardyy.com/data/3'})
$.ajax({url:'hhardyy.com/data/4'})比如这个先到了就用这个
$.ajax({url:'hhardyy.com/data/5'})
])
面向对象
以前的面向对象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
28
29// 对象
function User(name,pass){
this.name=name;
this.pass=pass;
}
User.prototype.showName=function(){
console.log(this.name);
}
User.prototype.showPass=function(){
console.log(this.pass)
}
let user=new User('hhardyy','123456')
user.showName();
user.showPass();
// 继承
function VipUser(name,pass,level){
User.call(this,name,pass);
this.level=level;
}
VipUser.prototype=new User();
VipUser.prototype.constructor=VipUser;
VipUser.prototype.showLevel=function(){
console.log(this.level)
}
let vipuser=new VipUser('huangbingzhen','123456',3)
console.log("继承")
vipuser.showName();
vipuser.showPass();
vipuser.showLevel();
ES6的面向对象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
28
29
30//对象
class User{
constructor(name,pass){
this.name=name;
this.pass=pass;
}
showName(){
console.log(this.name)
}
showPass(){
console.log(this.pass)
}
}
let user=new User('huangbingzhen','123456')
user.showName();
user.showPass();
// 继承
class vipUser extends User{
constructor(name,pass,level){
super(name,pass) //超类
this.level=level
}
showLevel(){
console.log(this.level)
}
}
let vipuser=new vipUser('huangbingzhen','123456',3)
vipuser.showName();
vipuser.showPass();
vipuser.showLevel();
generator
generator: 生成器
普通函数=一路到底,generator函数=中间能停
比方:普通函数=飞机高铁,行驶途中不允许下车一下;generator函数=出租车(:老哥我下车一下马上回来,:去吧)
generator函数1
function* show | function * show() | function *show()
yield:作用和java里头的一样1
2
3
4
5
6
7
8
9function *show(){
console.log('a');
yield; //截断
console.log('b');
}
let genObj=show();
console.log(genObj)
genObj.next() //只出a
genObj.next() //出完a,b
它相当于把一个大函数分成了几个小函数show_1()、show_2(),应用场景=比如请求数据1
2
3
4
5
6
7
8
9
10
11
12
13function ajaxFunc(){
代码...
$.ajax(xxx.function(){
然后为了写的好,下面的代码就要写到这里,回调代码...,然后就形成回调地狱~~~
})
代码...
}
//generator函数
function ajaxFunc(){
...代码
yield $.ajax(xxx,function(){}) //到这里就可以暂停一下读数据
...代码
}
yield可以传参1
2
3
4
5
6
7
8
9function *show(num){
console.log(num)
console.log('a');
let item=yield;
console.log('b')
console.log(item)
}
let genObject=show(100)
genObject.next(12)
第一个过程传参=原来函数的传参方法,第一个过程是没办法给yield传参的1
2
3
4
5
6
7function *show(num){
console.log(num);
yield
console.log('b')
}
第二个过程之后就可以
genObject.next(5)
1 | function *show(){ |
ES7&&ES8
includes:检查数组是否包含某个东西
1 | let arr=[1,2,3,4,5] |
keys/values/entries
keys=>把所有的key拿出来
values=>把所有的value拿出来
entries=>把所有的键值对拿出来1
2
3
4
5
6
7
8
9
10
11
12
13
14let arr=[11,22,33,44,55]
for(let item of arr.keys()){
console.log(item) //0,1,2,3,4
}
for(let item of arr.values()){
console.log(item) //11,22,33,44,55
}
for(let item of arr.entries()){
console.log(item) //[0,11],[1,22],[2,33],[3,44],[4,55]
}
entries加上解构赋值可以这么玩
for(let [key,value] of arr.entries()){
console.log(`${key}:${value}`)
}
需要注意的是上面这些json暂时玩不了
幂
以前的求幂是用Math的pow,ES7给了个比较6的方法,就是两个**,像python1
2console.log(Math.pow(5,2)) //25
console.log(5**2) //25
字符串
ES6多了个startsWith/endsWith,7的时候多了个padStart/padEnd,也就是前后补东西,很简单理解,比方说过去我要取一个字符串的前面10位1
2
3
4
5
6let str='hhardyy'
console.log(`(${str.padStart(10)})`) //( hhardyy),不够10位它会补上,endsWith也是差不多
console.log(`(${str.padEnd(10)})`) //(hhardyy )
//我还可以要求补位的时候补上我喜欢的东西
console.log(`(${str.padStart(10,'6')})`)//(666hhardyy)
console.log(`(${str.padEnd(10,'6')})`) //(hhardyy666)
async await
ES6的generator是配合yield用的,具体看上面写的ES6的东西,这里说的是ES7出来一个把它们取代的,叫做async
await,区别就是用generator的时候需要用到一个renner,但是async+await就不需要,可以直接用,但是对浏览器版本要求比较高,它的写法1
2
3
4
5async function Object(){
console.log('a')
await
console.log('b')
}
举例肯定是数据交互,要起一个本地服务,ajaxFunc是封装的一个普通的原生ajax,就不放出来了1
2
3
4
5
6async function getData(){
let data1=await ajaxFunc('arr.txt')
let data2=await ajaxFunc('json.txt')
console.log(data1,data2)
}
getData()
过去的generator写法,需要写一个runner,有人把它写成了库给别人去install,还有一点是generator不能写成箭头函数,但是async可以 async getData()=>{}1
2
3
4
5renner(function *getData(){
let data1=yield ajaxFunc('arr.txt')
let data2=yield ajaxFunc('json.txt')
console.log(data1,data2)
})