【自学笔记】适合新手入门的ES6基础讲解

第一节

1.let、const

作用域:ES5中作用域有函数作用域、全局作用域、局部作用域;ES6中新增了块级作用域。

let和const中属于块级作用域。

//ES6中,默认使用的是【use strict】严格模式
function test(){
    let a=1;
    //let a=2;  1. 不可以对已经声明的变量重新声明
    console.log(a);// 1
    
    for(let i=0;i<3;i++){  //2. i的作用域只在当前的循环有效
        console.log(i);// 0 1 2
    }
    console.log(i);//报错,i在当前作用域找不到
}
console.log(a);//报错 
function test(){
    const PI=3.1415926;//1. const通常用来声明静态变量
    //PI=555;   2. 当const声明的变量是一个具体数值,不可以对其进行赋值修改
    
    const PI={a:1};  //该出声明的是一个引用类型 
    PI.a=3;//可以对其修改 
    //因为PI指向的是{a:1}的地址,不能改变的是地址。所以可以对对象里面的值可以改变
}

第二节

1.解构赋值
什么是解构赋值?

左边的结构和右边的结构一一对应,进行赋值。

解构赋值的分类

数组、对象、字符串、布尔值、函数参数、数值等解构赋值

{
    let a,b,rest;
    [a,b]=[1,2];//数组解构赋值
    console.log(a,b);//1 2
}
{
    let a,b,c;
    [a,b,c=3]=[1,2];//c的默认值是3
    console.log(a,b,c)//1 2 3
}
{
    //使用场景:1.特别适用于变量交换
    let a1=1,b1=2;
    [a1,b1]=[b1,a1];
    console.log(a1,b1);// 2 1
}

{
    //使用场景:2.获取函数返回的值
    function f(){
        return [1,2];
    }
    let a2,b2;
    console.log(a2,b2);//1 2
}

{
    //使用场景:3.选择性获取某个值
    function f(){
        return [1,2,3,4,5];
    }
    let a,b,c;
    [a,,,b]=f();//逗号之间的便是省略的
    console.log(a,b);//1 4
}

{
	let a,b,rest;
	[a,b,...rest]=[1,2,3,4,5,6];//不确实数值存入rest中
    console.log(a,b,rest);//a=1 b=2 rest=[3,4,5,6]
}
{
    let a,b;
    ({a,b}={a:1,b:2});//对象解构赋值
    console.log(a,b);// 1 2
}

{
    let o={p:2,q:true};
    let {p,q}=o;
    console.log(o)//2 true
}

{
    let {a=10,b=5}={a:3};//对a进行了重新赋值 而 b没有
    console.log(a,b);//3 5
}

//使用场景:
{
    let metaDta={
        title:'abc',
        test:[{
            title:'test',
            desc:'description'
        }]
    }
    let {title:esTitle,test:[{title:cnTitle}]}=metaData;
    console.log(esTitle,cnTitle);//abc test
}

第三节

1.正则扩展
正则新增特性:

这节介绍与ES5的比较,主要有构造函数的变化、正则方法的扩展、u修饰符、y修饰符、s修饰符。

//构造函数的变化
{
    //ES5 定义正则的两种方法
	let regex=new RegExp('xyz','i');
    let regex2=new RegExp(/xyz/i);
    console.log(regex.test('xyz123'),regex2.test('xyz123'));
    
    //ES6的写法
   let regex3=new RegExp(/xyz/ig,'i');
   console.log(regex3,flags);//i ,flags是新加的是获取第二个参数的。
   // ES6中第一个参数允许是正则表达式的方式,第二个参数在去填写修饰符,后边的会覆盖前面的修饰符。
}

//y修饰符   g\y都是全局匹配
{
    let s='bbb_bb_b';
    let a1=/b+/g;//es5  g修饰符是从上一次匹配的位置继续寻找,不要求下一次匹配的字符是符合的,只要有符合的就可以。
    let a2=/b+/y;//es6   y修饰符则要求,下一次不许要符合要求的可以成功
    console.log('one',a1.exec(s),a2.exec(s));//结果一样
    
    console.log(a1.sticky,a2.sticky);//检查是否开启y修饰符
}

//u修饰符 
//1.字符串中有大于两个字节的字符,则要加u修饰符
//2.在es6中 ‘.’ 并不能匹配任何字符(前提必须小于两个字符长度)
{
    //没加u修饰符的把'\uD83D\uDC2A'当做两个字母匹配,而加u修饰符的当做一个字母
    console.log(/^\uD83D/.test('\uD83D\uDC2A'));//true
    console.log(/^\uD83D/u.test('\uD83D\uDC2A'));//false
    
    // /\u{61}/表示Unicode编码,不加u是无法识别的
    console.log(/\u{61}/.test('a'));//false
    console.log(/\u{61}/u.test('a'));//true
}

第四节

1.字符串扩展
字符串新增特性:

Unicode表示法、遍历接口、模板字符串、新增方法(10种)

{
    //Unicode表示方法
    console.log('a',`\u0061`);//a a
    console.log('s',`\u{20BB7}`);//𠮷 unicode的表示法
}

{
    let s='𠮷';
    console.log(s.length);//2
    console.log(s.charAt(0));//乱码
    console.log(s0.charCodeAt(0).toString(16));//d842
    console.log(s0.charCodeAt(1).toString(16));//dfb7
    console.log(s.codePointAt(0).toString(16));//20bb7
    console.log(s.codePointAt(1).toString(16));//dfb7
}

//区别:能不能处理大于两个字节的Unicode字符
{
    console.log(String.fromCharCode("0x20bb7");//es5  乱码
    console.log(String.fromCodePoint("0x20bb7");//es6  𠮷
}

//遍历接口
{
    let str='\u{20bb7}abc';
    for(let i=0;i<str.length;i++){
        console.log('es5',str[i]);// 乱码 乱码 a b c
    }
    for(let code of str){
        console.log('es6',code);//𠮷 a b c
    }
}

//判断一个字符串是否包含某个字符,是否是某个字符开始的或结尾的
{
    let str="string";
    console.log(str.includes("r"));//true
    console.log(str.startsWith("str"));//true
    console.log(str.endsWith("ng"));//true
}

{
    let str="abc";
    console.log(str.repeat(2));//abcabc 重复n次
}

//模板字符串
{
    let name="list";
    let info="hello world";
    let m=`i am ${name},${info}`;
    console.log(m);// i am list,hello world
}

//es7的草案  补全功能
{
    console.log('1',padStart(2,'0'));//01
    console.log('1',padEnd(2,'0'));//10
}

//标签模板 1.
{
    let user={
        name:'list',
        info:'hello world'
    };
    abc`i am ${user.name},${user.info}`;
    function abc(s,v1,v2){
        console.log(s,v1,v2);
    }
}

第五节

1.数值扩展
数值处理新增特性

新增方法、方法调整

{
    console.log(0b11110111);//  二进制的表示方法
    console.log(0o767);//八进制的表示方法
}

{
    //Number.isFinite();
    console.log('15',Number.isFinite(15));//true
    console.log('NaN',Number.isFinite(NaN));//false
    console.log('1/0',Number.isFinite(1/0));//false
    console.log('NaN',Number,isNaN(NaN));//true
    console.log('0',Number.isNaN(0));//false
}

//判断一个数是否是整数
{
    console.log('25',Number.isInteger(25));//true
    console.log('25.0',Number.isInteger(25.0));//true
    console.log('25.1',Number.isInteger(25.1));//false
    console.log('25.1',Number.isInteger('25.1'));//false  必须是数值
}

//检测一个是否符合是一个数字
{
    console.log(Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER);
    console.log('10',Number.isSafeInteger(10));//true
    console.log('a',Number.isSafeInteger('a'));//false
}

//取整
{
    console.log(4.1,Math.trunc(4.1));//4
    console.log(4.9,Math.trunc(4.9));//4
}

//检测一个数字是正数还是负数
{
    console.log('-5',Math.sign(-5));//-1
    console.log('0',Math.sign(0));//0
    console.log('5',Math.sign(5));//1
    console.log('50',Math.sign(50));//1
    console.log('foo',Math.sign('foo'));//NaN
}

第六节

1.数组的扩展
数组新增特性

Array.from、Array.of、copyWithin、find\findIndex

fill、entries\keys\values、includes

//Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。
{
    let arr= Array.of(3,4,7,9,11);
    console.log('arr=',arr);//arr=[3,4,7,9,11]
    
    let empty=Array.of();
    console.log('empty=',empty);//empty=[]
}

//转换成数组
{
    console.log(Array.from([1,2,3],x => x * 2));//[2 4 6]
    console.log(Array.from("foo"));//["f","o","o"]
}

//填充数组
{
    console.log([1,'a',undefined].fill(7)]);//Array[7,7,7]
    console.log(['a','b','c'].fill(7,1,3));//['a',7,7]  fill(替换值,起始位置,替换的个数)
}

//遍历相关函数
{
    for(let index of ['1','2','ds'].keys()){
        console.log(index);// 0 1 2
    }
    //有兼容问题  需要使用babel插件来解决
    for(let value of ['1','2','ds'].values()){
        console.log(value);//1 2 ds
    }
    for(let [index,value] of ['1','2','ds'].entries()){
        console.log(index,value);//0 1,1 2,3 ds
    }
}

//copyWithin()
{
    console.log([1,2,3,4,5].copyWithin(0,3,4));//[0,2,3,4,5]
}

//find/findIndex  查找
{
    console.log([1,2,3,4,5,6].find(item => item > 3));//4
    console.log([1,2,3,4,5,6].findIndex(item => item > 3));//3 索引
}

{
    console.log([1,2,3,4,5,NaN].includes(5));//true
    console.log([1,2,3,4,5,NaN].includes(NaN));//true
}

第七节

1.函数扩展
函数新增特性

参数默认值、rest参数(…变量名)、扩展运算符、箭头函数、this绑定(定义所在)、尾调用

//参数默认值  如果参数设置了默认值,则后面不可以再定义没有默认值的参数
{
    function test(x,y='world'){
        consoel.log("默认值",x,y);
    }
    test('hello');//默认值 hello world
    test('hello','kill');//默认值 hello kill
    
    //function test(x,y='world',z){  这是错误的
    //    consoel.log("默认值",x,y);
    //}
}

//作用域的问题
{
    let x='test';
    function test(x,y=x){
        console.log(x,y);
    }
    test('kill');//kill kill
    
    let x='test';
    function test(x,y=x){
        console.log(x,y);
    }
    test();//undefined undefined
    
    let x='test';
    function test(c,y=x){
        console.log(x,y);
    }
    test('kill');//kill test
}

//rest参数 之后不能再有其他的参数【把离散的值转成数组】
{
    function test(...arg){
        for(let v of arg){
            console.log(v);
        }
    }
    test(1,2,3,4,'a');//1 2 3 4 a
}

//扩展运算符  
{
    console.log(...[1,2,4]);//1 2 4  把数组转成离散的值
    console.log('a',...[1,2,4]);//a 1 2 4
}

//箭头函数
{
    //函数名 = 函数参数 => 函数返回值
    let arrow = v => v*2;
    console.log(arrow(3));//6
    
    //没有参数使用()代替
    let arrow1 = () => 2;
    console.log(arrow());//2
}

//尾调用  性能优化过程中不断调用其它函数 就使用尾调用
{
    tail = x => x;
    foo = y => tail(y);
    console.log(foo(3));//3
}

第八节

1.对象扩展
函数新增特性

简洁表示法、属性表达式、扩展运算符、Object新增方法

//简洁表示法
{
    let o=1;
    let k=2;
    let es5={
        o:o,
        k:k
    };
    
    let es6={
      	o,
        k
    };
    console.log(es5,es6);//Object{o:1,k:2} Object{o:1,k:2} 
    
    let es5_method={
        hello:function(){
            console.log('hello');
        }
    };
    let es6_method={
        hello(){
            console.log('hello');
        }
    };
    console.log(es5_method.hello(),es6_method.hello());//hello
}

//属性表达式
{
    let a='b';
    let es5_obj={
        a:'c'
    };
    let es6_obj={
        [a]:'c'//[a]是一个表达式 相当于 b:'c'
    }
    console.log(es5_obj);//a:c
    console.log(es6_obj);//b:c
}

//常用的API
{
    //判断两个字符串是否相等
    console.log('字符串',Object.is('abc','abc'),'abc'==='abc');//true true
    //该两个数组虽然的值都是空,但是数组是一个引用类型  指向的地址都不一样,严格意义上它们两个是不相等的。is() 与 === 作用是一样的
    console.log('数组',Object.is([],[]),[]===[]);//false false
    

    //拷贝  为浅拷贝 只会拷贝自身的  引用的无法拷贝
    console.log('拷贝',Object.assign({a:'a'},{b:'b'}));//{a:'a',b:'b'}  合并为一个对象
    
    let test={k:123,p:456};
    for(let [key,value] of Object.entries(test)){
        console.log([key,value]);//["k",123] ["p",456]
    }
}

//扩展运算符  存在兼容问题 babel插件也无法解决
{
    let {a,b,...c}={a:'test',b:'kill',c:'ddd',d:'ccc'};
    c={
        c:'ddd',
        d:'ccc'
    }
}

第九节

1.Symbol的概念

用Symbol声明的变量,是独一无二的。

2.Symbol的作用
{
    //声明
    let a1=Symbol();
    let a2=Symbol();
    console.log(a1===a2);//false
    
    let b1=Symbol.for('b1');//该作用是在作用域内寻找是否符合要求的变量,没有就新增一个,否则就是原来找到的那个值
    let b2=Symbol.for('b1');
    console.log(b1===b2);//true
    
    let c=Symbol.for('abc');
    let obj={
        [c]:"123",
        'abc':345,
        'a':1253
    };
    console.log(obj);
    
    //需要注意的是使用了Symbol定义的key  是拿不到的
    for(let [key,value] of Object.entries(obj)){
        console.log(key,value);//abc 345、a 1253
    }
    
    //获取Symbol定义的
    Object.getOwnPropertySymbols(obj).forEach(function(item){
        console.log(obj[item]);//123
    });
    
    //获取Symbol定义的及非Symbol定义的
    Reflect.ownKeys(obj).forEach(function(item){
        console.log(item,obj[item]);
    })
}

第十节

数据结构

Set的用法、weakset的用法、map的用法、weakmap的用法

{
    //set的用法
    let list = new Set();
    list.add(5);
    list.add(7);
    
    //查看长度
    console.log(list.size);

    let arr=[1,2,3,4,5];
    let list1 = new Set(arr);
    console.log(list1.size);
}

{
    //特性:Set()定义的内容如果是重复的,是不会生效的  作用:去重
    let list = new Set();
    list.add(1);
    list.add(2);
    list.add(1);
    
    console.log(list);//1 2
    
    //注意:不会做数据类型转换
    let arr = [1,2,3,1,5];
    let list1 = new Set(arr);
    console.log(list1);//{1,2,3,5}
    
    //相关方法
    let arr=['add','delete','clear','has'];
    let list2=new Set(arr);
    
    console.log(list2.has('add'));//true
    console.log(list2.delete('add'),list2);//true {'delete','clear','has'}
    list2.clear();//清空内容
}

{
    //Set()的遍历
    let arr=['add','delete','clear','has'];
    let list=new Set(arr);
    
    //可以发现keys与values获取的值都一样
    for(let key of list.keys()){
        console.log(key);// 'add','delete','clear','has'
    }
    for(let value of list.values()){
        console.log(value);// 'add','delete','clear','has'
    }
    for(let [key,value] of list.entries()){
        console.log(key,value);// add add,delete delete,clear clear,has has
    }
    
    list.forEach(item => console.log(item));// 'add','delete','clear','has'
}

{
    //WeakSet() 与 Set()的区别:
    //1.支持的数据类型不一样,weakset只能是对象
   	//2.weakset的对象都是弱引用,它不会检测这个对象有没有在其他地方用过,意味着不会跟垃圾回收机制挂钩。通俗来说,在weakset中添加了一个对象,该对象不是整个值拷贝过来,它是一个地址的引用,而且它也不会检测这个地址是不是被垃圾回收机制给回收掉了。
    //3.没有size方法,clear 不能遍历
    let weakList = new WeakSet();
    let arg={};
    weakList.add(arg);
    weakList.add(2);//出错
    console.log(weakList);//WeakSet [ {} ]
}

{
    //map方法
    let map = new Map();
    let arr =['123'];
    //添加数据 map的key值可以使任意数据类型
    map.set(arr,456);
    
    console.log(map,map.get(arr));//Map { (1) […] → 456 } 456
}

{
    let map = new Map([['a',123],['b',456]]);
    console.log(map);//Map { a → 123, b → 456 }
    
    //map相关属性与方法
    console.log(map.size);
    console.log(map.delete('a'));
    console.log(map.clear(),map);
}

{
    //weakmap与map的区别和weakset与set的区别一样
    let weakmap=new WeakMap();
    let o={};
    weakmap.set(o,123);
    console.log(weakmap.get(o));//123
}

现有数据结构的差异
{
    //map、set与array的区别
    let map=new Map();
    let array=[];
    
    //增
    map.set('t',1);
    array.push({t:1});
    
    //查
    let map_exist=map.has('t');//true
    let array_exist=array.find(item=>item.t);//找到返回对象
    
    //改
    map.set('t',2);
    array.forEach(item=>item.t?item.t=2:'');
    
    //删
    map.delete('t');
    let index=array.findIndex(item=>item.t);
    array.splice(index,1);
}

{
    //set与array的区别
    let set=new Set();
    let array=[];
    
    //增
    set.add({t:1});
    array.push({t:1});
    
    //查
    let set_exist=set.has({t:1});//false  想要返回true 对象需保存在一个变量中
    let array_exist=array.find(item=>item.t);
    
    //改
    set.forEach(item=>item.t?item.t=2:'');
    array.forEach(item=>item.t?item.t=2:'');
    
    //删
    set.forEach(item=>item.t?set.delete(item):'');
    let index=array.findIndex(item=>item.t);
    array.splice(index,1);
}

{
    //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.log(obj,map,set);
    
    //查
    console.info({
        map_exist:map.has('t'),
        set_exist:has(item),
        obj_exist:'t' in obj
    })
    
    //改
    map.set('t',2);
    item.t=2;
    obj['t']=2;
    console.log(obj,map,set);
    
    //删
    map.delete('t');
    set.delete(item);
    delete obj['t'];
}

//使用建议:优先使用map,如果对数据要求比较高,保证数据唯一性使用set,摈弃array object

第十一节

Proxy和Reflect

Proxy和 Reflect的概念:代理与反射

{
    //proxy
    let obj={
        time:'2019-12-21',
        name:'vvv',
        _f:456
    }
    //有时候用户不可以直接操作obj  所以代理一个moniter给用户操作
    let moniter=new Proxy(obj,{
        //拦截对象属性的读取
        get(target,key){
			return target[key].replace('2019','2020');
        },
        //拦截对象设置属性
        set(target,key,value){
            //只能修改name属性
            if(key==='name'){
                return target[key]=value;
            }else{
                return target[key];
            }
        },
        //拦截 key in objet操作
        has(target,key){
            //只能查到name属性
            if(key==='name'){
                return target[key]
            }else{
                return false;
            }
        },
        //拦截delete
        deleteProperty(tartget,key){
            //只能删除包含’_‘的键值对
            if(key.indexOf('_') > -1){
                delete target[key];
                return true;
            }else{
                return false;
            }
        },
        //拦截object.keys()、object.getOwnPropertySymbols、object.getOwnProxyNames
        ownKeys(target){
            //time属性用户不可见
            return Object.keys(target).filter(item=>item!='time');
        }
    });
    console.log('get',moniter.time);
    
    moniter.time='2020';//修改失败
    moniter.name='ggg';//修改成功
    
    console.log('has','name' in moniter,'time' in moniter);//true false
    
    console.log('ownKeys',Object.keys(moniter));  //[ "name", "_f" ]
}

{
    //Proxy拥有的方法 Reflect都有 写法也一样
    let obj={
        time:'2019-12-21',
        name:'vvv',
        _f:456
    };
    
    console.log(Reflect.get(obj,'time'));//'2019-12-21'
    Reflect.set(obj,'name','asdasd');//修改成功
    console.log(Reflect.has(obj,'name'));
}

{
    //使用场景:数据校验  解耦
    let validator = (target,validator) =>{
        return new Proxy(target,{
            //保存配置选项
            _validator:validator,
            set(target,key,value,proxy){
                //判断key是否存在
                if(target.hasOwnProperty(key)){
                    let va=this._validator[key];
                    //如果存在
                    if(!!va(value)){
                        return Reflect.set(target,key,value,proxy)
                    }else{
                        throw Error(`不能设置${key}到${value}`);
                    }
                }else{
                    throw Error(`${key}不存在`)
                }
            }
        })
    }
    
    //设置过滤选项
    const personValidators={
        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,personValidators)
        }
    }
    
    const person=new Person('lilei',30);
    console.info(person);//Object { name: "lilei", age: 30 }
    
    person.name=48;//报错 不能设置
}


第十二节

类和对象

类的概念

基本语法、类的继承、静态方法、静态属性、getter、setter

{
    //基本定义和生成实例
    class Parent{
        constructor(name='song'){
            this.name=name;
        }
    }
    //实例
    let v_parent=new Parent('b');
    console.log('构造函数和实例'v_parent);//{name:'b'}
}

{
    //继承
    class Parent{
        constructor(name='song'){
            this.name=name;
        }
    }
    
    class Child extends Parent{
        
    }
    
    console.log('继承',new Child());//{name:'song'}
}
{
    //继承传递参数
    class Parent{
        constructor(name='song'){
            this.name=name;
        }
    }
    
    class Child extends Parent{
        constructor(name='child'){
            //改变父类的默认值 使用super()方法
            super(name);//如果super为空 就会使用父类的值
            //添加自己的属性,需要定义在super后面
            this.type='child';
        }
    }
    
    console.log('继承传递参数',new Child());//{name:'child',type:'child'}
}

{
    //getter setter
    class Parent{
        //构造函数
        constructor(name='song'){
            this.name=name;
        }
        //这是属性
        get longName(){
            return 'i am '+this.name;
        }
       	set longName(value){
            this.name=value;
        }
    }
    
    let v=new Parent();
    console.log('getter',v.longName);// i am song
    v.longName='hello';
    console.log('setter',v.longName);// i am hello
}

{
    //类的静态方法
    class Parent{
        constructor(name='song'){
            this.name=name;
        }
        //这是方法
        static tell(){
            console.log('tell');
        }
    }
    //调用静态方法 必须使用类来调用
    Parent.tell();//tell
}

{
    //静态属性
    class Parent{
        constructor(name='song'){
            this.name=name;
        }
        //这是方法
        static tell(){
            console.log('tell');
        }
    }
    //设置属性
    Parent.type='test';
    
    console.log('静态属性',Parent.type);//test
}

第十三节

Promise

什么是异步:a与b同时进行
Promise的作用:解决异步操作问题
Promise的基本用法:

{
    //es5中完成a执行完后执行b
    var ajax=function(callback){
        console.log('a');
        setTimeout(function(){
            callback && callback.call()
        },1000);
    };
    ajax(function(){
        console.log('b');//先执行a  一秒后执行b
    })
}

{
    //es6 resolve代表下一步要执行的内容,reject代表中断当前操作
    let ajax = () =>{
        console.log('a');
        return new Promise((resolve,reject) => {
            setTimeout(() =>{
                resolve();
            },1000);
        })
    };
    
    ajax().then(() => {
        console.log('b');
    })
}
    
{
    //如果在实际工作中,我要a执行完 再执行b 再执行n个函数,这显而易见es5写法很难做到。所以使用Promise
    let ajax = () =>{
        console.log('a');
        return new Promise((resolve,reject) => {
            setTimeout(() =>{
                resolve();
            },1000);
        })
    };
    
    ajax()
        .then(() => {
        	console.log('b');
        	return new Promise((resolve,reject) => {
                setTimeout(() => {
                    resolve();
                },2000);
            });
        })
    	.then(() => {
        	console.log('c');
    	})
}

{
    //在串行执行过程中,如何捕获过程中的错误
    let ajax = num => {
        console.log('a');
        return new Promise((resolve,reject) => {
            if(num>5){
                resolve();
            }else{
                throw new Error(`出错了`);
            }
        })
    }
    
    ajax(6).then(() => {
        console.log('正确');//输出这句
    }).catch(err =>{
        console.log(err);
    })
    
     ajax(3).then(() => {
        console.log('正确');
    }).catch(err =>{
        console.log(err);//输出这句
    })
}

{
    //使用场景:所有图片加载完再添加到页面
    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(img){
            document.body.appendChild(img);
        })
    }
    
    Promise.all([
        loadImg('1.jpg'),
        loadImg('2.jpg'),
        loadImg('3.jpg')
    ]).then(showImgs)
}

{
    //使用场景:有三张图片,分别来自不同的网站,哪张先加载完成就显示在页面上,怎么实现?
    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('1.jpg'),
        loadImg('2.jpg'),
        loadImg('3.jpg')
    ]).then(showImgs)
}

第十四节

Iterator 和 for…of循环

{
    //如何直接调用
    let arr=['hello','world'];
    let map=arr[Symbol.iterator]();
    //done如果为false表明未执行完成,否则执行完成
    console.log(map.next());//{ value: "hello", done: false }
    console.log(map.next());//{ value: "world", done: false }
    console.log(map.next());//{ value: undefined, done: true }
}

{
    //如何自定义iterator
    let obj={
        start:[1,3,2],
        end:[4,5,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);//1 3 2 4 5 9
        console.log(obj[Symbol.iterator]().next());
    }
}

{
    //for...of循环 是基于 iterator
    let arr=['hello','world'];
    for(let value of arr){
        console.log(value);
    }
}

第十五节

Generator

基本概念

generator就是异步问题的解决方案,相对于promise更高级一点。在执行过程中遇到yield 或 return 就停止执行。next函数是执行下一步。

{
    //generator基本定义
    let tell=function* (){
        yield 'a';
        yield 'b';
        return 'c'
    };
    let k=tell();
    console.log(k.next());//{ value: "a", done: false }
    console.log(k.next());//{ value: "b", done: false }
    console.log(k.next());//{ value: "c", done: true }
    console.log(k.next());//{ value: undefined, done: true }
}

{
    //经过以上的输出,会发现输出结果与iterator遍历器是一样的,所以generator也可以做作为iterator遍历器的返回值 写起来会比自定义iterator方便很多
    let obj={};
    obj[Symbol.iterator]=function* (){
        yield 1;
        yield 2;
        yield 3;
    }
    
    for(let value of obj){
        console.log(value);//1 2 3
    }
}

{
    //什么情况下generator有它最大的优势呢?其实就是一个状态机,比如说有a、b、c三种状态其描述一个事务,也就是说这个事务只存在这三种状态,使用generator来实现这种状态是特别合适的
    
    //定义一个状态机
    let state=function* (){
        while(1){
            yield 'A';
            yield 'B';
            yield 'C';
        }
    }
    
    //调用---通过不断的调用next()会一直循环执行状态机里的内容
    let status=state();
    console.log(status.next());//{ value: "A", done: false }
    console.log(status.next());//{ value: "B", done: false }
    console.log(status.next());//{ value: "C", done: false }
    console.log(status.next());//{ value: "A", done: false }
    console.log(status.next());//{ value: "B", done: false }
}

{
    //generator的语法糖 执行效果与上面的并无差别
    //但是执行它需要babel的一个插件
    let state=async function (){
        while(1){
            await 'A';
            await 'B';
            await 'C';
        }
    }
    
    let status=state();
    console.log(status.next());//{ value: "A", done: false }
    console.log(status.next());//{ value: "B", done: false }
    console.log(status.next());//{ value: "C", done: false }
    console.log(status.next());//{ value: "A", done: false }
    console.log(status.next());//{ value: "B", done: false }
}

{
    //使用场景1:抽奖环节,当前用户还可以抽奖五次,如何做抽奖次数的限制?
    
    let draw=function(count){
        //抽奖逻辑
        console.info(`剩余${count}次`);
    }
    let residue=function* (count){
        while(count>0){
            count--;
            yield draw(count);
        }
    }
    let start=residue(5);//这里由服务端传来数据
    let btn=document.createElement('button');
    btn.id='start';
    btn.textConten='抽奖';
    document.getElementById('start').addEventListenner('click',function(){
        start.next();
    },false)
}

{
    //使用场景2:服务端的某个数据状态定期的变化,前端则需要定时的去取这个数据状态,因为http是无状态的连接,我们要怎么去实时的取这个状态呢?
    //有两种方法:一是'长轮询',二是'websocket'。因为第二种方法兼容性问题,所以第一种使用较普遍
    
    //长轮询
    //模拟通信过程
    let ajax=function* (){
        yield new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve({code:0})
            },2000);
        })
    }
    //轮询过程
    let pull=()=>{
        let generator=ajax();
        let step=generator.next();
        step.value.then((d)=>{
            if(d.code!=0){
                setTimeout(()=>{
                    console.log('wait');
                    pull();
                },1000);
            }else{
                console.log(d);
            }
        })
    }
    pull();
}

第十六节

Decorators 修饰器

基本概念:修饰器是一个函数,修改行为(扩展类的功能),修改类的行为(只能作用于类)

在下面的代码中,都是自己手动写的,在实际开发中,可以使用第三方库:core-decorators

{
	//定义修饰器函数 需要安装babel-plugin-transform-decotators-legacy插件来支持运行
    let readonly=(target,name,descriptor){
        descriptor.writable=false;
        return descriptor;
    };
    
    class Test{
        //对类的内部进行操作
        @readonly
        time(){
            return '2019-12-22'
        }
    }
    
    let test=new Test();
    
    //无法修改time属性 正是readonly修饰器起的作用
    test.time=function(){
        console.log('reset time');
    }
    console.log(test.time);
}

修饰器也可以对类的外部进行操作,但是只能写在类的前面

{
    //定义修饰器函数
    let typename=(target,name,descriptor){
        target.myname='hello';
    }
    
    @typename
    class Test{}
    //因为上面定义的是一个空类,但是在定义修饰器的时候,设置了一个默认属性,所以可以直接诶调用该属性
    console.log(Test.myname);
}

{
    //使用场景:日志系统,传统写法都写在业务逻辑中。在一个网站需要做广告日志,现在使用修饰器来实现买点日志。
    let log=(type)=>{
        return (target,name,descriptor)=>{
            let src_method=descriptor.value;
            descriptor.value=(...arg)=>{
                src_method.apply(target.arg);
                console.log(`log ${type}`);//在实际开发中换成实际的代码
            }
        }
    }
    class AD{
        @log('show')
        show(){
            console.log('ad is show')
        }
        @log('click')
        click(){
            console.log('ad is lick')
        }
    }
    let ad=new AD();
    ad.show();//ad is show => log show
    ad.click();//ad is click => log click
}
//这样写的优势:可维护性好、复用性好

第十七节

模块化

import导入 export导出

//模块文件model.js
{
    //官方写法 但是不推荐
    export let A=123;
    export let test=function(){
        console.log('test');
    }
    export class Hello{
        test(){
            console.log('class')
        }
    }
    
    //推荐写法
    let A=123;
    let test=function(){
        console.log('test');
    }
    class Hello{
        test(){
            console.log('class')
        }
    }
    //把需要的代码统一写完,最后再进行统一的导出,default代表导出的文件名可以由第三方引入者随意命名
    export default{
        A,
        test,
        Hello
    }
}

//引用文件
{
    //这样一个一个引用显得很笨重,如果需要引入文件有上百个,确实恒吃力 不推荐
    import {A,test,Hello} from './model';
    console.log(A,test,Hello);
    
    //导入方法二:
    import * as model from './model';
    console.log(model.A,model.test,model.Hello);
    
    //导入方法三:
    import myname from './model';
    console.log(myname.A,myname.test,myname.Hello);
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋承佑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值