ES6
es6是js的最新标准,新增了很多概念,目前大部分浏览器都支持ES6,ie7~11除外。
let
let和const也是声明变量的关键字,和之前的var一样,不过有一些特殊。
-
let 声明的变量只在let命令所在的代码块有效。
例: if(true){ let a=0; console.log(a)// 0 } console.log(a) //报错 a is not defined 找不到a这个变量 a变量只在{}内才有效,{}外无法找到。
-
let同一个变量只能声明一次,多次声明会报错。
例: let a=0; let a=1; //报错 'a' has already been delared
-
let不存在变量提升,在声明语句前调用变量会报错
console.log(a) //undefined var a=10; console.log(a)//10 console.log(b) // 报错b is not defined let b=10;
const
const声明一个只读变量,声明后不允许在改变,否则会报错。
注意:
const保证的是内存地址保存的数据不能改动,但是如果是声明对象、数组、函数时,变量指向的内存地址只是一个指向数据的指针,const只能保证这个指针固定,但是指针指向的数据就不能控制了。
解构赋值
数组解构
let [a,b,c]=[121,222,321]
//打印 a =121 b=222 c=321 可以理解为 let a=121,b=222,c=321
数组也可以进行嵌套
let [a,[[b],c]]=[1,[[2],3]]
//打印 a=1 b=2 c=3 只要位置对应上就可以把右边的值付给左边
let [a,b,c]=[1,,3]
// 打印 a=1 b=undefined c=3 中间有空值不会影响其他的赋值
let [a,...b]=[1,2,3]
//打印 a=1 b=[2,3] ... 是剩余运算符 表示剩下的值都付给该变量
字符串解构
let [a, b, c, d, e] = 'hello';
//打印 a='h' b='e' c='l' d='l' e='o' 当字符串可以遍历时,就可以进行解构赋值
解构默认值
let [a = 2,b=a] = [1];
//打印 a=1 b=1
//解构赋值的规则:先按位置在右边寻找左边变量对应的值,因为a在右侧有对应的值, 那么a变成右边的1,忽略a=2这个默认值,b在右边没有对应的值,那他的默认值就是b=a,所以b=a=1
let [a = 3, b = a] = []; // a = 3, b = 3
let [a = 3, b = a] = [1]; // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
对象结构
//基本解构赋值
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
//嵌套解构赋值
let obj = {p: ['hello', {y: 'world'}] }; 对象通过 {属性:值}来进行赋值,数组只要位置对应即可
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
//可忽略解构赋值
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello' 即使右边的有多余的值,只要其他位置对应 即可解构赋值,不会受到影响
//不完全解构
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined 左侧有多余的变量,右边没有对应的值时 默认undefined
// y = 'world' 可以理解成 只要 右边有值的对象或数组 在左侧都能找到对应位置 即可解构赋值,有点类似数学中的 子集 即{a,b,[c]}={1,2}
//剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40} 剩余的值以新对象的方式付给rest
//解构默认值
let {a=10,b=2} ={a:3};
//a=3 b=2
let {a:aa=10,b:bb=5}={a:3}; 这里的a 可以看做是两边联系的变量
//aa=3 bb=5
//如果是let {a:aa=10,b:bb=5}={b:3}
//那么打印出来就是 aa=10 bb=3
symbol
symbol是新的原始数据类型,通常用来表示独一无二的值,我个人理解相当于是一个不能改变的常量,就像html的id一样 是唯一的,最大的用法,是用来给对象定义属性名。
基本用法
let sy=symbol("kk") /"kk”不是这个sy的值,他只是sy的描述
console.log(sy) //symbol("kk")
console.log(typeof sy) //"symbol"
let sy1=symbol("kk")
console.log(sy === sy1) //false 尽管相同参数,但是sy和sy1是不同的两个变量,可以把symbol()想象成一个生成随机数的方法,每次调用它都会产生一个随机数来表示这个变量,每次的结果都是独一无二的。
使用场景
当一个对象属性过多时,可以用symbol来给对象定义属性
let sy=symbol("key");
let obj={};
obj[sy]="kk"
console.log(obj) //{symbol("key1"):"kk"}
let sy=symbol("key");
let obj={
[sy]:"kk"
};
console.log(obj) //{symbol("key1"):"kk"}
let obj= {};
Object.defineProperty(obj, sy, {value: "kk"});
console.log(syObject); // {Symbol(key1): "kk"}
symbol.for()和symbol.keyFor()
symbol.for("Yellow")是现在全局搜索,如果没有以"Yellow"作为参数登记的symbol,那么新建一个symbol("Yellow"),并登记在全局以供搜索,如果下次搜索搜索到已经有这个symbol,那么就会直接返回。所以用symbol.for()创建的参数一样的symbol,其实是同一个,是相等的
let yellow1 = Symbol.for("Yellow");
let yellow2 = Symbol.for("Yellow");
yellow1 === yellow2; // true
symbol.keyFor()返回一个已登记symbol的key
symbol.keyFor(yellow1) //"Yellow"
注意事项
- symbol作为对象属性使用时不能通过.调用,只能使用[ ]
- symbol不能进行任何运算(+ - * /)
- symbol不会出现在for…in for…of的循环中,要读取对象的symbol属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
- ES5声明常量通常用const,但是const声明的常量值是有可能相等的,比如声明两个值都为"red"的常量,在判断这两个常量时就会出现问题,所以symbol是用来声明独一无二的常量的。
MAP对象与SET
map对象
map对象和object类似,不同的是:
-
obj对象键只能是字符串或symbol,map对象的键可以是任意值。
var myMap = new Map(); var keyString = "a string"; //当key是字符串 myMap.set(keyString, "2333"); myMap.get(keyString); // "2333" myMap.get("a string"); // "2333" // 因为 keyString === 'a string' var keyObj = {}, //当key是对象 myMap.set(keyObj, "和键 keyObj 关联的值"); myMap.get(keyObj); // "和键 keyObj 关联的值" myMap.get({}); // undefined, 因为 keyObj !== {} var keyFunc = function () {}, // 函数 myMap.set(keyFunc, "和键 keyFunc 关联的值"); myMap.get(keyFunc); // "和键 keyFunc 关联的值" myMap.get(function() {}) // undefined, 因为 keyFunc !== function () {} myMap.set(NaN, "not a number"); //key是NAN myMap.get(NaN); // "not a number" var otherNaN = Number("foo"); myMap.get(otherNaN); // "not a number"
-
map对象键值数量可以通过sizi属性获取,obj对象需要手动计算。
-
object都有原型,原型中的键名可能和自身的键名产生冲突。
set对象
set对象允许存储任何类型的唯一值,因为里边的值都是唯一的,所以存的时候会判断是不是恒等,比如我存了1 后边再存1的时候就会判断 已经有1了 就不会重复存储了。
let mySet = new Set();
mySet.add(1); // Set(1) {1}
mySet.add(5); // Set(2) {1, 5}
mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性
var o = {a: 1, b: 2};
mySet.add(o);
mySet.add({a: 1, b: 2});
// Set(5) {1, 5, "some text", {…}, {…}}
// 这里体现了对象之间引用不同不恒等,即使值相同,Set 也能存储
特殊情况
因为要判断恒等,有几种特殊情况需要记住:
1.+0和-0在判断唯一性的时候是恒等的,所以不重复。
2.undefined和undefined是恒等的,所以不重复。
3.NaN和NaN是不恒等的,但是在set只能存一个,所以不重复
class
相当于构造函数的另一种写法
class Person{ //定义了一个叫Person的类
constructor(name,age){ //constructor是一个构造方法,用来接收参数
this.name=name;
this.age=age;
}
say(){ //这是一个类的方法,注意千万不要加上function
return "我的名字叫"+this.name+"今年"+this.age+"岁了"
}
}
var obj=new Person("laotie",88)
console.log(obj.say()) //我的名字叫laotie,今年88岁了
注意
在类中声明方法的时候,千万不要给class加function关键字
方法之间不要用逗号分隔,否则会报错
class不存在变量提升,所以需要先定义再使用
promise对象
promise是异步编程的解决方案,有时我们需要在异步运算结束后进行操作,使用promise就可以直到异步什么时候结束,并在结束时执行要执行的操作。
特点
promise有三种状态 pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,其他操作都无法改变这个状态。
Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(要么成功,要么失败,已经定型)。
基本用法
const p1 = new Promise(function(resolve,reject){ //resolve是成功执行的函数,reject是失败执行的函数。
resolve('success1'); //成功执行时会把参数传进去
resolve('success2');//失败执行时会把参数传进去,这两个只会执行其中一个,不是失败就是成功。
});
p1.then(function(value){ //then()方法可以有两个参数,就是对应p1对象里fuction的resolve成功回调和reject失败回调。
console.log(value); // success1
});
//上边可以理解成
const p1 = new Promise(function(resolve,reject){
resolve('success1'); //成功执行时会把参数传进去
//resolve=function(value){ //value就是传进来的"success1"
console.log(value);
}
resolve('success2');//失败执行时会把参数传进去,这两个只会执行其中一个,不是失败就是成功。
});
缺点
1.无法中途取消promise,一旦新建成功那么就会立即执行,无法取消。
2.如果不设置回调函数,那么promise抛出的错误不会返回到外部。
3.当处于pending时,无法直到代码执行到具体哪一步(刚刚开始执行还是快要结束了)。
Then()
then方法可以接受两个参数,一个是对应resolve的回调,一个是对应reject的回调,所以我们能够分别拿到他们传过来的数据。同时,then()方法可以多次调用,每次调用return会返回一个resolve或reject的promise对象用于接下来的链式调用。
const p = new Promise(function(resolve,reject){
resolve(1);
}).then(function(value){ // 第一个then // 1
console.log(value);
return value * 2;
}).then(function(value){ // 第二个then // 2
console.log(value);
}).then(function(value){ // 第三个then // undefined
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ // 第四个then // resolve
console.log(value);
return Promise.reject('reject');
}).then(function(value){ // 第五个then //reject:reject
console.log('resolve:' + value);
}, function(err) {
console.log('reject:' + err);
});
catch()
catch方法和then的第二个参数一样,用来指定reject的回调,而且在执行resolve的回调时,如果代码出错了,那么不会卡死报错,而是进到这个catch方法中,相当于try/catch
all()
并行异步操作,在所有异步操作执行完后才执行回调(谁跑得慢以谁为准)
race()的用法
相当于谁跑得块以谁为准,当异步加载资源时,可以再写个延时函数,如果延时函数先执行,那么就提示加载超时
箭头函数
就是将之前的函数写法简单化
左边是函数名和参数 => 右边是函数体
var f=v=>v
//上面相当于var f=function(v){return v}
var f=(a,b)=>a+b //当有多个参数,用()括起来
var f=function(a,b){return a+b}
var f=(a,b)=>{
当函数体有多行代码时,用{}包裹起来
}
当返回对象时,要区分代码块,用({})包裹代码块
报错
var f=(id,name)=>{id:id,name:name}
不报错
var f=(id,name)=>({id:id,name:name})
注意:
箭头函数里没有this(构造函数里的this指的是写函数时那个外部的this)、super、arguments和new.target绑定
箭头函数不可作为构造函数 不能用new命令
扩展运算符
对象中的扩展运算符 … 用来取出参数对象中所有可遍历的属性,拷贝到当前对象中
let bar={a:1,b:2}
let baz={…bar} //{a:1,b:2}
如果用户自定义的属性,放在扩展运算符的后面,则扩展运算符内部的同名属性会被覆盖
扩展运算符是浅拷贝,拷贝基础数据类型没问题,但是拷贝引用数据类型时,会拷贝引用的地址,当拷贝对象变化的时候,源对象会跟着改变
数组中的扩展运算符
可以将数组转换为参数序列
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
可以复制数组
const arr1 = [1, 2];
const arr2 = [...arr1];//[1,2]
可以和解构赋值一块用
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
可以把字符串转成真正的数组
[...'hello']
// [ "h", "e", "l", "l", "o" ]
用于数组赋值时,只能放在最后一位,否则报错