一、新增 let 和 const 声明变量关键词
由于var关键词创建的全局变量是作为window对象的属性,新增的变量更广泛使用
1. let 变量
- let 关键词是创建块级作用域的变量,不会作为window的属性。建议用let
- let 创建的变量没有变量提升
- let 在for循环语句中声明的变量是一个块级变量
- 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对象
- 语法:new Promise(function(resolve,reject) { })
Promise构造函数需要传一个函数参数,该函数有两个参数,resolve(函数)表示成功结果,reject(函数)表示失败的结果- 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