理想的语法
复用 | 循环:复用几乎相同的代码 函数:复用相同的代码 结构体/类:复用函数-属性的组合 类继承:复用已有的类 |
复合类型1 | 数组:有序、限长、同类型 Link集合:有序、不限长、任意类型 Set集合:无序、不限长、任意类型 |
复合类型2 | Map集合:无序、不限长、任意类型 实例对象:无序、限长、预定义类型 |
函数式编程 | 函数式编程应是主流 类/结构体只是复合类型 |
函数和类的嵌套 | 函数中能定义函数、类 类中能定义函数、类 |
类型限制 | 类型约束、代码提示、性能提升 |
类型推断 | 变量赋值&定义:自动推断类型 函数定义:自动推导返回值类型 |
类型写法 | 类型前置,类型后置 |
any&Object | any类型:能指向任意类型 Object类型:能指向任意实例对象 |
编译运行 解释运行 | 编译功能:事先编译能大大提升性能 解释功能:边解释边执行 |
堆内存回收 | 手动释放、GC释放 |
动态类加载 | JS的动态import,require Java的反射机制 |
序列化 | JS把对象转成JSON字符串 Java把对象转成二进制数据 |
注释 | // 单行注释 |
/* 多行注释 */ | |
/** *文档注释 */ | |
循环 | for in迭代器 while循环 loop无限循环 |
判断 | if-else、switch是判断表达式 case后面是条件判断,而非数据 |
模块化 | 模块A的首次引入 会运行模块A的代码 返回暴露对象a 并将暴露对象a缓存起来 模块A的后续引入 不会运行模块A代码 直接返回暴露对象a |
写法优化 | 省略语句分号 模板字符串 剩余参数、默认参数、具名参数 箭头函数 对象访问:obj.属性名、obj.['属性名'] 数组、对象、字符串的解构赋值 类型别名 |
通过代码生成公共代码
- Rust:宏在编译期实现扩展
- Java:注解+动态生成类,动态实现类的扩展
- JS:eval运行动态拼接的代码字符串
- JS:装饰器+babel转换,在打包时实现扩展
组合扩展结构体
结构体 | 支持定义实例属性、实例方法 支持定义静态属性、静态方法 |
接口 | 约定结构体的方法 |
多态 | 结构体B实现接口A方法1、2 创建接口A的引用变量a 创建结构体B的实例b 变量a能调用实例b的方法1、2 |
组合扩展 | 结构体A定义一些属性和方法 结构体B定义一些属性和方法 结构体C定义一些属性和方法 结构体C的属性中包含结构体A、B 结构体C即对是结构体A、B的扩展 |
继承扩展类
- 类有结构体的特性:定义实例方法、实例属性
- 类有接口的特性:定义抽象方法
- 类的继承是一种规范化的组合
- 类能继承或实现类的抽象方法
- 类能继承或重写类的实例方法、实例属性
简单对比
- 接口:静态属性、静态方法,抽象方法
- 抽象类:静态属性、静态方法,抽象方法,普通方法,普通属性
- 普通类:静态属性、静态方法,普通方法,普通属性
Java单继承
- 普通类单继承普通类、普通类单继承抽象类、普通类多实现接口
- 抽象类单继承抽象类、抽象类多实现接口
- 接口多继承接口
C++多继承
- 普通类多继承普通类、普通类多继承抽象类
- 抽象类多继承普通类、抽象类多继承抽象类
变量-类型
强弱类型语言
静态强类型 | 动态弱类型 | |
函数参数 函数返回值 | 参数个数固定 参数类型固定 返回值类型固定 重载扩展:支持定义不同形参的同名函数 泛型扩展:动态指定参数和返回值类型 形参不同=>参数个数或参数类型不同 | 参数个数随意 参数类型随意 返回值类型随意 |
变量指向 | 类变量能指向本类实例 多态的扩展: 类变量能指向后代实例 类变量能调用后代实例重写的方法 类变量不能调用后代实例扩展的方法 | 变量指向任意类型 |
权限修饰符 | private、protect、public | 无 |
性能 | 更高 | 较低 |
IDE提示依据 | 变量类型,返回值类型,参数类型 XXXDom注释 | XXXDom注释 |
权限修饰符
- private:支持本类内部访问
- protect:支持本类和子类内部访问
- public:支持本类和子类内部访问,支持类实例访问
- 若子类方法抛出的异常大于父类,可能发生try-catch能处理父类方法的异常,但是不能处理子类抛出的异常。所以子类方法抛出的异常不能大于父类抛出的异常
- 若子类方法的访问权限小于父类,可能发生父指针能调用父类方法,但不能调用子类方法。所以子类方法的访问权限必须大于父类方法的访问权限
泛型的使用
class rapList<T>{ //类上定义
public String rap<D>(){ //方法上定义
D d; //泛化属性类型
return "阿坤";
}
public T rap(T name){ //泛化方法返回值
return name;
}
}
JavaScript类型
语言类型 | JS:动态弱类型 TS:类型限制的动态弱类型 |
值类型 | 大整型:bigInt 浮点型:number 布尔型:boolean undefined、null、symbol、string |
引用类型 | 集合、数组、普通对象、函数 |
判断类型 | let res=typeof xxx res的类型是字符串 res的值是变量xxx的类型 |
变量声明 | let k1:number=v1,k2=v2 类型推断、后置类型 |
局部性死区 | 变量声明后,才能使用 |
常量 | const |
x的if条件为假 Bollean(x)为false !!x为false | 布尔值:false 数字:0 空字符串:"" 特殊值:null、undefined、NaN |
模板字符串 | `${变量}字符${变量}` |
n进制 | 数字类型全部是十进制 字符串存放其他进制数字 |
枚举
Javascript:枚举名是实例对象,枚举属性是常量,枚举属性是基本数据类型
enum Gender{male='男',female='女',unknown='未知'}
console.log(Gender) //{male:'男',female:'女',unknown:'未知'}
enum Gender{male,female,unknown}
console.log(Gender) //{male:0,female:1,unknown:2}
let info:{name:string,gender:Gender}
//info.gender的值只能是Gender.male、Gender.female、Gender.unknown
Java:枚举名是类名,枚举属性是类的静态属性,枚举属性是对象
public enum Gender {
male("male1","male2","male3"),
female("fmale1","fmale2","fmale3"),
unknown("unknown1","unknown2","unknown3");
public String str1;
public String str2;
public String str3
Gender(String str1,String str2,String str3){
this.str1=str1;
this.str2=str2;
this.str3=str3;
}
}
Gender.female
Gender.female是Gender类型对象
Gender.female.toString()=='female'
Gender.female.str1=male1
Gender.female.str2=male2
Gender.female.str3=male3
type
定义函数类型 | type Add=(a:number , b:number) => number |
定义对象类型 | type Point<T>={ x?:T ; readonly y:number } |
重命名类型 | type Num=number |
联合类型 | type Status="success" | "fail" | "pending" type Result=string | number |
交叉类型 | type Person=Student & Children |
语句&运算符
概述
条件语句 | for、if-else、switch switch(value){ | |
扩展运算符 | {...obj,sex:'男'}、[..arr,'男'] | |
解构赋值 | 单层: let [name,age]=arr 多层解构: let {name,age:rAge}=obj 默认值: let {name="Tom",age}=obj | |
==和=== | ==:会做类型转换 ===:不会做类型转换,更严格 | |
终止关键字 | break:终止循环语句 continue:结束循环语句当前迭代 break无法终止try-catch、if-else return:结束函数 |
简化写法
普通写法 | JS简写 |
if(isTrue){ c=a+b return } | if(isTrue) return c=a+b |
if(isTrue) {c=a+b} | if(isTrue) c=a+b |
promise then | async await |
obj.name?.reallyName arr[1]?.[1] | obj.name!=null? obj.name.reallyName: undefined arr[1]!=null? arr[1].[1]: undefined |
{ age:age} | {age} |
- { fn(){} }写法基本等效于{ fn:function(){} },但是{ fn{} }写法中的fn不能作为构造函数使用
- { fn:()=>{} }是箭头函数写法,与上面两种差距明显
a++ & ++a
- let a=0
- console.log(a++) :输出0;先赋值,再加1
- console.log(++a):输出1;先加1,再赋值
&&-||-??
a && b && c | a失败,返回a a成功,判断b:b失败,返回b b成功,判断c:c失败,返回c c成功,返回c |
a || b || c | a成功,返回a a失败,判断b:b成功,返回b b失败,判断c:c成功,返回c c失败,返回c |
a ?? b | a不是null或undefined,返回a a是null或undefined,返回b |
> < ==
2>1 | true;比较数字大小 |
'b'>'a' | true;比较Unicode码大小 UTF-8、UTF-16 和 UTF-32 都是 ASCII 的超集 Javascript采用UTF-8 |
'a'>1 ==> NaN>1 | false;NaN与任何数据比较,都返回false |
== 等值组一 | 0、""、[]、false |
== 等值组二 | null、undefined |
NaN | NaN与任何值比较,都为false |
a?b:c
a?b:c | a? b: c | if(a) b else c |
a?b?e:f:c?g:h | a? b? e: f: c? g: h | if(a){ else h |
num>50?num>75?log('大于75'):log('50~75'):num>25?log('25~50'):log('小于25') | num>50? log('大于75'): | if(num>50){ if(num>75) log('大于75') else log('50~75') }else{ if(num>25) log('25~50') else log('小于25') } |
函数
概述
函数属性 | fun.length :参数个数 fun.name:函数名称 | |
创建实例 | {...}、new fun(...args) | |
剩余参数 | function fun( a, b, ...args ) | |
默认参数 | function fun( a=1, b=2 ) | |
作用域链 | 层层向上寻找对应变量 | |
副作用 | 纯函数:相同的输入=>相同的输出 副作用函数:相同的输入=>可能不同的输出 |
简化写法
普通写法 | JS简写 |
(age)=>{return age} | age=>age |
()=>{console.log(123)} | ()=>console.log(123) |
function t(a,b){return a+b} | (a,b)=>a+b |
立即执行函数
- 作用:不用写变量名,避免变量污染
- 写法:;(function(){...})()
- 等效写法:function test(){...};test()
箭头函数
- 写法:()=>{}
- 不能作为构造函数使用
- 没有自己的this
- this继承自上层函数作用域
- bind、apply、call无法修改它的this指向
- 没有arguments对象,能用剩余参数解决
- 没有prototype对象
装饰器函数
类装饰器 | 唯一参数ClassVar:ClassVar==被装饰的类对象 返回值:void || ClassVar的子类;代替当前类 |
方法装饰器 | 参数1(Object):类原型对象 参数2(string):当前方法名 参数3:{ value:当前方法, writable:true, enumerable:true, configurable:true } 返回值:void || PropertyDescriptor;代替当前方法 |
属性装饰器 | 参数1(Object):类原型对象 参数2(string):当前属性名 返回值:void || PropertyDescriptor;代替当前属性 |
参数装饰器 | 参数1(Object):类原型对象 参数2(string):当前方法名 参数3(number):当前参数在参数列表的索引 |
JavaScript类和继承
概述
- 支持函数中定义函数、类
- 支持类中定义函数、类
- 构造方法写法:constructor(){}
- 构造方法必须调用super(...args)
- 无内部代码块
- 静态属性无法继承
- 访问对象属性的方式:obj.属性名、obj.['属性名']
属性划分
- 原型对象挂载:成员方法
- 实例对象挂载:成员变量
- 函数对象挂载:静态变量和静态方法
- 实例对象.__proto__==函数对象.prototype==原型对象
- 函数对象的静态属性,属于函数对象本身
class Cat{} | 静态变量和静态方法挂载到Cat上 成员方法挂载到Cat.prototype上 |
const cat=new Cat() | 成员变量挂载到cat上 cat.__proto__=Cat.prototype constructor函数的this指向cat 调用constructor |
构造方法
- 默认实现无参构造:constructor(){super()}
- 自定义的构造方法,必须显式调用super(...args)
整合式继承成员属性
- 子类实例存有自己和祖先的成员变量
- 成员变量若重名,只保留最近的成员变量
链式继承成员方法
- 子类原型存有自己的成员方法
- 祖先原型存有自己的成员方法
链式继承原型属性
对象son链式查找变量xxx | son.xxx son.__proto__.xxx son.__proto__.__proto__.xxx ....... |
xxx instanceof yyy | xxx.__proto__.constructor == yyy xxx.__proto__.__proto__.constructor == yyy ...... |
Java类和继承
概述
- 支持函数中定义类
- 支持类中定义函数、类
- 构造方法写法:类名(){}
- 构造方法必须调用super(...args)
- 支持类中定义内部代码块和静态代码块
- 代码块无法继承
- 访问对象属性的方式:obj.属性名
属性划分
- 实例对象:成员变量、成员方法
- 类对象:静态变量、静态方法
构造方法
- 默认实现无参构造:类名(){}
- 构造方法未显式调用super(...args),则会隐式调用super()
- 构造方法未显式调用this(...args),进而间接调用super(...args),则会隐式调用super()
包裹式继承成员属性
链式继承静态属性
类Son链式查找静态变量xxx | Son.xxx Father.xxx Grandfather.xxx |
实例son链式查找静态变量xxx | son.xxx Son.xxx Father.xxx Grandfather.xx |
JavaScript对象
概述
- 数据属性:enumerable、configurable、writable、value
- 访问器属性:enumerable、configurable、get方法、set方法
- 数据属性可以值本类型、引用类型
- 访问器的返回值可以是值类型、引用类型
字面量设置
const info={
fun:function(){},
fun2(){},
_age:43,
get age(){
return this._age
},
set age(newVal){
this._age=newVal
}
}
console.log(info)
{
fun: [Function: fun],
fun2: [Function: fun2],
_age: 43,
age: [Getter/Setter]}
defineproperty设置
Object.defineproperty(obj,prop,configuration)
- obj:对象
- prop:对象属性
- configuration:配置对象
configuration的属性
- enumerable:可遍历;configurable:可再次修改configuration
- writable:可重写;value:当前值
- get、set方法
- 2和3不能同时存在
const info={}
let _name="Tom"
Object.defineProperty(info,'age',{
configurable:true,
enumerable:true,
writable:true,
value:43
})
Object.defineProperty(info,'name',{
configurable:true,
enumerable:true,
get(){
return _name
},
set(newVal){
_name=newVal
}
})
console.log(info)
{
age: 43,
name: [Getter/Setter]
}
对象作用域
JS&Java&this指向
- this指向方法所属对象;谁调用我,我指向谁
- child.func():对象child调用自己的方法func,func的this指向对象child
- child.func():对象child调用父类father的方法func,func的this指向对象child
- child.func():对象child调用爷类grandFather的方法func,func的this指向对象child
- func():JS&严格模式下,this指向undefined
- func():JS&非严格模式,this指向window/globalThis
- 构造方法的this指向实例对象obj
- 静态方法没有this
JS&改变的this指向
- obj.fun.call(child,arg1,arg2...):this指向child
- obj.fun.apply(child,[arg1,arg2...]):this指向child
- fun2=obj.fun.bind(child):方法fun2的this指向child
- Reflect.get(target,key,receiver):target对象中,getter函数的this指向receiver
JS&手写的call方法
function call(fun,thisObj,...args){
thisObj.fun=fun
thisObj.fun(...args)
}
JS&super指向
- Faher中定义fun,fun中的super指向Father.prototype
- super的指向与xxx.fun()的xxx无关
class Father{
fun(){super.xxx}
}
JS模块化概述
<script type="text/javascript">写法
- script标签们,按序依次运行
- 因为script标签作用域的var变量、function xxx(){},会挂载到window对象上
- 所以script标签间的作用域,互通且平级
C语言无模块化
- 编写A.c、B.c、C.c三个文件
- 把三个文件编译成可执行文件run.exe
- 从入口文件的main函数开始运行
- 三个文件的顶层作用域,互通且平级
- 顶层作用域只能定义函数和变量
- 同名函数能定义一次,声明多次
- 同名变量能定义一次,声明多次
C++的module模块化
- 所有模块的首行:export module 模块名
- 导出语法:export 变量名/函数名
- 导入语法:import 模块名
- 模块名不能重复
模块分类
引入方式 | 内容 | |
自定义模块 | 相对路径、绝对路径、文件名 | 第三方模块、自己封装的模块 |
内置模块 | 文件名 | fs、path、http模块等 |
内置对象 | 无需引入 | Object、Array、String、Math、Date |
函数作用域
父函数中定义子函数
- 在FatherFn函数中定义ChildFn函数
- 把ChildFn函数传入UncleFn函数
- 在UncleFn函数中调用ChildFn函数
- 函数ChildFn的父级作用域仍是Father函数作用域
父函数中定义对象.子函数
- 在FatherFn函数中定义ChildObj对象,ChildObj对象定义ChildFn函数
- 把ChildObj对象传入UncleFn函数
- 在UncleFn函数中调用ChildObj.ChildFn函数
- ChildFn函数的父级作用域仍是Father函数作用域
Other
- 把FatherFn、UncleFn换成FatherModule,UncleModule。效果类似
- 父函数中定义子函数:会发生函数提升
- 子函数调用的时候,才会真正与父作用域发生关联
模块引入策略
绝对路径引入 | import {xx,yy} from 'file://绝对路径' |
相对路径引入 | import {xx,yy} from './xxx' 相对的基准是:当前JS文件 |
文件名引入 | import Vue from 'vue' 在 内置模块 中寻找Vue 在 当前目录/node_modules/vue 中寻找Vue 在 上层目录/node_modules/vue 中寻找Vue …… 顶级目录依旧没找到,报错 |
路径最后一级 | 是xxx.js文件,取xxx.js文件 是xxx文件夹,取xxx的入口文件 |
CommonJS&&ES6
node中,CommonJS转ES6
- 方式一:把xxx.js后缀改为xxx.mjs
- 方式二:在package.json中添加 "type": "module"
require导入
- 动态导入
- 运行过程中,遇到require语句,就进行导入操作
import导入
- 静态导入
- import语句会提升到文件头部
- 先理清导入顺序,再运行代码
- 最先导入的文件,最后运行
eval(str)实现动态生成代码
- 参数str:string字符串
- str字符串格式:str字符串必须符合js代码规范
- 返回值:str中最后一个语句的值
- str代码的作用域<=>调用eval函数的作用域
CommonJS模块化
导入导出
导出变量 | module.exports={...} module.exports.xxx=yyy |
导入变量 | const xxxExports=require('xxx') xxxExports==module.exports |
module和require
console.log(module)
PS C:\Users\Administrator\Desktop\many_env\my_node> node .\test.js
Module {
'9': [Function: internalRequire],
id: '.',
path: 'C:\\Users\\Administrator\\Desktop\\many_env\\my_node',
exports: {},
filename: 'C:\\Users\\Administrator\\Desktop\\many_env\\my_node\\test.js',
loaded: false,
children: [],
paths: [
'C:\\Users\\Administrator\\Desktop\\many_env\\my_node\\node_modules',
'C:\\Users\\Administrator\\Desktop\\many_env\\node_modules',
'C:\\Users\\Administrator\\Desktop\\node_modules',
'C:\\Users\\Administrator\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
}
console.log(require)
PS C:\Users\Administrator\Desktop\many_env\my_node> node .\test.js
[Function: require] {
resolve: [Function: resolve] { paths: [Function: paths] },
main: Module {...},
extensions: [Object: null prototype] {
'.js': [Function (anonymous)],
'.json': [Function (anonymous)],
'.node': [Function (anonymous)]
},
cache: [Object: null prototype] {
'C:\\Users\\Administrator\\Desktop\\many_env\\my_node\\test.js': Module {...}
}
}
console.log(arguments)
PS C:\Users\Administrator\Desktop\many_env\my_node> node .\test.js
[Arguments] {
'0': {},
'1': [Function: require] {...},
'2': Module {...},
'3': 'C:\\Users\\Administrator\\Desktop\\many_env\\my_node\\test.js',
'4': 'C:\\Users\\Administrator\\Desktop\\many_env\\my_node'
}
手写require
function myRequire(filename){
//将filename转换成绝对路径
switch(filename){
case 绝对路径: break
case 相对路径: filename=path.resolve(filename);break
case 文件名: filename=toAbs(filename);break
}
for(cacheItem in myRequire.cache){
if(cacheItem==filename) return myRequire.cache[filename].exports
}
const pathname=path.dirname(filename)
const code=fs.readFileSync(filename,'utf8')
const module={
id:filename,
filename,
path:pathname,
exports:{},
loaded:false
}
eval(`
function callFn(exports,myRequire,module,__filename,__dirname){
${code}
};
callFn(module.exports,myRequire,module,filename,pathname)
`)
module.loaded=true
myRequire.cache[filename]=module
return module.exports
}
ES6模块化
导入写法
- impor导入的数据,用常量来接收
- 批量导入:import { xxx as xx , yyy , default as dd } from 'zzz'
- 统一导入:import * as xxx from 'yyy'(包含default)
- 默认导入:import xxx from 'yyy'
- 动态导入 :let result=import('./xxx');result是promise对象
导出写法
- 批量导出:export { xxx as xx , yyy , zzz as default }
- 定义导出:export let key=value
- 默认导出:export default variable
- 导出命名变量:export * from 'yyy'(不包含default)
- 导出默认变量:export { default } from 'vvv'
- 批量导出:export { xxx as xx , yyy , default as zz } from 'vv'
导入JSON
- 静态引入json:import json from './package.json' assert {type:'json'}
- 动态引入json:let json=await import('./package.json',{assert :{type:'json'}})
import对象
- import.meta.url:当前模块绝对目录
Java模块化
类即是模块
静态导入
导入方式 | 内容 | |
自定义模块 | 需要配置classpath 需要写包名 | classpath路径下的class文件 |
内置模块 | 无需配置classpath 需要写包名 | JAVA_HOME\lib\src下class文件 |
内置类 | 无需配置classpath 不用写包名 | JAVA_HOME\lib\src\java.base\java\lang下class文件 |
使用同包的类 | 类名 |
使用不同包的类 | 包名.类名 (import 包名)+类名 |
动态导入(反射机制)
动态类加载
- main方法执行前,会提前加载把需要的类加载到内存中
- main方法运行时,反射机制能动态的把需要的类加载到内存中
面向字节码编程
- 字节码能生成类构造方法对象、类属性对象、类方法对象
- 类构造方法对象能生成类实例
- 类方法对象、类属性对象能突破访问修饰符和多态的限制
- 类方法对象可以获取注解信息,进而根据注解信息为类添加相应功能
字节码对象
- 包目录:类字节码.getProtectionDomain().getCodeSource().getLocation().getPath()
- 包名:类字节码.getPackageName()
动态生成类
- 动态拼接一个字符串类,并转成二进制码
- 把二进制码编译成Java字节码
- 虚拟机动态读取Java字节码
JS微任务和宏任务
分类
- 放入微任务:Promise,queueMicrotask
- 放入宏任务:setTimeout,setInterval,读取文件回调,ajax回调
运行过程
- 把同步代码,加入宏任务队列
- 取出一个宏任务(首次即同步代码)运行,
- 取出所有微任务运行
- 取出一个宏任务运行
- 取出所有微任务运行
- ....
- 直到宏任务和微任务队列都没有可执行代码
Promise构造
Promise {
[[PromiseState]]: 'fulfilled',
[[PromiseResult]]: 123,
Symbol(async_id_symbol): 5,
Symbol(trigger_async_id_symbol): 1
}
new Promise
let pro=new Promise((res,rej)=>{
res(value) 或 rej(value) 或 throw new Error(value) 或 都不满足
})
调用res(value) | value是非promise对象: pro是成功的promise对象 pro的PR==value value是promise对象: pro的值取决于value |
调用rej(value) | pro是失败的 promise pro的PR==value |
抛出错误 | pro是失败的promise对象 |
rej()未调用 res()未调用 没有抛出错误 | pro是未定义的promise对象 |
取决于 value的含义:
value是成功的 promise对象
pro 是成功的promise对象pro的PR==value的PR
value是失败的 promise对象
pro 是失败的 promise对象pro的PR==value的PR
value是未定义的promise对象
pro 是未定义的promise对象
Promise.resolve
let pro=Promise.resolve(value) //相当于 res(value)
value是非promise对象
pro是成功的promise对象
pro的PR==value
value是promise对象
pro的值取决于 value
Promise.reject
let pro=Promise.reject(value) //相当于 rej(value)
- pro是失败的promise
- pro的PR==value
Promise.all
let p=Promise.all([p1,...,pn])
p1~pn有失败的promise
p是失败的promise
p的PR==首个失败promise的PR
p1~pn无失败的promise
p是成功的promise
p的PR==[p1的PR,…,pn的PR]
Promise.race
let p=Promise.race([p1,...,pn])
- pj最先改变状态
- p的状态==pj的状态
- p的PR==pj的PR
async函数
async function fn(){
let result=await Promise.resolve(promiseValue)
return value
}
let pro2=fn()
xxx.yyy(async ()=>{
let result=await zzz.hhh()
return value
})
- await前面的语句同步执行
- await后面的语句异步执行
- 函数执行时报错,pro2是失败的promise对象
return的value是非promise对象
pro2是成功的promise
pro2的PR==value
return的value是promise对象
pro2的值取决于value
顶级await使用条件
- node版本:15+
- package.json配置:"type": "module"
- tsconfig.json配置:"module": "esnext", "target": "es2017+"
Promise回调
pro.then回调
函数返回值为undefined的情况
- 函数中未显式调用 return xxx
- 函数中显式调用 return
- 函数中显示调用 return undefined
let pro2=pro.then((resValue)=>{ //funOne
return value
},(rejValue)=>{ //funTwo
return value
})
回调函数的选择
pro是成功的promise
调用funOne
resValue==pro的PR
pro是失败的promise&&存在funTwo
调用funTwo
rejValue==pro的PR
pro是失败的promise&&不存在funTwo
pro2是失败的promise
pro2的PR==pro的PR
pro是未定义的promise
不调用回调函数
pro2是未定义的promise对象
运行回调函数
return的value是非promise对象
pro2是成功的promise对象
pro2的PR==value
return的value是promise对象
pro2的值取决于 value
回调函数执行时报错
pro2是失败的promise对象
pro.catch回调
let pro2=pro.catch((rejValue)=>{return value})
是否运行回调函数
pro是成功的promise
不运行回调函数
pro2是成功的promise
pro2的PR==pro的PR
pro是失败的promise
执行回调
运行回调函数
return的value是非promise对象
pro2是成功的promise
pro2的PR==value
return的value是promise对象
pro2的值取决于value
回调函数执行时报错
pro2是失败的promise对象
Promise强化
实现Promise
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.resultVal = null;
this.onResolveCallbacks = [];
this.onRejectCallbacks = [];
const resolve = (value) => {
queueMicrotask(() => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.resultVal = value;
this.onResolveCallbacks.forEach(callback=>{
callback(this.resultVal
});
}
})
};
const reject = (error) => {
queueMicrotask(()=>{
if (this.state === 'pending') {
this.state = 'rejected';
this.resultVal = error;
this.onRejectCallbacks.forEach(callback=>{
callback(this.resultVal
)};
}
})
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onResolve, onReject) {
return new MyPromise((resolve, reject) => {
this.onResolveCallbacks.push((resultVal) => {
try {
const result = onResolve(resultVal);
resolve(result);
} catch (error) {
reject(error);
}
});
this.onRejectCallbacks.push((resultVal) => {
try {
const result = onReject(resultVal);
resolve(result);
} catch (error) {
reject(error);
}
});
});
}
catch(reject) {
return this.then(null, reject);
}
}
异步回调转同步
function awaitImgUpload() {
return new Promise((resolve, reject) => {
const observableImg = qiniu.upload(
tempImgFile.value.raw,
imgStore +
moment().format('YY/MM/DD/HH:mm:ss/') +
tempImgFile.value.name, uploadToken.value,
{},
{}
)
observableImg.subscribe({
next(res) {
console.log(res)
},
error(err) {
reject(err)
ElMessage.error("图片上传失败")
},
complete(res) {
resolve(res.key)
ElMessage.success("图片上传成功")
}
})
})
}
async function test(){
const result=await awaitImgUpload()
...... //在七牛云图片上传完成后,要做的逻辑
}
forEach/Async1
function test(){
arr.forEach(async (item)=>{ //async的正确放入方式
await ......
})
}
forEach/Async2
function wait() {
return new Promise(resolve =>
setTimeout(resolve, 2000));
}
function myForEach(arr, fn) {
for (let i = 0; i < arr.length; i++) {
fn(arr[i])
}
}
const arr=["a","b"]
async function main1() {
console.log("Start");
arr.forEach(async (item) => {
await wait()
await console.log(item)
})
console.log("End")
}
async function main2() {
console.log("Start");
for (let i = 0; i < arr.length; i++) {
await wait()
await console.log(arr[i])
}
console.log("End");
}
async function main3() {
console.log("Start");
myForEach([1, 2], async (item) => {
await wait()
await console.log(item)
})
console.log("End")
}
//main1()
//main2()
//main3()
等待三秒
new Promise(resolve => setTimeout(resolve, 3000));
Java多线程
run方式的实现
- 继承的方式:重写run方法
- 传参的方式:传入run方法
线程状态
线程等待API
- Thread.yield():当前线程主动让出时间片
- Thread.sleep(time):当前线程交出时间片,并休眠time毫秒
- myThread.join():当前线程主动阻塞,直到myThread线程完成
- 锁对象.await():当前线程主动阻塞,直到被唤醒;只能在同步语句中使用
- myThread.interrupt():给线程打上true标识
- Tread.interrupted():判断当前线程的标识,并把线程标识设置为false
- 线程等待遇到true标识=>会结束等待&抛出异常&标识设为false
- 锁对象.notify():唤醒await阻塞的线程;只能在同步语句中使用
Thread对象信息设置API
- Thread myThread=new Thread()
- Thread.currentThread()==myThread:获取当前线程
- myThread.start():开启一个线程(一个myThread实例只能调用一次start方法)
- myThread.setName(str):设置线程名称
- myThread.getName():获取线程名称
- myThread.threadId():获取线程id
- myTread.setPriority(1~10):设置线程优先级
- myTread.getPriority:获取线程优先级
- myTread.isAlive():线程在运行=>true;线程未开启或结束=>false
- myThread.setDaemon():设置成守护线程,前台进程结束=>程序结束=>守护进程结束
锁
必要性
public class Main {
public int num=0;
public void add(){
for (int i=0;i<10000;i++){
num++;
}
}
public static void main(String[] args) {
Main main = new Main();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
main.add();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
main.add();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(main.num);
}
}
运行结果:main.num<=20 000
- 线程1读取num=100
- 线程2读取num=100
- 线程1执行num=100+1=101
- 线程2执行num=100+1=101
- num本应等于102,由于上述原因导致num数值变小
概述
- 乐观锁:能直接访问修改数据;写回修改数据时,会验证其它线程是否已修改数据
- 悲观锁:获得锁后,才能访问修改数据
synchronized锁
class Main{
Obj obj=new Obj();
//同步代码块,锁对象是obj对象
public void show1(){
synchronized(obj){
......
}
}
//同步实例方法,锁对象是Main实例对象
public synchronized void show2(){
......
}
//同步静态方法,锁对象是Main.class
public synchronized static void show3(){
......
}
}
同步代码块、同步方法运行结束,会释放锁
同步代码块,同步方法抛出异常,会释放锁
ThreadLocal
线程池和数据库连接池
- 服务器开启,会创建线程池和连接池
- 用户发送请求,会从池中取出线程对象和连接对象
- 服务器完成响应,会把连接对象和线程对象放入池中
Map<Thread,Connection>
- 单次请求调用的servlet的线程对象是同一个
- 为确保连接对象也是同一个,我们把线程对象和连接对象以键值对的形式存到Map集合中
- 服务器完成响应时,需要把响应的键值对从Map集合中除去
异常
种类
- 普通异常:如果捕捉并处理,能避免程序崩溃
- 无解异常:无法处理,程序直接崩溃
处理普通错误
- try捕捉异常,catch处理错误
- 向上抛出异常(JavaScript会自动上抛)
try-catch-finally
t-c-f t-f | try语句块未发生异常 执行finally语句 |
t-c-f | try语句块发生异常 try语句块后面的代码不再执行 默认生成异常对象 把异常对象传入catch语句块 执行catch语句块 执行finally语句 |
t-f | try语句块发生异常 try语句块后面的代码不再执行 默认生成异常对象 执行finally语句 把异常对象向上抛出 |
无t-c-f | 发生异常 默认生成异常对象 把异常对象向上抛出 |