作用域的概念
-
es2015
- 函数作用域
- 全局作用域
-
es6
- 函数作用域
- 全局作用域
- 块作用域(新增,标识:有大括号包围的)
Ps:
1.es6中强制开启了严格模式;
2.let声明的变量只在块作用域中有效,let不能重复声明同一个变量;
3.const声明变量初始化后不能修改常用类型变量值,但可以修改引用类型变量值(因为引用类型变量实际上是指针不变);
解构赋值
数组赋值
例子:
{
let a,b,rest;
[a,b] = [1,2];
console.log(a,b);
}
结果输出:1 2
{
let a,b,rest;
[a,b,...rest] = [1,2,3,4,5,6];
console.log(a,b,rest);
console.log(a,b,rest);
}
结果输出:1 2 [3, 4, 5, 6]
对象赋值
例子:
{
let a,b;
({a,b} = {a:2,b:4});
console.log(a,b);
}
结果输出:2 4
解构赋值默认值
1.没有设置默认值的变量值为undefined
2.默认值设值例子:
{
let a,b,c,rest;
[a,b,c=3] = [1,2];
console.log(a,b,c);
}
结果输出:1 2 3
解构赋值作用
1.变量交换
{
let a = 1;
let b = 2;
[a,b] = [b,a];
console.log(a,b);
}
2.函数返回数组变量,可选择性的获取某个或某几个数值
{
function fn(){
return [1,2,3,4,5];
}
let a,b;
[a,,,b] = fn();
console.log(a,b);
}
输出结果:1 4
3.函数返回数组变量,只获取数组首个值,其他获取剩余数组
{
function fn(){
return [1,2,3,4,5];
}
let a,b;
[a,...b] = fn();
console.log(a,b);
}
输出结果:1 [2,3,4,5]
4.对象变量的赋值方法写法
{
let {a=1,b=5} = {a:3};
console.log(a,b);
}
输出结果:3 5
假设获取后台接口数据,取其中个别数据:
{
let metaData = {
title: 'test',
test: [{
title: 'hello',
description: 'helloworld'
}]
};
let {title:esTitle,test:[{title:cnTitle}]} = metaData;
console.log(esTitle,cnTitle);
}
输出结果:test hello
关于字符串拓展
unicode字符处理
es6能处理大于两个字节的字符,es5会将字符解析成乱码
遍历接口
遍历输出例子:
{
let s = '\u{20bb7}abc';
for(let i=0;i<s.length;i++){
console.log('es5:', s[i]);
}
for(let j of s){ //es6循环遍历能输出大于两个字节的字符
console.log('es6:',j);
}
}
模板字符串
实现模板字符串的例子:
{
let name = 'list';
let info = 'hello world!';
let a = `I am ${name},${info}`;
console.log(a);
}
新增方法(10种)
1.includes(str)
判断字符串中是否包含某字符
2.startsWith(str)
判断是否以某字符为开始
3.endsWith(str)
判断是否以某字符为结束
4.repeat(num)
复制num次字符串
5.padStart(num,str) padEnd(num,str)
补全位数,前面补全或后面补全
例子:
一般常用于处理时间日期等
{
//处理时间,补全位数
console.log('1'.padStart(2,'0')); //固定两位,前面补全
console.log('1'.padEnd(2,'0')); //固定两位,后面补全
}
6.String.raw(str)
帮转义字符转义,实现原转义字符的输出,使用场景不多
*标签模板
例子:
{
let user = {
name:'list',
info:'hello world'
};
console.log(abc `i am ${user.name},${user.info}`); //处理多语言转换
function abc(s,v1,v2){
console.log(s,v1,v2);
return s + v1 + v2;
}
}
数值拓展
新增方法
1.多进制表示方法
0b - 二进制
0o - 八进制
2.Number.isFinite(number) - 用处不大
判断一个数是否有穷
3.Number.isNaN(number) - 用处不大
判断是否非数字
4.Number.isInteger(number) - 常用
判断一个数是否是整数
5.数值必须要在一个范围内才有效
-2的53次方 ~ 2的53次方
Number.MAX_SAFE_INTEGER
Number.MIN_SAFE_INTEGER
6.Number.isSafeInteger(number)
判断一个数是否在有效范围
方法调整
7.Math.trunc(number)
取一个小数的整数部分
8.Math.sign(number)
返回正数还是负数
返回值:1 0 -1 NaN
9.Math.cbrt(number)
求立方根
数组拓展
Array.of()
把一组数据变量转变成数据类型
不能使用forEach遍历
Array.from()
- 把一个集合真正转化成一个数组
- 对一个数组中的元素遍历操作
{
console.log(Array.from([1,3,5],function(item){return item*2}));
}
[].fill(repalce,start,end)
替换数组中的元素值
repalce - 替换内容
start - 开始位置
end - 结束位置
['1','c','ks'].keys() - 使用频率较高
获取数组的索引集合
['1','c','ks'].values() - 使用频率较高
获取数组的值集合
PS:浏览器使用有兼容性问题,必须要引入babel-polyfill插件
['1','c','ks'].entries() - 使用频率较高
获取数组的索引,值的集合
例子:
for(let [index,value] of ['1','c','ks'].entries()){
console.log('values',index,value);
}
[].find(function(){})
返回第一个符合条件的数组成员
[].findIndex(function(){})
返回第一个符合条件的数组成员的索引
[].includes(number)
数组中是否包含某个值
函数拓展
默认参数值
例子:
{
function test(x = 'aaa', y = 'world'){
console.log('默认值',x,y);
}
test();
test('hello');
test('hello','kill');
}
输出结果:
aaa world
hello world
hello kill
作用域的概念
例子说明:
{
let x='test';
function test2(x,y=x){
console.log('作用域',x,y);
}
test2('kill');
test2();
}
块作用域定义了一个x块变量,值为test;
function作用域重新定义了一个局部变量x,y等同于局部函数作用域变量x,值为undefined
test2('kill')执行,赋值x为kill,所以y的值为kill
输出结果为作用域 kill kill
{
function test3(...arg){
for(let v of arg){
console.log('rest',v);
}
}
test3(1,2,3,4,'a');
}
...arg表示把离散的值转化成一个数组,适用于参数未知的情况
{
console.log(...[1,2,4]);
console.log('a',...[1,2,4]);
}
...[1,2,3]写法表示把数组转化成离散值
{
function tail(x){
console.log('tail',x);
}
function fx(x){
return tail(x)
}
fx(123)
}
一个函数的内部返回另一个函数,称为伪调用
一般用在循环遍历函数或嵌套函数中,提升性能
对象拓展
简洁表示法
{
// 简洁表示法
let o=1;
let k=2;
let es5={
o:o,
k:k
};
let es6={
o,
k
};
console.log(es5,es6);
let es5_method={
hello:function(){
console.log('hello');
}
};
let es6_method={
hello(){
console.log('hello');
}
};
console.log(es5_method.hello(),es6_method.hello());
}
属性表达式
{
// 属性表达式
let a='b';
let es5_obj={
a:'c',
b:'c'
};
let es6_obj={
[a]:'c'
}
console.log(es5_obj,es6_obj);
}
其中es6——obj中的[a]中的a可以是个表达式或变量
新增的API
{
// 新增API
console.log('字符串',Object.is('abc','abc'),'abc'==='abc');
console.log('数组',Object.is([],[]),[]===[]);
console.log('拷贝',Object.assign({a:'a'},{b:'b'}));
let test={k:123,o:456};
for(let [key,value] of Object.entries(test)){
console.log([key,value]);
}
}
Object.is - 判断是否完全相等
Object.assign - 拷贝对象,返回拷贝后的对象,是浅拷贝,如果对象有继承或有引用类型,继承或引用的具体内容不会拷贝
Symbol
声明一个独一无二的变量
1.Symbol()
2.Symbol.for('a1')
{
let a1=Symbol.for('abc');
let obj={
[a1]:'123',
'abc':345,
'c':456
};
console.log('obj',obj);
for(let [key,value] of Object.entries(obj)){ //仅能拿到对象中非Symbol类型的属性
console.log('let of',key,value);
}
Object.getOwnPropertySymbols(obj).forEach(function(item){ //getOwnPropertySymbols取对象的所有Symbol类型的属性
console.log(item);
console.log(obj[item]);
})
Reflect.ownKeys(obj).forEach(function(item){ //不仅能拿到Symbol类型的也能拿到非Symbol类型的属性
console.log('ownkeys',item,obj[item]);
})
}
首先定义Symbol类型的属性,使用对象的表达式属性方式;
let [key,value] of Object.entries(obj)
仅能获取对象的非Symbol类型的属性
Object.getOwnPropertySymbols(obj)
仅能获取对象的Symbol类型的属性
Reflect.ownKeys(obj)
不仅能拿到Symbol类型的也能拿到非Symbol类型的属性
数据结构
Set的用法
表示一个集合。
{
let list = new Set();
list.add(5);
list.add(7);
console.log('size',list.size);
}
{
let arr = [1,2,3,4,5];
let list = new Set(arr);
console.log('size',list.size);
}
{
let list = new Set();
list.add(1);
list.add(2);
list.add(1);
console.log('list',list); //输出list Set(2) {1, 2}
let arr=[1,2,3,1,'2'];
let list2=new Set(arr);
console.log('unique',list2);
}
1.set中增加元素要用add方法;
2.获取集合中的元素个数用size属性;
3.set可以把数组转化成集合数据类型;
4.set中的元素都是唯一的,add相同的元素不会报错,只会不起效,可用于去重功能;
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
console.log('has',list.has('add'));
console.log('delete',list.delete('add'),list);
list.clear();
console.log('list',list);
}
list.has() - 集合中是否含有元素
list.delete() - 删除元素
list.clear() - 清空集合
list.add() - 增加元素
Set实例的遍历
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
for(let key of list.keys()){
console.log('keys',key);
}
for(let value of list.values()){
console.log('value',value);
}
for(let [key,value] of list.entries()){
console.log('entries',key,value);
}
list.forEach(function(item){console.log(item);})
}
Set集合的遍历跟数组的遍历方法类似,keys和value值相同,都是元素值
keys() - 取集合的索引
values() - 取集合的值
entries() - 取集合的索引和值
WeakSet
1.只能放对象,不能放其他基本类型;
2.没有垃圾回收机制,没有clear方法,没有set属性,不能遍历;
Map的用法
1.添加元素Map.set()和获取Map中key索引对应值Map.get()
{
let map = new Map();
let arr=['123'];
map.set(arr,456);
console.log('map',map,map.get(arr));
}
map中设置key为一个数组;
map.get获取索引对应值;
2.map创建的方式
{
let map = new Map([['a',123],['b',456]]);
console.log('map args',map);
console.log('size',map.size);
console.log('delete',map.delete('a'),map);
console.log('clear',map.clear(),map);
}
等同于
{
let map2 = new Map();
map2.set('a',123);
map2.set('b',456);
console.log('map2 args',map2);
console.log('size',map2.size);
console.log('delete',map2.delete('a'),map2);
console.log('clear',map2.clear(),map2);
}
delete,clear方法与set类似。
WeakMap
与Map的区别:
1.只能放对象,不能放其他基本类型;
2.没有垃圾回收机制,没有clear方法,没有set属性,不能遍历;
map-set与数组和对象的区别
Map和Array的相比
{
//数据结构横向对比,增删改查
let map = new Map();
let array = [];
//增
map.set('t',123);
array.push({t:123});
console.info('map-array',map,array);
//查
let map_exist = map.has('t');
let array_exist = array.find(item=>item.t);
console.info('map-array',map_exist,array_exist);
//改
map.set('t',2);
array.forEach(item=>item.t?item.t=2:'');
console.info('map-array',map,array);
//删
map.delete('t');
let index = array.findIndex(item=>item.t);
array.splice(index,1);
console.info('map-array',map,array);
}
Set和Array的相比
{
//Set和Array的对比
let set = new Set();
let array = [];
//增
let obj = {t:1};
set.add(obj);
array.push(obj);
console.info('set-array',set,array);
//查
let set_exist = set.has(obj);
let array_exist = array.find(item=>item.t);
console.info('set-array',set_exist,array_exist);
//改
set.forEach(item=>item.t?item.t=2:'');
array.forEach(item=>item.t?item.t=2:'');
console.info('set-array',set,array);
//删
set.forEach(item=>item.t?set.delete(item):'');
let index = array.findIndex(item=>item.t);
array.splice(index,1);
console.info('set-array',set,array);
}
map,set和object的对比
{
//map,set,object对比
let item = {t:1};
let map = new Map();
let set = new Set();
let obj = {};
//增
map.set('t',1);
set.add(item);
obj['t'] = 1;
console.info('map-set-array',map,set,obj);
//查
console.log({
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.info('map-set-array',map,set,obj);
//删
map.delete('t');
set.delete(item);
delete obj['t'];
console.info('map-set-array',map,set,obj);
}
综上总结:
整个数据开发过程中涉及数据结构,能使用map就不使用数组,特别是不使用数组;
优先使用map,如果对数据要求比较高,对数据结构存储的唯一性要求,要考虑使用set,反正优先使用map和set,放弃传统的数组和object;
Proxy和Reflect
Proxy - 代理
1.有一个原始数据对象
2.通过Proxy拦截,代理生成一个新的处理后的对象数据返回
- get()
当对象获取属性时触发,进行拦截代理
-set()
当对象设置属性时触发,进行拦截代理
has()
拦截key in object操作
返回值是true和falsedeleteProperty()
拦截删除属性操作ownKeys()
拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
2.Reflect - 反射
Reflect.get(obj,'time')
读取对象的属性Reflect.set(obj,'name','mukewang')
设置对象的属性Reflect.has(obj,'name')
判断对象是否存在该属性
Proxy使用场景例子:
{
function validator(target,validator){
return new Proxy(target,{
_validator:validator,
set(target,key,value,proxy){
if(target.hasOwnProperty(key)){
let va = this._validator[key];
if(!!va(value)){
return target[key];
}else{
throw Error(`不能设置${key} 到 ${value}`);
}
}else{
throw Error(`${key} 不存在`);
}
}
});
}
const PersonValidator = {
name(val){
return typeof val === 'string';
},
age(val){
return typeof val === 'number' && val >= 18;
}
}
class Person{
constructor(name,age){
this.name = name;
this.age = age;
return validator(this,PersonValidator);
}
}
let p = new Person('lincimy',30);
console.log(p);
p.a = 1;
p.name = 12;
}
总结:使用Proxy,通过代理的方式把条件和对象本身也就是业务逻辑分离,在后续代码维护和健壮性有很好的作用。
类和对象
基本用法
{
// 基本定义和生成实例
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
let v_parent=new Parent('v');
console.log('构造函数和实例',v_parent);
}
继承
class Child extends Parent{
}
如果子类要传递参数,对父类默认值进行覆盖,使用super方法:
{
// 继承传递参数
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
class Child extends Parent{
constructor(name='child'){
super(name); //要覆盖父类的什么参数,就在这里传参
this.type='child'; //自定义参数要在super之后,否则报错
}
}
console.log('继承传递参数',new Child('hello'));
}
getter和setter
{
// getter,setter
class Parent{
constructor(name='mukewang'){
this.name=name;
}
get longName(){ //longName属性获取
return 'mk'+this.name
}
set longName(value){ //longName属性设置
this.name=value;
}
}
let v=new Parent();
console.log('getter',v.longName);
v.longName='hello';
console.log('setter',v.longName);
}
静态方法 - 加了static
静态方法是通过类去调用,而不是通过类的实例去调用
{
// 静态方法
class Parent{
constructor(name='mukewang'){
this.name=name;
}
static tell(){
console.log('tell');
}
}
Parent.tell();
}
静态属性
静态属性在ES6中没有关键词
{
// 静态属性
class Parent{
constructor(name='mukewang'){
this.name=name;
}
static tell(){
console.log('tell');
}
}
Parent.type='test';
console.log('静态属性',Parent.type);
}
在类定义完之后,直接在类上定义,而不是实例,之后读取就是类的静态属性;
Promise - 解决异步操作问题
原始用法 - 使用callback回调函数
缺点:如果回调函数过多,导致无法清晰逻辑操作的执行顺序
使用Promise例子:
{
let ajax=function(){
console.log('执行2');
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 1000);
})
};
ajax().then(function(){
console.log('promise','timeout2');
})
}
与ES5的回调方法在结果上来说是一致的,但在代码的可读性和维护性上是非常有优势的。
Promise中捕获错误:
{
let ajax=function(num){
console.log('执行4');
return new Promise(function(resolve,reject){
if(num>5){
resolve()
}else{
throw new Error('出错了')
}
})
}
// ajax(6).then(function(){
// console.log('log',6);
// }).catch(function(err){
// console.log('catch',err);
// });
ajax(3).then(function(){
console.log('log',3);
}).catch(function(err){
console.log('catch',err);
});
}
Promise高级用法
1.Promise.all()
{
// 所有图片都加载完再添加到页面
function loadImg(src){
return new Promise((resolve,reject)=>{
let img = document.createElement('img');
img.src = src;
img.onload = function(){
resolve(img);
};
img.onerror = function(err){
reject(err);
};
});
}
function showImgs(imgs){
imgs.forEach(function(item){
document.body.appendChild(item);
});
}
Promise.all([
loadImg('https://upload.jianshu.io/admin_banners/web_images/3995/97984254f407672e5ca56e4aa66ad9e4330e3e0b.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540'),
loadImg('https://upload.jianshu.io/admin_banners/web_images/3989/986f00c9a5ab4406b143b8985f925258bba06b9d.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540'),
loadImg('https://upload.jianshu.io/admin_banners/web_images/3990/4a0679fb9dde465cd13797299699be750382b04c.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540')
]).then(showImgs);
}
Promise.all中元素promise全部返回有所变化,promise.all总元素才会有所触发,触发then中操作执行;
2.Promise.race()
{
// 所有图片都加载完再添加到页面
function loadImg(src){
return new Promise((resolve,reject)=>{
let img = document.createElement('img');
img.src = src;
img.onload = function(){
resolve(img);
};
img.onerror = function(err){
reject(err);
};
});
}
function showImgs(img){
let p = document.createElement('p');
p.appendChild(img);
document.body.appendChild(p);
}
Promise.race([
loadImg('https://upload.jianshu.io/admin_banners/web_images/3995/97984254f407672e5ca56e4aa66ad9e4330e3e0b.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540'),
loadImg('https://upload.jianshu.io/admin_banners/web_images/3989/986f00c9a5ab4406b143b8985f925258bba06b9d.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540'),
loadImg('https://upload.jianshu.io/admin_banners/web_images/3990/4a0679fb9dde465cd13797299699be750382b04c.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540')
]).then(showImgs);
}
Promise.race中元素只要有一个状态发生了变化,就会触发后续操作,即.then中的逻辑操作;
Iterator和for...of循环
自定义部署对象的Iterator:
{
let obj={
start:[1,3,2],
end:[7,9,8],
[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);
}
}
Generator - 异步操作
基本定义用法:
{
// genertaor基本定义
let tell=function* (){
yield 'a';
yield 'b';
return 'c'
};
let k=tell();
console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
}
使用场景:
1.抽奖次数限制
{
let draw = function(count){
//具体抽奖逻辑
console.log(`剩余${count}次`);
}
let residue = function* (count){
while(count>0){
count--;
yield draw(count);
}
}
let star = residue(5);
let btn = document.createElement('button');
btn.id = 'start';
btn.textContent = '抽奖';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click',function(){
star.next()
},false);
}
2.长轮询 - 服务端的某个数据状态定期变化,前端展示需要定期取状态
Decorator - 修饰器
1.是个函数;
2.修改行为;
3.修改类的行为;
{
let readonly=function(target,name,descriptor){
descriptor.writable=false;
return descriptor
};
class Test{
@readonly
time(){
return '2017-03-11'
}
}
let test=new Test();
// test.time=function(){
// console.log('reset time');
// };
console.log(test.time());
}
当设置类中的函数为不可修改时,当实例尝试对类中的方法进行修改,即报错~
模块化
1.模块的引入 - import
2.模块的带出 - export
let A=123;
let test=function(){
console.log('test');
}
class Hello{
test(){
console.log('class');
}
}
export default {
A,
test,
Hello
}
import lesson from './class/lesson17';
console.log(lesson.A,lesson.test,lesson.Hello);