let 和 const
作用域的概念
- 全局作用域
- 函数作用域
- 块作用域
如何使用let和const
使用let
1、形成块级作用域
2、es6下强制开启严格模式,而在es5下,需要"use strice"才能开启严格模式;
const
1、声明的常量不能修改;但是对象可以修改,因为对象是对地址的引用,我们可以在内存空间中更改对象
function last() {
const k={
a:1
};
k.a=123;
console.log(k)
}
2、声明的时候,必须赋值;
3、const也有块作用域的概念;
Symbol
Symbol的概念:用这种方式声明的数据类型,永远是独一无二的值;
Symbol的作用:避免属性名相同的问题
注意:对象中,有用到symbol做key值的话,是取不到的;即let...of只能拿到非Symbol对象的值;
let a1=Symbol.for('abc');
let obj={
[a1]:'123',
'abc':345,
'c':456
};
console.log(obj)
for(let [key,value] of Object.entries(obj) ){
console.log('let of',key,value)
}
解决办法:通过以下代码可以取到Symbol的值;Object.getOwnPropertySymbols可以拿到Symbol对象的值;
Object.getOwnPropertySymbols(obj).forEach(function (item) {
console.log(obj[item])
})
返回了所有key和value值;通过es6最增的Reflect.ownKeys(obj)
Reflect.ownKeys(obj).forEach(function (item) {
console.log(item,obj[item])
});//返回了所有key和value值;
Set的用法
Set的用法:set数据类型的元素,必须是唯一的;添加重复的元素不会报错,只是不会生效;
他在转换元素的时候,不会进行数据类型的隐式转换;
可以用它来去重;
let arr=[1,2,3,4,2,1,2,3,2];
let list=new Set(arr);
console.log(list)
Set()实例的方法:
1、add() 添加元素
2、delete() 移出
3、clear() 清空
4、has() 判断元素中是否有某个内容
let arr=['add','delete','clear','has'];
let list=new Set(arr);
console.log(list.has('add'))
console.log(list.delete('add'),list)
console.log(list.clear(),list)
Set实例的遍历
//1:遍历属性名
for(let key of list.keys()){
console.log(key)
}
//2:遍历属性值
for(let value of list.values()){
console.log(value)
}
//3:遍历属性名和属性值
for(let [key,value] of list.entries()){
console.log(key,value)
}
Set实例的 forEach
list.forEach(function (item) {
console.log(item)
})
WeakSet的用法
let weakList=new WeakSet()
WeakSet和set的区别:
1、WeakSet和set支持的数据类型不一样;
2、WeakSet中的对象都是若引用;不会检测地址是否被垃圾回收掉;
3、他的属性,没有size属性,没有clear方法,不能遍历;
Map的用法
map的属性名可以是任意数据类型;
map增加值,用set,获取值用get
map的两种写法
//第一种写法:
let map=new Map();
let arr=['123'];
map.set(arr,456);
console.log('map',map,map.get(arr))
//第二种写法
let map=new Map([['a',123],['b',456]])
console.log(map)
map常用的属性值和方法
map.size 长度
set设置,get获取
delete() 删除; clear()清空
WeakMap的用法
weakMap和map的区别:
前者接收的值只能是对象
他没有set属性,不能使用clear()
不能遍历; 跟weakSet和set的区别一样;
数据结构-和数组的对比
Map与Array的对比,从增删改查出发
//数据结构横向对比,增,查,改,删
let map=new Map();
let ary=[];
//增
map.set('t',1);
ary.push({t:1});
//console.info('map-array',map,ary)
//查
let map_exits=map.has('t');
let ary_exites=ary.find(item=>item.t);
/*console.log(map_exits) //返回true,表示存在
console.log(ary_exites) //返回当前对象*/
//改
map.set('t',2);
ary.forEach(item=>item.t?item.t=2:'');
console.log(map,ary)
//删除
map.delete('t');
let index=ary.findIndex(item=>item.t);
ary.splice(index,1);
console.log(map,ary)
Set与Array的对比
let set=new Set();
let ary=[];
let item={t:1};
//增加
set.add(item);
ary.push({t:1})
console.log(set,ary)
//查
let set_exist=set.has(item);
let ary_exist=ary.find(item=>item.t)
console.log(set_exist,ary_exist)
//改
set.forEach(item=>item.t?item.t=2:'');
ary.forEach(item=>item.t?item.t=2:'');
//删
set.forEach(item=>item.t?set.delete(item):'');
let index=ary.findIndex(item=>item.t)
ary.splice(index,1)
console.log(set,ary)
数据结构- 和对象Object的对比
###Map,Set与Object的对比
//map,set,object对比
let item={t:1};
let set=new Set();
let map=new Map();
let obj={};
//增
map.set('t',1);
set.add(item);
obj['t']=1;
console.log('map-set-obj',map,set,obj)
//查
console.info({
map_exist:map.has('t'),
set_exist:set.has(item),
obj_exist:'t' in obj
})
//改
map.set('t',2);
item.t=2;
obj['t']=2;
console.log('map-set-obj',map,set,obj)
//删
map.delete('t')
set.delete(item)
delete obj['t'];
console.log('map-set-obj',map,set,obj)
小总结:数据开发中,涉及数据结构,能使用map,不使用数组;如果对数据要求比较高,比如数据唯一性,考虑使用
类
类的基本定义和生成实例: class
class Parent{
constructor(name='leilei'){
this.name=name;
}
}
let v_parent=new Parent('ymy');
console.log(v_parent.name)
类的继承: extends
class Parent{
constructor(name='leilei'){
this.name=name;
}
}
class Child extends Parent{
//继承:子类怎么在自己的构造函数中传递参数
constructor(name='child'){
super(name);//如果不传参,子类使用的是父类默认的参数;super一定放在构造函数的第一行;
this.type='child';
}
}
console.dir(new Child('hello'))
类中的getter和setter
分别用来获取属性和设置属性
class Parent{
constructor(name='leilei'){
this.name=name;
}
get longName(){
return 'ymy '+this.name;
}
set longName(value){
this.name=value;
}
}
// 创建实例
let p1=new Parent();
console.log(p1.longName) //获取属性
p1.longName='tangtang'; //设置属性
给类中添加静态方法 static
注意:static属性只能用来设置类的静态方法,不能用来设置类的静态属性
类的静态属性只能通过: 类.key=value;来设置
类的静态方法,只有类能使用,实例不能使用,实例只能使用原型上的属性和方法;
class Parent{
constructor(name='leilei'){
this.name=name;
}
//设置静态方法
static tell(){
console.log('tell');
}
}
Parent.sex='gril'; //设置类的静态属性
Parent.tell() //调用类的静态方法;
es6 Iterator 和 for...of 循环
什么是Iterator接口
ES6中内置了一些Symbol,其中最重要的一个恐怕就是Symbol.iterator了,相当于迭代器的接口,只有对象里有这个symbol的属性,才可以认为此对象是可迭代的。
我们先看一下规范中对这个symbol的描述:
A method that returns the default Iterator for an object. Called by the semantics of the for-of statement.
js中哪些对象里实现了这个接口呢?常见的有Array,String,arguments,还有后面高级的数据结构,如Set,Map等。
Iterator的基本用法
for...of
for...of循环的过程,其实就是通过不断调用Iterator接口来达到这种形式;
也就是说,不同的数据结构,通过for...of这种统一的形式,来达到读取不同数据结构的目标;但是背后的Iterator接口其实不同;
数组自动帮我们带了iterator接口
let arr=['hello','world'];
let map=arr[Symbol.iterator]();
console.log(map.next());
console.log(map.next());
console.log(map.next());
Object {value: "hello", done: false};done代表,是否做完了所有操作,true:代表是; false:代表无,还有其他操作;
Object对象不是一个iterator,那么如何把它变成一个iterator呢?
let obj={
start:[1,3,2],
end:[7,8,9],
[Symbol.iterator](){
let self=this;
let index=0;
let arr=self.start.concat(self.end);
let len=arr.length;
return {
next (){
if(index<len){
return {
value:arr[index++],
done:false
}
}else{
return {
value:arr[index++],
done:true
}
}
}
}
}
}
for(let key of obj){
console.log(key);
}
let...of的使用
let arr=['hello','world'];
for(let value of arr){
console.log(value);
}
Generator
基本概念
Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。
执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
形式上Generator函数是一个普通函数,但是有两个特征。
function关键字与函数名之间有一个星号*;
函数体内部使用yield语句,定义不同的内部状态(yield语句在英语里的意思就是“产出”)。
let tell=function* () {
yield 'a';
yield 'b';
yield 'c';
};
let k=tell();
console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
上面代码定义了一个Generator函数tell,它内部有三个yield语句'a','b'和'c' ; 即该函数有三个状态:'a','b'和'c'语句(结束执行)。
总结一下,调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。true:表示遍历结束,false:表示遍历没结束;
yield
需要注意的是,yield语句后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
function* gen() {
yield 123 + 456;
}
上面代码中,yield后面的表达式123 + 456,不会立即求值,只会在next方法将指针移到这一句时,才会求值。
yield语句注意事项
yield语句不能用在普通函数中,否则会报错。
yield语句如果用在一个表达式之中,必须放在圆括号里面。
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
yield语句用作函数参数或赋值表达式的右边,可以不加括号。
foo(yield 'a', yield 'b'); // OK
let input = yield; // OK
暂缓执行函数
Generator函数可以不用yield语句,这时就变成了一个单纯的暂缓执行函数。
function* f() {
console.log('执行了!')
}
var generator = f();
setTimeout(function () {
generator.next()
}, 2000);
上面代码中,函数f如果是普通函数,在为变量generator赋值时就会执行。但是,函数f是一个Generator函数,就变成只有调用next方法时,函数f才会执行。
Generator 与 Iterator接口的关系
任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。
由于Generator函数就是遍历器生成函数,因此可以把Generator赋值给对象的Symbol.iterator属性,从而使得该对象具有Iterator接口。
//generator对象的新应用:给obj对象部署Iterator;
let obj={};
obj[Symbol.iterator]=function* () {
yield 1;
yield 2;
yield 3;
};
for(let value of obj){
console.log(value);
}
用Generator写抽奖
let draw=function (count) {
//具体抽奖逻辑
console.info(`剩余${count}抽奖次数`);
};
let residue=function* (count) {
while(count>0){
count--;
yield draw(count);
}
};
let star=residue(5);
let btn=document.createElement('button');
btn.innerHTML='点击抽奖';
document.body.appendChild(btn);
btn.onclick=function () {
star.next();
}
前端定时的去接收服务端的变化
两种办法:1)websocket-兼容性不好 ; 2)常轮询-看如下代码
let ajax=function* () {
yield new Promise((resolve,reject)=>{
setTimeout(function () {
resolve({code:0})
},200)
})
};
let pull=function () {
let generator=ajax();
let step=generator.next();
//拿回后台的数据
step.value.then(function (d) {
if(d.code!=0){
setTimeout(function () {
console.info('wait');
pull();
},1000)
}else{
console.log(d);
}
})
};
pull();