2017-08-22-day08
Set 集合:
- 什么是: 值不允许重复的集合。专门用于按元素值,直接判断集合中是否存在指定元素
- 何时: 快速去重复(通过元素值,快速判断是否包含元素时)
- 如何使用:
var set=new Set(凡是可遍历的,都可打散转为Set)
将set转回数组: var arr=[...set];//散播
下面是一个简单的例子
var arr=[1,2,3,2,1];
var set=new Set(arr);
console.log(set);//将数组转为Set类型
arr=[...set];
console.log(arr);//将Set类型转为数组
以下是一个经典笔试题,数组去重复,且考虑百万数量级的情况下有什么方法效率高,repeat1是最笨的实现方式,indexOf底层是for循环的方式,repeat2是用Set类型来实现的,底层是哈希实现方式,下面用过打印执行时间来观察。在执行函数前加console.time,执行函数后加console.timeEnd可以打印出该程序的执行时间。
for(var i=0,arr=[];i<1000000;i++){
arr[i]=parseInt(Math.random()*1000+9000)
}
function repeat1(arr){
var res=[];
for(var i=0;i<arr.length;i++){
if(res.indexOf(arr[i])==-1)
res.push(arr[i]);
}
return res;
}
function repeat2(arr){
var set=new Set(arr);
return [...set];
}
以下是另一种实现方法,结果巨快的。这是一种最好的数组去重的方法。这种答案最好。
function repeat3(arr){
var res=[];
var hash={};
for(var i=0;i<arr.length;i++){
if(hash[arr[i]]===undefined){
hash[arr[i]]=1;
res.push(arr[i]);
}
}
return res;
}
左1是要被去重的数组,左2是一个哈希数组(也可以理解为对象)它是用来筛选数组的,右1是最终结果,将左1的值在对象的key值中是undefined,那么就给它定义并赋值1,不是1也可以这个随意。只是要让它存在而已。后面的自行理解。
- Set集合的其他方法:
1)set.size 获取集合中元素的个数
2)set.has(value)判断集合中是否包含指定元素
3)set.add(value)添加元素,如果已经存在,则无效
4)set.delete(value)删除元素
5)set.clear()清空集合
Map 集合:
- 什么是: 一组键值对的集合(实际上就是关联数组或对象的一种封装)
- 何时: 代替关联数组(对象)使用
如何:
var hash=new Map(); //相当于新建对象{}
hash.set(key,value); //相当于给对象赋值hash[key]=value
var bool=hash.has(key); //相当于判断对象值是否存在hash[key]!==undefined
- Map集合的其他API
map.size 获得map中键值对的个数
map.has(key)查询一个key是否存在
map.get(key)获得指定键对应的值
map.set(key,value)添加一个新的键值对,如果key已经存在,则不会重复添加
map.delete(key)删除指定key对应的键值对
map.keys()获取map中所有的key的集合
map.values()获取map中所有的value的集合
将上面百万级别数组去重的问题用map的方式改写一遍,结果如下,速度还可以,和set差不多,但是没有hash快,那个最好。
function repeat3(arr){
var res=[];
var hash=new Map();
for(var i=0;i<arr.length;i++){
if(!hash.has(arr[i])){
hash.set(arr[i],1);
res.push(arr[i]);
}
}
return res;
}
for of : 代替for循环遍历
- 什么是: 依次获得数组/类数组对象/集合中每个值
- 何时: 只要不关心位置,只关心每个元素值时
- 如何:
for(var value of 数组|类数组对象|集合){
value: 自动获得当前元素值
}
以下是一个简单的遍历对象数组的例子
let array = [{"a": 1}, {"b": 2}, {"c":3}];
for (let obj of array) {
console.log(obj);
}
问题:
1). 无法获得位置
2). 必须从头到尾,逐个执行,不能跳跃或遍历部分
3). 只能从头到尾,顺序遍历,不能反向遍历
Symbol类型
- ES6中,为了获得一个不与对象中现有任何属性冲突的唯一属性,定义了Symbol类型
- Symbol是第六大原始类型
- Symbol的值必须通过Symbol函数获的
- Symbol的值作为属性名时,不能用.访问,只能用[]访问。
以下是一个例子,点击一下按钮就让小方块右移一段距离,图片正在移动时再点击无效,html中
<button id="btn_right">右移一次</button>
<div id="pop"></div>
css中
div{
width:100px;
height:100px;
background:red;
position:fixed;
top:100px; left:0;
transition:all 2s linear;
}
js中
function moveRight(elem,dist){
var left=
parseFloat(getComputedStyle(elem).left);
left+=dist;
elem.style.left=left+"px";
}
var isMoving=Symbol("isMoving");
console.dir(isMoving);
pop[isMoving]=false;
btn_right.onclick=function(){
if(pop[isMoving]==false){
pop[isMoving]=true;
moveRight(pop,100);
setTimeout(()=>pop[isMoving]=false,2000);
}
}
pop.isMoving=false;
****Promises(重点掌握)
- 什么是: 解决"callback hell"(回调地狱)
- 为什么: 回调函数,层级很可能很深,可读性极差
- 何时: 今后,只要多个回调函数,必须保证顺序执行时
我先写了这样的3个函数,callback指的是回调函数,下一个任务必须要在上一个任务完成之后才可以执行,这种必须得用回调函数来做。如果把callback()直接放在第一个任务中的话,它不会等3秒,而会直接执行,只有放在setTimeOut中才会等3秒再执行。
function conn(callback){
console.log("连接数据库...");
//3秒后自动调用callback
setTimeout(callback,3000);
}
function query(callback){
console.log("查询数据...");
setTimeout(callback,5000);
}
function response(callback){
console.log("返回响应...");
setTimeout(callback,2000);
}
如何让这3个函数串起来顺序执行呢,执行conn并在它的参数中传一个匿名函数,匿名函数中调用query(),依次类推。只有这样写才会先执行conn,等3秒后执行query,等5秒后执行response,等2秒后打印查询出结果。
//callback hell
conn(function(){
query(function(){
response(function(){
console.log("查询出结果!");
})
})
});
但是上面的写法很难看一直往下陷,像一个大坑。称之为回调地狱。
- 如何使用Promise: 2步:
1). 在前一个函数内
return new Promise((callback)=>{
... callback() ...
})
2). 在调用前一个函数时,
前一个函数.then(()=>{ 后一个函数(); })
下面我们将上面的那个例子改为promise的形式,它可以将不确定层级的函数抹平成一级。.then的作用不是调用的意思,它是把后面的参数给Promise对象。这里的callback在后面的介绍中相当于resolve函数。
function conn(){
console.log("连接数据库...");
return new Promise((callback)=>{
setTimeout(callback,3000);
});
}
function query(){
console.log("查询数据...");
return new Promise((callback)=>{
setTimeout(callback,5000);
});
}
function response(){
console.log("返回响应...");
return new Promise((callback)=>{
setTimeout(callback,2000);
})
}
conn()//往下调用的时候是平级的
.then(()=>query())
.then(()=>response())
.then(()=>console.log("查询出结果"))
Promise可以接受2参数来进行错误处理: 分2步
1). 在前一个函数内
return new Promise(resolve,reject)=>{
如果没出错,执行resolve
如果出错,执行reject("错误提示")
})
2). 在调用前一个函数时,其中.then执行的是resolve的内容,.catch执行的是reject的内容。一旦出错后面调用的函数都不会执行。
前一个函数.then(()=>{ 下一个函数(); })
.catch((err)=>{ 输出err }
将上面的例子模拟出可以出错的样式,用resolve和reject来进行改写,这里在reject中还传了参数以便出错时知道在哪个函数中出错的。
function conn(){
console.log("连接数据库...");
return new Promise((resolve,reject)=>{
//.then .catch
setTimeout(()=>{
var err=Math.random()<0.3?true:false;
if(!err)
resolve();
else
reject("连接出错");
},3000);
});
}
function query(){
console.log("查询数据...");
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var err=Math.random()<0.3?true:false;
if(!err)
resolve();
else
reject("查询出错");
},5000);
});
}
function response(){
console.log("返回响应...");
return new Promise((resolve,reject)=>{
setTimeout(()=>{
var err=Math.random()<0.3?true:false;
if(!err)
resolve();
else
reject("响应出错");
},2000);
})
}
conn()
.then(()=>query())
.then(()=>response())
.then(()=>console.log("查询出结果"))
.catch((err)=>console.log(err))
很多情况一些框架都支持promise,都不需要开发者去写return promise这段代码,只要会使用.then和。catch的用法即可。