ES6在ES5的基础上增加了许多新功能和新用法,现在js更加倾向于面向对象的语言了,而且用过es6的写法可以大大简化代码。
不过很遗憾的是只有当前的一些高版本的浏览器才支持ES6,因此我们需要ES6的转码器babel,利用nodejs的npm功能就可以下载
1.所声明的变量,只有在let命令所在的代码块内才有效,这也是es6最大的特点之一:增加了块级作用域。
2.let不存在变量提升的问题,let声明的变量一定要在let声明之后使用,否则会报错。
3.暂时性死区:在let命令声明变量之前,该变量都是不可用的。只要在作用域之内存在let,它所声明的变量就绑定到了这个区域之内,不受外部影响。
4.暂时性死区的本质就是:只要进入当前作用域,所要使用的变量就已经存在了,但是不可以获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
es6明确规定:如果区块中有let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭性作用域,凡是在声明之前就使用这些变量就会报错。
es6中的暂时性死区和无变量提升:
主要是为了减少运行时的错误,防止在变量声明之前就使用这个变量,从而导致意料之外的错误。
不允许重复声明:在同一个作用域之内,let和const都不允许重复声明同一个变量,包括变量和形参的名字相同的情况。
const命令
const声明一个只读的常量,const声明的变量不能被修改,这意味着我们只能在声明的同时就赋值,不能先声明后赋值。
不过const对于一个引用值来说有点特殊,const只是保证变量指向的地址不变,当我们const声明一个对象或者数组的时候
顶层对象的属性
顶层对象指的就是我们的window这一级别的对象。
在es5中,我们在全局环境声明一个变量,相当于把这个变量挂载到了window对象上面:
这就导致了编程人员在写的时候经常会不知不觉地创建了全局变量,这样很不利于模块化变成。
在es6中,var和function 命令声明的全局变量,依然是window的属性,但是let、const、class声明的全局变量,不再是window的属性了。
ES6-解构赋值
es6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,这被称之为解构赋值。
如果解构失败,变量的值就是undefined
如果等号的右边不是数组(或者严格来说,不是可遍历的结构),那么将会报错。
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
这些都会报错。
解构赋值对于var、let、const命令都适用。
解构赋值与允许执行默认值
let [a,b=2] = [1];
只有数组的值严格等于undefined,才会被赋予默认值,否则即使是null和false也会被正常赋值
let [x=1] = [undefined]; // x = 1;
let [x=1] = [null]; // x = null;
默认值可以引用解构赋值的其他变量,但是前提是该变量必须已经声明。
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError
最后一个之所以会报错,是因为x用到默认值y的时候,y还没有被声明。
对象的解构赋值和数组的解构赋值的区别在于:数组的元素是按照次序排序的,因此它们赋值也是按照顺序赋值的,
但是对象的属性是没有次序的,变量必须与属性同名,才会被赋值。
var {a,b} = {b:'234',a:123};
// a = 123;
// b = '234';
对象的解构赋值的内部机制是:先找到同名的属性,然后把这个属性值赋予给对应的变量。
{a:demo} = {a:123};
//demo 123
a只是一个匹配模式,模式是不会被赋值的。
字符串也可以被解构赋值,和数组非常类似
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
由于类数组和对象都有length属性,我们还可以专门对这个属性进行解构赋值:
let {length : len} = 'hello';
len // 5
解构赋值的时候,如果等号右边是数组和布尔值,则会有限转化成对象。
由于undefined和null无法转化为对象,所以对他们进行解构赋值都会报错。
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
参数的解构赋值也可以有默认值,当我们不传入实参的时候就会赋值为默认值。
但是要注意这种写法和下面这一种是不一样的:
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
二者的主要区别在于默认值的写法上,第一种是给x、y变量指定默认值,而第二种是给move函数指定默认值而不是x、y变量。这一点一定要注意!!
还有一点:圆括号可能会对解构赋值带来影响,因此能不用圆括号就不用。
1.交换变量
[x, y] = [y, x];
2.函数可以有多个返回值
return [1, 2, 3];
然后可以通过解构赋值给多个变量赋值
3.函数参数的定义
解构赋值可以方便的把形参和实参对应起来,只需要形参是一个对象即可,不需要是按照固定的顺序写实参,就像我们在es5中用的json参数一样
function f({x, y, z}) { … }
f({z: 3, y: 2, x: 1});
4.提取JSON数据
let { id, status, data: number } = json;
不仅仅是json,只要是个对象我们都可以把属性值快速的提取出来。
5.函数参数的默认值
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
6.遍历Map解构
map可以用for…of循环遍历,解构赋值可以方便获得键/值。而且可以只获取键名或者键值
7.配合requireJS
const {src} = require(‘source’);
这样可以直接获取source模块中的src变量。
当然也可以配合es6中的import来使用
新加的:var a = `gudhol
dhjikjkpois` 这个是撇
还可以加变量:var str = ‘abc’
var b = `gh
deh${str}
fg
1.Array.from()
这个方法用于将类数组和可比遍历的对象(包括es6的Set和Map数据结构)转化成真正的数组.
浅克隆:arr1 = arr.slice(); 或者 arr1 = arr.concat();
Array.from()还接受第二个参数,作用类似数组的map方法,用来对每个元素进行处理,然后将处理后的值放入到返回的数组中。
添加一个函数,进行处理
Array.from()的另一个应用是,将字符串转化成数组,然后返回数组的长度。
function countSymbols(string) {
return Array.from(string).length;
}
因为它能够正确处理各种Unicode字符,可以避免Javascrit将大于\uFFFF的Unicode字符算成两个字符的bug。
2.Array.of()
var arr = Array.of(10)
创建的就是一个只含有元素10的数组。
3.copyWithin 数组实例的方法
(该方法定义在原型上,必须用数组实例来调用该方法)
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。
接受三个参数:
target(必选):从该位置进行替换数据
start(可选):从该位置开始读取数据,默认是0,如果是负值,表示倒数第几个
end(可选):从该位置前停止读取数据,默认等于数组长度。如果是负值,表示倒数第几个。
例如:
var arr = [1,2,3,4,5,6,7,8];
arr.copyWithin(0,3,6) 结果是: [4, 5, 6, 4, 5, 6, 7, 8]
4.find 和 findindex (数组实例的方法)
find 返回第一个符合条件的元素,
findindex 返回第一个符合条件的元素的索引值
例如:
var arr = [1,2,3,4];
var sum = arr.find(function (ele,index,self) {
return ele > 3;
})
5.fill (数组实例的方法)
例如:
var arr = new Array(100);
arr.fill(10,1,10) 将arr从第二位到第十位填充上10
第一个参数:填充的数字
第二个参数:开始填充的位置的索引值
第三个参数:从该位置前停止填充
6.includes (数组实例的方法)
var arr = [1,2,NaN];
console.log(arr.includes(NaN))
结果是true
以前:var arr = [1,2,NaN];
console.log(arr.indexof(NaN)); 结果会-1,因为indexof方法是通过绝对相等来判断的,因为NaN === NaN 结果是false。
使用Number.isNaN()进行判断
(1)在es5中,判断一个变量是否为NaN,采用isNaN( )函数判断,预期应该只有NaN才会返回true,但是对于某些特别的变量,这个方法就出现了不符合预期的值 :
isNaN(NaN) //true
isNaN(undefined) //true
isNaN(‘qwer’) //true
isNaN(123) //false
(2)在es6中推出了新的判断方法 Number.isNaN( ):
Number.isNaN(undefined) //false
Number.isNaN(NaN) //true
Number.isNaN(‘qwer’) //false
Number.isNaN(123) //false
7.keys
values 不好使,用valuesOf
entries
var arr = [1, 2, 3];
var it = arr.keys(); //创建接口
it.next() //执行一次结果是{value: 0, done: false}
1、当对象的属性名和变量名相等时,
//例如:
var name = ‘gln’;
var obj = { name: name}
//前一个name是属性名,后一个name是变量名。
//扩展之后:
var name = ‘gln’;
var obj = { name} 即可,
2.当对象里面的属性对应着一个函数;
扩展之后: 之前:
var obj = { var obj = {
a() { } } a:function() {} }
3.属性名表达式
var name = ‘a’;
var obj = {
a1:1 ,
[name] : 2,
[name + '123'] :3
}
console.log(obj[name + 1]); 结果是1
console.log(obj) 结果是{a1:1,a:2}
4.4.Object.is() 判断两个值是否相等
例如:Obeject.is(NaN,NaN) 结果是true
等同于 ===
(1)+0 不得于 -0 (2)NaN 得于 NaN
5.Object.assign() 对象合并(浅克隆)
第一个参数:目标对象
后面的所有参数都是要融合要目标函数的函数
var target = { a: 2};
var source = {b : 3};
var source1 = {c: 4};
Object.assign(target,source,source1);
6.Object.keys(将对象的属性放到一个数组里面,返回),
Object.values(将对象的每一个属性的值放到一个数组里面,返回),
Object.entries(返回一个二维数组)
例如:
var obj = { a : 1, b: 2, c: 3}
console.log(Object.keys(obj))
var [a, ...arr] = [1,2,3]
console.log(arr)
结果是[2,3]
ar obj = {a:2};
var obj1 = {b:3,...obj,}
console.log(obj1)
结果是{b: 2, a: 2}
unction add(...arr) {
console.log(arr);
}
add(1,2,3) 处理传入不定个参数时,用arguments接受是类数组,用该方法接收,是数组。
(1)函数体里面的this对象,就是定义时所在的对象,并不是使用时所在的对象。
(2)不可以当做构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可使用arguments对象,该对象在函数体内不存在,如果要用,可以用rest参数代替。
(4)当return的是一个对象的时候,不可以var ar = () => {a};会报错,应该将对象用括号包裹。
1.不可以重复变量声明
2.类内部所定义的方法是不可枚举的。(即遍历不出来)
3.类的实例共享一个原型对象(即同样一个原型实例化的两个对象,修改其中的一个对象的原型上的方法或者属性的时候,另一个对象的方法或者属性也会改变)
4.extends 继承 (见 4.1)
5.静态方法(可通过函数名点一个方法调用)以及实例方法(必须要实例化一个对象才能调用该方法)
(见5.1,5.2(是在class上面定义一个静态函数))
6.子继承夫级的静态方法,见6.1,、
子必须静态的方法来继承父亲的静态方法,实例化方法继承实例化方法。
7.constructor 当你不写的时候,默认给你一个空的,但是该方法一定是有的。
定义一个class类:
class person{
constructor () {
this.name = 'gaolina'
};
eat() {
console.log(this.name);
}
}
var person1 = new person();
person1.eat();
//4.1:
class father{
constructor(){
this.money = "一亿";
} }
class son extends father{
constructor(){
super(); 创建一个father的对象
this.age = '10'; 将该方法添加的father对象中
} }
var son1 = new son();
console.log(son1);
// 5.1
function f() {this.name = "f"}
f.show = function() {return "show"}; //静态化方法
f.prototype.eat = function() {return "eat"} //实例化方法
console.log(f.show());
var ff = new f();
console.log(ff.eat());
// 5.2
class father{
constructor(){
this.money = "一亿";
}
static eat() { 表静态
return "eat";
}
}
console.log(father.eat());
// 6.1
class son extends father{
constructor(){
super();
this.age = '10';
}
static eat() {
super.eat();
}
}
var obj = { a:1 }
Object.defineProperty(obj,"b",{
value:2, writable:false,
enumerable:false, configurable:false,
get() { return 1000; },
set(newValue) { return newValue; }
})
取值函数:get(当你obj.b时,走这个函数)
存值函数:set(obj.b = 3,走这个函数)
value:值,writable:可写性 ,enumerable:可枚举
configurable:可配置(设置为false,后面就不发修改b的任何属性)
class类的取值函数和存值函数: class father{
constructor(){
this.money = “一亿”;
}
set money(value) {
return value;
}
get money() {
return 100;
}
}
1.为数据的集合(数据结构)map,set,数组的遍历提供一个统一的接口。
2.使用方法:
var arr = [1,2,3];
var it = arrSymbol.iterator;
console.log(it.next());
结果是:{value: 1, done: false}
遍历结束之后的结果是:{value: undefined, done: true}
3.调用iterator接口的场合(底层实现)
(1)解构赋值
(2)扩展运算符
(3)for of
(4)Array.from()
(5)Map(),Set(),(比如new Map([‘a’,1],[‘b’,2]));
1.Set
Set和数组很相似,但是它的成员的值都是唯一的,没有重复值。
Set实例的方法分为了两大类:操作方法和遍历方法。
操作方法:
add(value):添加某个值,返回Set结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
遍历方法:
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
属性:
size 长度
由于set结构的实例没有属性名,只有属性值,所以keys和values方法的行为一致。
set结构的forEach方法,对于每个成员执行某种操作,没有返回值。
2.WeakSet
WeakSet结构和Set很相似,主要有两个区别:
1.WeakSet的成员只能是对象,不能是其他类型的值。
2.WeakSet中的对象都是若引用,即垃圾回收机制不会考虑WeakSet对该对象的引用,也就是说,如果有其他对象都不在引用该对象,
那么垃圾回收机制会自动回收该对象所占用的内存,不考虑它是否在WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可以遍历的。
3.Map
Map主要是解决传统es5中的键值对的键名只能是字符串的问题而引入的一种结构 。
在Map中,键值名不限于字符串,可以使各种类型的值。
创建:var m = new Map();
特点:他的属性名可以是任意类型,
初始化的值可以是:二维数组,map,
var m = new Map([[属性名,属性值],[属性名,属性值]])
方法:
(1)size 长度 (2)set 设置属性,键名,键值
(3)has 是否具有否属性 (4)get 获取属性值
(5)delete 删除属性 (6)clear 清空
(7)keys(), 返回键名的遍历器
(8)values(),返回键值的遍历器
(9)entries(),返回键值对的遍历器
(10)forEach(),使用回调函数,遍历每个成员
4.symbol
symbol:用于当你要往某个工具库里面添加方法的时候,因为你不知道你准备设置的方法是否在工具库里面已有,如果已有,数据库里面的方法将会被覆盖,
1.创建:var s = new Symbol();
括号里面可以添加字符串,字符串仅是说明的功能
使用:
var obj = {a:1}
var g = Symbol(“添加属性”)
obj.g = 4;
console.log(obj) 结果是:{a: 1, g: 4}
Promise
一、Promise含义
Promise是异步编程的一种解决方案,所谓的Promise,简单来说就是一个容器,里面保存着未来才会结束的事件的结果。
Promise对象有以下两个特点:
1.对象的状态不受外界影响。Promise对象代表一种异步操作,有三种状态:Pending(进行中)、Resolved(已完成)、Rejected(已失败)。
只有异步操作的结果可以改变状态,其他的任何操作都不能改变状态。
2.一旦状态改变了,就不会再变了,任何时候都可以得到这个结果。Promise对象的状态只有两种可能:Pending->Resolved或者Pending->Rejected。
只要这两种情况发生了,状态就不会再改变了,并且会一直保持这个结果。这与事件监听不同,事件的特点是,不同时间监听,得到的结果都是不同的。
当然Promise也有一些缺点:
首先无法取消Promise,一旦创建他就会立即执行,中途无法取消。
其次,如果还不设置回调函数,Promise内部跑出的错误,不会反映到外部。
最后,当处于Pending状态时,无法得知目前进展到哪一个阶段了。
当p.then 的回调函数返回的是一个简单的值的时候,即使p是rejected,p.then 的状态也是resolved,当p.then 返回一个新的promise对象的时候,p.then的状态就和里面返回的promise对象状态一致。
二、基本用法
Promise对象是一个构造函数,用来生成Promise实例的。
var promise = new Promise(function(resolve, reject) {
// … some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。他们是两个函数,由js引擎提供,不需要自己部署。
将Promise对象的状态从“未成功”变成“成功”(Pending->Resolved),在异步操作成功时调用,并将一步操作的结果,作为参数传递出去。
将Promise对象的状态从“未成功”变成“失败”(Pending->Rejected),在异步操作失败时候调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变成Rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
// 这里我们就可以用Promise来异步加载图片:
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
var image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
这一段代码中使用了then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数中。
三、Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
getJSON("/posts.json").then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
四、Promise.all()
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.all([p1, p2, p3]);
当p1p2p3的状态都是resolved的时候,p的状态才是resolved,否则只要有一个是rejected,那么p的状态就时rejected。
五、Promise.race()
Promise.race()方法同样是将多个Promise实例,包装成一个新的Promise实例,用法和all是一样的。
它与all的不同在于:race是只要有一个状态改变了,p的状态就会改变,并且变成和第一个状态改变之后的一样的状态。
六、Promise.resolve()
有时候需要将现有的对象转为Promise对象,Promise.resolve方法就起到这个作用。
Promise.resolve(‘foo’)
// 等价于new Promise(resolve => resolve(‘foo’))
Promise.resolve方法的参数分为四种情况:
1.参数是一个Promise实例
如果参数是Promise实例,那么Promise.resolve将不做任何修改、原封不动地返 回这个实例。
2.参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。
Promise.resolve方法会将这个对象转为Promise对象,然后立即执行thenable对象的then方法。
3.参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve 方法返回一个新的Promise对象,状态为Resolved
Promise.resolve方法允许调用时不带参数,直接返回一个Resolved状态的Promise对象。
所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve方法
七、Promise.reject()
Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。它的参数用法与Promise.resolve方法完全一样。
八、其他两个附加方法
1.done()方法
Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。
因此,我们可以提供一个done方法,总是处于 回调链的尾端,保证抛出任何可能出现的错误。
2.finally()方法
finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法最大的区别就在于,它接受一个普通的回调函数作为参数,该参数不管怎样都必须执行。
类似try catch finally里面的finally。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
箭头函数可以让this指向固定化,这种特性很有利于封装回调函数。
this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。
正是因为它没有this,所以也就不能用作构造函数。