ES6基础

一、新增 let 和 const 声明变量关键词

由于var关键词创建的全局变量是作为window对象的属性,新增的变量更广泛使用

1. let 变量

  1. let 关键词是创建块级作用域的变量,不会作为window的属性。建议用let
  2. let 创建的变量没有变量提升
  3. let 在for循环语句中声明的变量是一个块级变量
  4. let 不能重复声明变量

2. const 变量

const声明的是 只读变量,常量。(作用域和let一致)不能对值做修改,但如果是对象,对象本身不能修改,里面的值是可以修改的。

// 1. 创建的变量只能读取,不能修改
    {
        const userName ="zhang" 
        //userName = "li" // 修改userName的值会报错
        console.log(userName)
    }
// 2. userName在该模块没有声明,会报错
    {
        console.log(userName) 
    }
// 3. const创建的对象,可以修改对象里面的属性,但不能修改对象本身
    {
        const user = {name:"zhang",age:11}
        user.name="li" // 可以修改对象的属性
        console.log(user)
    }

二、变量的解构赋值

1. rest参数(…args)

rest参数用于获取函数的实参,用来代替 arguments,并且rest参数必须放在参数的最后

// 1. 数组模式: 数组结构赋值,左右两边的结构相等,可以自动结构赋值
    {
        let [s1,s2,s3] = ["sss1","sss2","sss3"]
        console.log(s1,s2,s3) // sss1 sss2 sss3
    }  
// 2. 右边结构大于左边,没有赋值的变量则是undefined
    {
        let [a1,a2,a3] = ["aaa1","aaa2"]
        console.log(a1,a2,a3) // aaa1 aaa2 undefined
    } 
// 3. ...v  把右边剩余的值赋值给v变量
    {
    //...v2只能放在其他参数的最后面
        let [v1,...v2] = ["vv1","vv2","vv3","vv4"]
        console.log(v1,v2)// vv1 ['vv2', 'vv3', 'vv4']
    }

三、对象模式的解构

优点:解构以后,拿对象内的属性值可以直接用属性值的名获取,不用再用对象名去拿

// 1. 对象的属性名称和值的变量名一致,可以简写为1个
    {
        let name = "zhang"
        let age = 10
        let peo = {
            name, // js会自动把name变量做为name属性的值
            age
        }
        console.log(peo) // {name: 'zhang', age: 10}
    }

// 2. 把对象的属性值结构为js变量
    {
        let user = {
            name: "zhangsan",
            age: 12
        }
    // 左右结构相等,把name属性对应的值赋值给创建的name1变量
        let {name:name1,age:age1} = user;
        console.log(name1,age1) // zhangsan 12
    // 如果变量名和属性名称一致,可以简写为一个。
        let {name,age} = user; //=> let {name:name,age:age}=user
        console.log(name,age) // zhangsan 12
    }
    
// 3. 可以设置对象解构变量的默认值(对象的属性没有才使用默认值)
	 {
	      let {name,age,go = 'text'} = {name:'XXX',age:12}
	      console.log(go); // text
	  }

四、函数参数的解构

// 1. 按数组方式解构
    {
        function f1([a1,a2,a3]){ //等价 [a1,a2,a3] = ["aa1","aa2","aa3"]
            console.log(a1,a2,a3);
        }
        f1(['aa1','bb1','cc1'])
    }
// 2. 对象的方式解构
    {
        function f2({name,age}){
            console.log(name,age);
        }
        f2({name:'zhang',age:12})
    }

五、字符串解构

{
// 字符串在js执行的时候会被包装为字符串对象
    let [a,b] = 'hello'
    console.log(a,b);// h e
}

1.字符串相关扩展

// 1. includes() 查询字符串中是否包含某个字符,包含返回true,不包含返回false。
// 不支持正则对象
    let str = 'hello JavaScript'
    console.log(str.includes('hello')); // true

// 2. trimStart() 去掉头部空格。trimEnd() 去掉尾部的空格。trim()去掉前后的空格
    let str1 = '  hello '
    let str2 = ' hel lo  '
    console.log(str1.length,str2.length); // 8 9
    str1 = str1.trimStart()
    str2 = str2.trimEnd()
    console.log(str1.length,str2.length);// 6 7
    str2 = str2.trim()
    console.log(str2.length); // 6

/** 
    3. matchAll(str) 查询字符串中所有的str字符,支持正则对象。
    返回一个迭代器(迭代器用next()取结果,取一个迭代器元素就少一个,直到把迭代器内容取完)
    迭代器返回一个固定对象{done:bool,value:[]}
    done为true则表示迭代器元素取完了
*/
    var str3 = "hello javascript is very good"
    var res1 = str3.matchAll("o")
    // 一般用while 循环取迭代器 
    var data = res1.next()
    while(!data.done){
        console.log(data.value[0])
        data = res1.next()
    }
    // 正则也可取
    console.log(str3.match(/o/g));

//4. replaceAll(str1,str2) 替换所有的str1字符为str2字符
    var str4 = "hello javascript is very good"
    console.log(str4.replaceAll('o','-'));
    console.log(str4.replace(/o/g,'-')); // 正则表达式

// 5. at(index) index是字符串的索引号,返回对应index索引的字符
    console.log(str4.at(1)); // e   正数从左往右计算
    console.log(str4.at(-1)); // d  负数从右往左计算

六、数值相关的扩展

/**
1. Number.isFinite(number) 检测一个值是否为有限值。true则是有限值,false则是Infinity(无限值)
当一个数值超过js能显示的范围,显示就是一个Infinity。
*/
    console.log(2**1023);// js 能表示的最大整数是2**1023,超过则显示Infinity
    console.log(2**1024); // Infinity

    console.log(Number.isFinite(123)); // true 只有能显示的数字才是true
    console.log(Number.isFinite(2**1024)); // false
    console.log(Number.isFinite('aaa')); // false

// 2. isNaN() 判断结果是否为NaN,true则是,false则不是
    console.log(Number.isNaN('a'/2)); // true

// 3. parseInt(),parseFloat() 方法移到了Number对象上(原来是在window对象,现在还是保留了这两个方法)
    console.log(Number.parseInt(3.7)); // 3

七、Math 对象扩展

// 1. trunc() 返回移出小数的整数部分(理解为直接去掉小数点)
    console.log(Math.trunc(7.6)); // 7
    console.log(Math.trunc(-7.6)); // -7

// 2. sign() 判断数值正负. 正数返回1,负数返回-1,正0返回0,负0返回-0,其他非数值类型返回NaN
    console.log(Math.sign(8)); // 1
    console.log(Math.sign(-8)); // -1
    console.log(Math.sign(0)); // 0
    console.log(Math.sign(-0)); // -0

八、BigInt(大整数)

js表示的最大值是2的1023次幂,如果超过以后显示Infinity,如需要显示超过1023的值,需要用BigInt类型

// 1. 创建大整数类型则是在数值后面添加一个n
        var n1 = 122n
        console.log(typeof n1,n1); // bigint 122n
         
// 2. BigInt可以把数值类型转为大整数类型
        console.log(BigInt(2**1022));

// 3. 大整数计算和普通数值类型计算一样
        console.log(BigInt(2n**1024n));

九、函数相关扩展

1. 箭头函数

箭头函数适合用在与 this 无关的回调:定时器、数组的方法回调…
箭头函数一般不适合用有 this 有关的回调:DOM事件回调、对象方法…

//1. rest 参数(...表示不确定参数的个数),类似于arguments,是一个数组,表示所有的参数
        // rest 返回是一个纯数组,arguments是一个类似数组对象
        function f1(...rest){
            console.log(rest,arguments);
        }
        f1('1','d','vvv')

// 2. 箭头函数(对普通函数的简化)
  // 创建箭头只能用表达式方式创建,不能用function关键词声明
    // 2.1 箭头函数如果只有单行表达式,单行表达式不加{}和return,会直接返回单行表达式的结果
        let f2 = (a,b)=> a + b // 箭头函数自动返回a+b的结果,如果加了{},则不会返回
        console.log(f2(1,3)); // 4
   
    // 2.2 如果箭头函数有多行语句,必需添加{} 和 return
        let f3 = (a,b)=> {
            a += 1
            b += 2
            return a + b
        }
        console.log(f3(2,3)); // 8

    // 2.3 箭头函数如直接返回一个对象,需要用()强调代码是一个对象
        let f4 = ()=> ({name:'zhang',age:29})
        console.log(f4()); //{name: 'zhang', age: 29}

    // 2.4 箭头函数是为了简化函数写法,比较适合直接返回一个结果的场景,如果函数逻辑代码较多,尽量不用箭头函数
        let arr1 = [1,2,4,6,7]
        console.log(arr1.filter((item)=> item > 5));

    // 2.5 箭头函数内部没有this,(普通函数内部的this作为对象属性是 谁调用,则表示谁)
        let obj1 = {
            name: 'obj1',
            go: ()=> {
                console.log(this); // 箭头函数没有this,这个this是外层this对象(这里表示window)
            },
            move: function(){
                console.log(this); // 普通函数内部this,谁调用,则表示谁
            }
        }

        let obj2 = {
            name: 'obj2'
        }
        obj2.go = obj1.go
        obj2.move = obj1.move

        obj2.go() // 指向 window的this
        obj2.move() // 指向 obj2的this

十、数组扩展

// 1. 扩展符号...,可以自动展开数组里面的所有元素(可以理解为直接去掉数组中括号,保留元素)
    let arr1 = ['a','b','c']
    console.log(...arr1); // a b c

// 2. ... 合并多个数组
    let arr2 = ["x1","x2","x3"]
    let arr3 = ["y1","y2","y3"]
    console.log([...arr2,...arr3]);

// 3. ... 可以进行数组的深拷贝
    // let arr4 = arr1 // true (只能拷贝一层数据)
    let arr4 = [...arr1] // 相当于把数组里面的值拿出来重新拷贝
    console.log(arr4 === arr1);// false

// 4. 将伪数组转为真正的数组
	//(1) [...arr]
    let divs = document.querySelectorAll("div")
    console.log(divs); // 可以获取到3个div
    console.log([...divs]); // [div, div, div]

	// (2) from() 可以把类似数组对象转为真正的数组
    let o1 = {
        0:"a",
        1:"b",
        2:"c",
        length: 3
    }
    console.log(o1)
    console.log(Array.from(o1)) // ['a', 'b', 'c']

// 5. of() 把一组值转为数组,可以用来创建数组
    let arr5 = Array.of("a","b","c")
    console.log(arr5) // ['a', 'b', 'c']

// 6. find((item)=>bool) // 主要用于查询符合条件的成员,bool为true,则停止查找并返回为true的该元素
    let arr6=[{name:"a",age:11,id:1},{name:"b",age:12,id:2},{name:"a",age:13,id:3}]
    console.log(arr6.find((item)=>item.id === 2)) //{name:"b",age:12,id:2}

// 7. keys() 遍历数组下标,values() 遍历数组的元素。返回迭代器
    let keys = arr6.keys(); // 返回数组下标的一个迭代器
    // for of 遍历迭代器,直接输出value里面的值
    for(let item of keys){
        console.log(item)
    }
    let values = arr6.values();
    for(let item of values){
        console.log(item)
    }
    // 实际开发中以for循环或者forEach为主

//8. includes(item) 查询数组中是否包含item元素,包含返回true,不包含返回false
// 适用数组元素是非引用数据类型
    let arr7 = ["a","java","html","css","js",{name:1}]
    console.log(arr7.includes("js")) // true

//9. flat() 把二维数组拉平变为一维数组
    let arr8 = [["a","a1"],"b","c"]// 直接把二维数组元素放入一维数组
    console.log(arr8.flat()) // ['a', 'a1', 'b', 'c']
//10. assign() 对象的合并
  // Object.assign(target,obj1,obj2...) -- 把后面的所有对象放入 target中
   let newObj = Object.assign({},{a:1},{type:"Python"})
   console.log(newObj);

十一、对象的扩展

// 1. 对象的属性可以用[],里面给表达式([]里面可以给变量等)
    let o = {}
    let name = "a1"
    o[name] = "a111111"
    console.log(o)

// 2. 对象属性简洁表示,对象属性和属性值变量名称一致,简写为一个
    let sex = "man"
    let user = {sex}
    console.log(user)

// 3. ... 扩展符,自动把对象的所有属性展开。和数组类似
    let user3 = {name:"name1",age:12,address:"xxxx"}
  // 可以深拷贝一层对象
    let user4 = {...user3}
    console.log(user4 === user3) // false
  // 合并对象
    var user31 = {cat:"cat1"}
    console.log({...user3,...user31})

//4. Object.assign(target,source) 把source对象合并到target对象,返回合并后的对象
    console.log(Object.assign(user3,user31))

//5. Object.keys()返回对象的key数组,Object.values() 返回对象的属性值数组
    console.log(Object.keys(user3))
    console.log(Object.values(user3))

//6. entires() 把对象转为二维数组
    console.log(Object.entries(user3))

十二、Symbol

新增的数据类型,返回一个独一无二的值(唯一值)

 let s = Symbol();// 返回一个唯一值

// symbol 数据类型
   // (1)Symbol() 每定义一个symbol都是唯一的,返回一个唯一值
       let s1 = Symbol("one")
       let s2 = Symbol("one")
       console.log(s1 == s2); // false
       console.log(s1.description); // one

   // (2)Symbol.for() 如果值相同则symbol是指向同一个symbol()
       let s3 = Symbol.for("111")
       let s4 = Symbol.for("222")
       let s5 = Symbol.for("222")
       console.log(s3 == s4); // false
       console.log(s4 == s5); // true

1. 给对象添加symbol属性

// 给对象添加symbol属性(唯一的)
    let game = {
        name: "狼人杀",
        // 注意:添加Symbol属性要用 [] 括起来 
        [Symbol("one")]: function(){
            console.log("我是预言家");
        },
        [Symbol("two")]: function(){
            console.log("我是狼人");
        }
    }
    console.log(game);

十三、数据结构的扩展

1. Set 数据结构

作用:和数组有点类似,set内部的元素不会有重复,如果有重复的自动去重
set一般开发中用的较少,一般使用数组。常用就是数组去重

// 1. 创建set数据
    let s1 = new Set(["x1","x2"])// 设置set默认值,需要用数组方式传入
    console.log(s1);

// 2. add() 向set 添加数据
    s1.add("a1")
    console.log(s1);

// 3. forEach()遍历set里面的元素
    s1.forEach(function(item){
        console.log(item);
    })

// 4. size 查看set数据元素的个数
    console.log(s1.size);

// 5. delete() 删除成员(元素)
    s1.delete("x2") // 删除set数据里面的x2成员

// 6. has() 判断set 里面是否有某个值,有则返回true,没有则返回false
    console.log(s1.has("x1"));

// 7. clear() 清除set数据里面的所有成员
    console.log(s1.clear());

// 8.set 一般用于数组去重
    var arr = [1,2,2,3,4,5,4,3,]
    let s2 = new Set(arr)
    console.log(s2);

// 9.把set数据转为数组2种方式:
    // (1) ... 可以展开set数据的成员
        console.log([...s2]);
    // (2) Array.from() 把set数据转为数组
        console.log(Array.from(s2));

2. Map 数据结构

作用:和对象比较类似,是一种键值对的集合
和对象的区别: 对象的键只能是字符串,Map的键可以是任意数据类型
实际开发使用较少,一般用对象结构

// 1. 创建map,使用Map构造函数
    // 初始化Map的数据参数格式是一个二维数组
    let map1 = new Map([["name","nana"],[{name:"11"},[12]]]) // 一个数组前面是键(name),后面是值(nana)
    
// 2. set(key,value) 设置map的数据,key和value可以是任意数据类型
    var obj = {age:19}
    map1.set(obj,{sex:"女"}) // [{age:19},{sex:"女"}]

// 3. forEach() 遍历map的成员的value值
    map1.forEach(function(item){
        console.log(item);
    })

// 4. get(key) 读取map中key值对应的value值。
    //如果key是一个引用数据类型,需要保证内存地址一样
    console.log(map1.get(obj));

// 5. for of 遍历map数据的key和value
    for(let [key,value] of map1) {
        console.log(key,value);
    }

// 6. size 查看map结构中成员的数量
    console.log(map1.size);

// 7. has(key) 判断map结构里面键是否存储,如果是引用数据类型,需要同一个内存地址
    console.log(map1.has("name"),map1.has(obj));

// 8. delete(key) 删除key
    map1.delete("name");

// 9. clear() 清空map的所有成员
    map1.clear()

十四、Proxy 对象代理

Proxy(object,{set(),get()})
对象代理:为目标对象object设置一层拦截器
访问object对象先进入到proxy拦截器。一般第三方的框架使用proxy较多

	let user={
	    name: "张三",
	    age: 12
	}
	
	user = new Proxy(user,{
	    // 修改对象属性或者添加属性的时候触发set方法
	    // target 是当前对象user,key是添加或者修改的键,value是值
	    set(target,key,value){
	        // 可以在set方法进行对象属性值的一些过滤,比如设置name属性值只能添加string类型
	        if (key === "name" && typeof(value) !== "string"){
	        throw new Error("name 只能添加字符串类型")
	        } else if (key === "age" && typeof(value) !== "number"){
	        throw new Error("age 只能是数值类型")
	        }
	        target[key] = value
	    },
	
	    // 读取对象属性触发get方法,return 返回的值就是读取属性的值
	    get(target,key){ // target是当前对象,key是读取的键
	        if(key === "age"){
	            return target[key] + "岁"
	        } else {
	            return target[key]
	        }
	    }
	})
	user.name = "aaa"
	user.age = 10
	// user.age = "10" // 有报错提示
	console.log(user);

十五、迭代器

迭代器:是一种特殊的对象,自带next()方法读取迭代器成员,读取一个成员就删除一个,(指针)指向下一个成员
next() 返回一个对象,有两个属性:{value:属性值,done: 是否读取完成}

// matchAll 返回的是一个迭代器
    var str = "hello javascript is good"
    var res = str.matchAll("o")
    for (let i of res) {
        console.log(i);
    }

1.生成器 Generator


// es6 使用Generator 生成器可以返回一个迭代器。生成器则是在普通的函数名前面添加*
// 总结: Generator函数是分段执行,yield语句是暂停执行,而 next语句是恢复执行
    function* as(){
        console.log("Start");
    // x 可真的不是yield 1 的返回值,它是next()调用恢复当前yield 从next("xx")传进来的值
        let x = yield 1
        console.log("x:" + x);
        let y = yield 2
        console.log("y:"+ y);
        return x + y
    }

    let fn = as()
    // 调用生成器函数时,相当于一个指针,只指向 yield,依次向下执行
    console.log(fn.next()); // Start {value: 1, done: false}
    console.log(fn.next(20));// x:20 {value: 2, done: false}
    console.log(fn.next(10)); // y:10 {value: 30, done: true}


    function* createText(){
        // 创建迭代器的value值,必需要yield关键词
        yield "a2"
        yield {name: "nana"}
        yield 111
    }
    for (let i of createText()){
        console.log(i);
    }

十六、异步编程

常见的异步模式:回调函数、定时器

1. 区别异步和同步模式

// (同步-按顺序执行)阻塞模式: 前面的代码执行完成,才能执行后面的代码(顺序可控。)
    {     function f1(){
            console.log("f1")
        }
        function f3(){
            console.log("f3")
        }
        f1()
        f3() // 此时顺序调用的结果是:f1 f3
    }

// (异步-同时执行)非阻塞模式:前面的代码指向不阻挡后面的代码,两段代码同时执行(效率高,顺序不可控)
    {    // 1. 回调函数是一种典型的异步模式
            function f1(callback){
                console.log("f1");
            }
            function f2(){
                console.log("f2")
            }
                
            f1(e => console.log("callback"))
            f2()

         // 2. 定时器是一典型的异步模式
            function a1(){
                setTimeout(function(){
                    console.log("a1")
                },500)
            }
            function a2(){
                console.log("a2")
            }
            a1()
            a2() // 此时顺序调用的结果是:a2 a1

    }

2. 异步编程解决方案 Promise

1. promise 的基本用法

Promise() 构造函数可以实例化一个Promise对象

  1. 语法:new Promise(function(resolve,reject) { })
    Promise构造函数需要传一个函数参数,该函数有两个参数,resolve(函数)表示成功结果,reject(函数)表示失败的结果
  2. Promise对象内部由一个标记用于记录其内部函数是否执行完成,默认是pending状态。当调用resolve或者reject方法会修改状态为成功fufilled或失败rejected的标识
// a. 创建Promise对象。创建的对象默认状态是pending状态,表示其回调函数在执行中
    let promise = new Promise(function(resolve,reject){
        let status = false // 定义一个标识,决定结果的成功和失败

        if (status){ // 如果status为true认为是执行成功,调用resolve方法
            resolve("执行成功!") // 对应的then方法的回调函数
        } else { // 否则调用reject方法
            reject("执行失败!") // 对应catch方法的回调函数
        }
    })

// b. 当promise对象调用resolve方法,则触发then方法的回调函数。then方法返回promise对象
    promise.then( param =>console.log(param))
// c. 当promise对象调用reject方法,触发catch方法的回调函数。catch方法返回promise对象
    promise.catch( err => console.log(err))

// d. promise对象支持链式操作(综合bc调用):
    promise.then((param) =>{
        console.log(param); // 成功执行
    }).catch((err) =>{
        console.log(err); // 失败执行
    }).finally(()=>{
        console.log("finally!!");// 只要Promise对象的状态是非pending状态,则执行finally的回调函数
    })

2. promise.all([…]) 返回多组结果

Promise.all([…]) --可以用数组同时返回多个结果

注意:其中有一个报错,全部都报错

function sum(a,b){
    return new Promise((resolve,reject) =>{
        resolve(a + b)
    })
}
Promise.all([sum(12,13),sum(4,6),sum(99,99)])
       .then(res =>console.log(res)) // [ 25, 10, 198 ]

3. Promise.allSettled([…])返回多组结果

Promise.allSettled([…]) 同时返回多个promise的执行结果(无论成功失败)

function sum(a,b){
    return new Promise((resolve,reject) =>{
        resolve(a + b)
    })
}
Promise.allSettled([sum(12,13),sum(4,6),Promise.reject('aa'),sum(99,99)])
       .then(res =>console.log(res)) // { status: 'fulfilled', value: 25 },...{ status: 'rejected', reason: 'aa' },...

4.Promise.race([…]) 返回执行最快

Promise.race([…]) 返回执行最快的promise(不考虑对错)

function sum(a,b){
    return new Promise((resolve,reject) =>{
        resolve(a + b)
    })
}
Promise.race([Promise.reject('aa'),sum(12,13),sum(4,6),sum(99,99)])
       .then(res =>console.log(res)) 
       .catch(res=>console.log('error')) // error

5.Promise.any([…]) 返回执行最快且成功的

Promise.any([…]) 返回执行最快且成功的promise

function sum(a,b){
    return new Promise((resolve,reject) =>{
        resolve(a + b)
    })
}
Promise.any([Promise.reject('aa'),sum(12,13),sum(4,6),sum(99,99)])
       .then(res =>console.log(res)) // 25
       .catch(res=>console.log('error')) 

3. 微任务和宏任务

在JS中任务队列有两种:

  • 微任务:Promise的回调函数(then、catch、finally)

  • 宏任务:大部分代码都去宏任务队列中去

  • 整个流程:
    1. 执行调用栈中的代码
    2. 执行微任务队列中的新任务
    3. 执行宏任务队列中的所有任务

    console.log(1); // 栈内
    setTimeout(()=>console.log(2)) // 宏任务1
    Promise.resolve().then(()=>console.log(3)) // 微任务1
    Promise.resolve().then(()=>setTimeout(()=>console.log(4))) // 微任务2 然后再添加里面 宏任务3
    Promise.resolve().then(()=>console.log(5)) // 微任务3
    setTimeout(()=>console.log(6)) // 宏任务2
    console.log(7); // 栈内
// 结果:1 7 3 5 2 6 4

queueMicrotask() 微任务函数:

queueMicrotask(()=>{
        console.log(33);
    })

4. ajax应用

ajax是一个异步程序,ajax发生以后,需要等待后端返回数据,可以把ajax放入Promise对象里面执行

/*
 * 由于promise对象是异步编程,不能控制其执行顺序。有时需要同时拿到多个promise的结果。
 * Promise.all([promise1,promise2]) 接受一个promise对象数组作为参数,
 * 该方法会等所有promise对象状态为非pending状态,统一返回结果
*/
    function ajaxPromise(time,obj){
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                var data = {
                    name: "aa",
                    age: 19,
                    success: true,
                    ...obj
                }
                if (data.success){
                    resolve(data)
                } else {
                    reject("error")
                }
            },time)
        })
    }
// (1) 这种会异步执行,先执行p2 等3秒再执行 p1
    let p1 = ajaxPromise(3000).then((data)=>console.log(data))// 先执行p1的then方法
    let p2 = ajaxPromise(1000,{name: "p2"}).then((data)=>console.log(data))// 后执行p2的then方法
    
// (2) 要同步p1 p2状态都为非pending状态,执行then方法,把两个promise的结果放入arr数组中
    let p1 = ajaxPromise(3000)
    let p2 = ajaxPromise(1000,{name: "p2"})
    Promise.all([p1,p2]).then((arr)=>{ // 会等待最长那个时间执行完,一起返回
        console.log(arr);
    })
封装ajax
// 封装 Ajax
    function ajax(url){
        return new Promise((resolve,reject)=>{
            let xhr = new XMLHttpRequest();
            xhr.open("get",url,true);
            xhr.send();
            xhr.onreadystatechange = function(){
                if (xhr.readyState == 4){
                    if (xhr.status >= 200 && xhr.status < 300){
                        resolve(JSON.parse(xhr.responseText))
                    } else {
                        reject(xhr.responseText)
                    }
                }
            }
        })
    }

    ajax("demo3.json").then(res =>{
        console.log("success:",res);
    }).catch(err => {
        console.log("error:",err);
    })

5. promise async…await 用法

上面的同步/阻塞方法是传统写法,现在较多使用 async…await

async 异步函数(在普通的函数前面添加async关键词,异步函数默认返回的是一个promise对象。) 如果函数有return 返回值,则会把return的值作为promise对象的结果

    async function f1(){
        return "xxx" // xxx作为promise对象的返回结果,默认调用的resolve方法
    }
    // f1() 是一个promise对象函数
    f1().then(param=>console.log(param))

await 关键词,可以放在执行的异步函数前面,表示异步函数执行完成后再往下指向代码(让promise对象同步指向) await 只能用在异步函数内部

// 接上面定义的 ajaxPromise() 函数
    async function go(){
        // await 等待p1对象的状态改变后再往后执行代码(阻塞/同步模式)
        let p1 = await ajaxPromise(3000,{name:"p1"}).then((data)=>{
            console.log(data)
            return data
        })
        // await能返回then回调函数返回的值。这里的p1等于then方法的回调函数返回的值
        console.log(p1);
        
        let p2 = ajaxPromise(1000,{name:"p2"}).then((data)=>console.log(data))
    }
    go()
// promise async...await 用法
    function one(){
        return "one"
    }
    function two(){
        // new 一个 promise函数
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                resolve("two")
            },2000)
        })  
    }
    function three(){
        return "three"
    }

    // 在执行函数前加 async
    async function run(){
        console.log(one());
      // 在具体调用的函数前加 await
       // await 等待two()对象的状态改变后再往后执行代码(阻塞/同步模式)
       // await 后面所有的代码都会放入微任务队列执行
        console.log(await two()); // 此时加promise函数结果:等待3秒输出; 未调用前:undefined
        console.log(three());
    }
    run()

十七、类 class

1. 类的创建和使用

// 1. class 创建类(用大驼峰命名)
    class Animal {
        // 类的构造函数,每一个类有且只有一个构造函数。不需要的时候可以不写。实例化类触发构造函数执行
        // 构造函数内部的this表示当前类的实例,构造函数可以接受实例化类传递的参数
        constructor(name,age){
            // 构造函数内部可以添加属性和方法
            this.name = name
            this.age = age
        }
        sex = "male"
// 3. static 静态属性,static修饰的属性是class类的属性,类产生的实例没有该属性,调用属性只能通过类名调用
        static address = "xxx" // 静态属性,放在Animal类上,只能通过Animal.address调用
        move = function(){ // 添加方法,放在实例上的方法
            console.log("move") 
        }
        go(){ // 添加方法,放在prototype原型上。
            console.log("go");
        }      
// 5. # 创建私有属性,只能在类的内部使用,类外部无法调用(有一定安全性,防止外部随意拿数据) 
        #id = 1 // 只能在Animal类内部使用
    // 如果外部要拿内部私有属性,通过在类的内部创建方法返回私有属性
        getId(){
            return this.#id
        }
// 6. static 静态代码块,用static修饰的代码块,创建class的时候执行一次,以后都不会执行
        static { // 创建一个静态代码块,js运行到Animal类的时候会执行一次代码块,后面不会再执行
            console.log("static code block");
        }

    }

// 2. 实例化类产生一个实例(对象)
    let a1 = new Animal("cat",3)
//4. 调用:
    console.log(a1); // (1)不会调用到静态属性
// (2)调用静态属性
    console.log(Animal.address);
// (3)调用私有属性
    // console.log(al.#id);// 直接调用报错
    console.log(a1.getId());

2. 类的继承 extends

    class Animal{
        constructor(name,age = 2){
            this.name = name
            this.age = age
        }
        sex = "male"
        go(){
            console.log("go");
        }

    }
// 1. 继承 类
    class Dog extends Animal { // 继承Animal类的属性和方法,包括原型上的方法都会继承
        constructor(name,money){
            // 使用了extends继承类,构造函数内部不能直接使用this。
            // 需要使用super()方法执行父类,产生this对象,子类才能用this
            super(name) // super表示父元素的构造函数
            this.money = money
        }
        id = 1
    }
    let dog1 = new Dog("Dog1",1000)
    console.log(dog1);
        
// 2. class的多重继承, 把类的所有属性和方法提取出来,放到一个对象里面
    let Class1 = class extends Animal{} // 等价于 class Class1 extends Animal
    function Extendsf(c){
    return class extends c{
     constructor(c){
       super();
       this.c=c
      }
      sex="sex"
    } 
   }
   
   class Class2 extends Extendsf(Animal){
   }
   let c2 = new Class2();

十八、模块化

传统有 AMD模块化(需要引入requery.js文件)和common js模块化
最新推出es6模块化

1. ES6 模块化使用

导入模块:

<!-- 使用es6模块化,需要设置type='module' 开启模块化支持 -->
 <script type="module">
  /** 
    ES6 新增了模块化功能,主要由export导出模块,import引入模块。
    模块可以单独放到一个文件,也可以和逻辑代码在同一个文件,模块内部的变量外部无法直接获取,需要提供接口给外部
  */
   // 从animal.js 文件导入animal 和move模块
    // 一. import 的模块名称和export 导出的变量名称要一致
      /* 由于自己创建的变量名不能和模块名称相同:
        as 可以给引入的模块取一个变量名。这是引入的animal模块放入a1变量里面 
      */
        import {animal as a1,move,user,go} from "./animal.js"
        console.log(a1);
        move()

    // 二、 * 可以导入所有的模块,导入的模块必需设置一个对象把所有模块放在该对象上面
        import * as tool from "./animal.js" // 引入animal.js的所有模块,起名tool并放入tool对象里面
        console.log(tool);

    // 三、import导入 default默认导出的模块。需要自定义一个变量接收导入内容
      /**
       * import default导出的模块可以自定义变量名
       * import export 直接导出的模块,变量名和导出模块名称要一致
      */
        // import 引入default的模块,不用{},直接自定义变量。
        import people,{move,user} from "./animal.js"
        console.log(people,user);
 </script>

导出模块的js:

// 1. 导出模块方法1:  export 直接添加在声明变量或者函数的关键词前面,表示导出对应的变量
    export const animal = {
        name: "dog",
        age: 10
    }

    export function move(){
        // 导出函数
        console.log("move");
    }

// 2. 导出模块方法2: 可以把需要导出的变量或者方法放入 export {} 的大括号里面
// export引入方式不变
    const user = {
        name: "nana",
        sex: "male"
    }
    function go(){
        console.log("go");
    }

    export {user,go}

// 3. 可以在模块文件中引入其他的模块,直接导出
    export {Test} from "./test.js"

// 4. default 一个文件只能有一个,默认导出模块。
  // (1)写法1:
    export default {
        id: 1
    }
  // (2)写法2:
    const peo = {
        sex: "female"
    }
    export default peo

十九、设计模式

设计模式: 是程序员针对特定的问题,给出的简洁而优化的处理方案(一种解决问题的思想)。
例: 一个设计模式A,只能解决A类型问题。设计模式B只能解决B类型的问题。
同一个问题,在不同位置,不同的思路,是不一定用同一种解决方案的。设计模式只能在特定的情况,针对特定的问题使用。

1. 单例模式

单例模式:一个构造函数一生只能有一个实例,不管 new 多少次,都只有这一个实例
比如网页的消息提醒框(自定义弹出层),始终保持只有一个dom显示消息提醒。

// 一个构造函数一生只能有一个实例,不管 new 多少次,都只有这一个实例
// 用一个 闭包函数 可以把 instance 变量保存下来,防止丢失数据发生变动就不是单例模式了
const singleTon =  (function(){
    function Animal(name,age,gender){
        this.name = name
        this.age = age
        this.gender = gender
    }
    Animal.prototype.run = function(){
        console.log("running~~");
    }

    let instance = null // 保存实例变量,在闭包内保存,会一直存在,不会被销毁
    return function singleTon(...arg){
        if (!instance){
            instance = new Animal(...arg)
        }
        return instance
    }
})()
let a1 = new singleTon("dog",7)
let a2 = new singleTon("cat",4,"male")
console.log(a1,a2);
console.log(a1 == a2); // true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值