Promise详解:手写Promise底层-实现Promise所有的功能和方法

前言

	目标:封装一个promise,更好的理解promise底层逻辑需求:实现以下promise所有的功能和方法 如下图所示

在这里插入图片描述

一、构造函数编写

步骤

1、定义一个TestPromise类,
2、添加构造函数,
3、定义resolve/reject,
4、执行回调函数
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>手写Promise</title>
</head><body><h2>构造函数</h2><script>// 1. 定义类class TestPromise{// 2. 添加构造函数constructor(func){// 3、声明 resolve rejectconst resolve = (result)=>{// TODOconsole.log("resolve",result)}const reject = (result)=>{// TODOconsole.log("reject",result)}// 4. 执行回调函数func(resolve,reject)} }// ------------- 测试代码 -------------const p = new TestPromise((resolve, reject) => {console.log("调用了")resolve('success')//   reject('error')})</script>
</body></html>

在这里插入图片描述

二、promise的状态和原因

分析

promise有pending->fulfilled pending->rejected,
所以我们要为我们的实例类添加状态以及导致状态变化的原因
state状态 result原因
而且当pending状态一旦发生变化,便不可逆

步骤

1、添加状态(pending / fulfilled / rejected)
2、添加原因
3、调整resolve/reject
4、状态不可逆
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>手写Promise</title>
</head><body><h2>构造函数</h2><script>// 通过变量保存状态,便于后续使用const PENDING = 'pending'const FULFILLED = 'fulfilled'const REJECTED = 'rejected'class TestPromise{// 1. 添加状态(pending / fulfilled / rejected)state = PENDING// 2. 添加原因result = undefinedconstructor(func){// 改状态: pending->fulfilled// 记录原因const resolve = (result)=>{// 加判断状态不可逆if(this.state===PENDING){this.state = FULFILLEDthis.result = result}}// 改状态: pending->rejected// 记录原因const reject = (result)=>{if(this.state===PENDING){this.state = REJECTEDthis.result = result}}func(resolve,reject)} }// ------------- 测试代码 -------------const p = new TestPromise((resolve, reject) => {resolve("fulfilled")// 只会执行上面的 状态变成fulfilled后下面不再执行reject("rejected")})</script>
</body></html>

在这里插入图片描述

三、then方法

分析

promise有成功和失败回调,异步多次调用

步骤一:成功和失败回调

1、添加实例方法
2、参数判断,判断传入的是不是回调函数
3、根据状态执行不同的回调函数(成功or失败)

注意如果传入的不是函数,成功和失败的回调默认实现是不同的,以下是文档,我们参考文档实现
在这里插入图片描述

class TestPromise{···constructor(func){···}// 1、添加实例方法then(onFulfilled, onRejected){// 2、参数判断,判断传入的是不是回调函数// 是函数返回函数,不是返回原值onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>x// 是函数返回函数,不是抛出onRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}// 3、根据状态执行不同的回调函数(成功or失败)if(this.state === FULFILLED){onFulfilled(this.result)}else if(this.state === REJECTED){onRejected(this.result)}}}// ------------- 测试代码 -------------···p.then(res=>{console.log('成功回调',res)}, err=>{console.log('失败回调',err)})

步骤二:异步和多次调用

1、定义实例属性(pending状态下保存then保存的回调函数)
2、执行保存的成功和失败回调
class TestPromise{···// 1、对象数组保存成功和失败的回调函数{onFulfilled, onRejected} // # 定义属性私有,只有内部可以访问到#handlers = [] constructor(func){const resolve = (result)=>{if(this.state===PENDING){this.state = FULFILLEDthis.result = result// 3、执行成功的回调this.#handlers.forEach(({onFulfilled})=>{onFulfilled(this.result)})}}const reject = (result)=>{if(this.state===PENDING){this.state = REJECTEDthis.result = result// 3、执行失败的回调this.#handlers.forEach(({onRejected})=>{onRejected(this.result)})}}func(resolve,reject)}then(onFulfilled, onRejected){onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>xonRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}if(this.state === FULFILLED){onFulfilled(this.result)}else if(this.state === REJECTED){onRejected(this.result)}else if(this.state === PENDING){// 2、保存回调函数this.#handlers.push({onFulfilled, onRejected})}}}// ------------- 测试代码 -------------const p = new TestPromise((resolve, reject) => {// 异步setTimeout(()=>{resolve("fulfilled")// reject("rejected")},2000)})p.then(res=>{console.log('then1',res)}, err=>{console.log('then1',err)})p.then(res=>{console.log('then2',res)}, err=>{console.log('then2',err)})
解析:当定义类存在setTimeout时,这时的state属性为pending,then执行了
(所以我们要在then方法中加保存当前回调函数,当倒计时结束,调取resolve时我们执行回调函数数组)

四、异步任务

在这里插入图片描述

分析

promise.then()里面执行的是异步任务
所以我们的promise中也要实现异步处理,实现{1、核心API2、函数封装
}
核心API,vue2中执行异步的API有Promise.then、MutationObserver、setImmediate、setTimeout
选用queueMicrotask、MutationObserver、setTimeout
queueMicrotask:直接执行一个异步任务(node11开始支持、支持新式浏览器、IE不支持)
MutationObserver:dom节点改变执行异步任务(IE11支持)
setTimeout都支持
// 使用
queueMicrotask((fun)=>{fun() // 回调函数直接异步执行
})
const obs = new MutationObserver(()=>{// ...
})
const divNode = document.createElement('div')
// 参数一:观察的dom节点 参数二:观察的选项 childList观察子节点
obs.observe(divNode, { childList: true }) // 检测子节点是否改变
divNode.innerText = 'tets' // 开始修改子节点
// 节点发生改变执行异步回调

基于核心API完成异步任务的函数封装

1、定义函数,接收一个回调函数
2、调用核心api(queueMicrotask,MutationObserver,setTimeout)
3、在我们的promise调用封装的函数

1、定义函数,接收一个回调函数

// 1、定义函数function runAsynctask(callback){// 2. 调用核心api(queueMicrotask,MutationObserver,setTimeout)if(typeof queueMicrotask === 'function'){queueMicrotask(callback)}else if(typeof MutationObserver === 'function'){const obs = new MutationObserver(callback)const divNode = document.createElement('div')obs.observe(divNode, { childList: true })// 不需要把节点添加到页面divNode.innerText = 'test'}else{setTimeout(callback,0)}}

2、在我们的promise调用封装的函数

	class TestPromise{···then(onFulfilled, onRejected){onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>xonRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}if(this.state === FULFILLED){
+++             runAsynctask(()=>{onFulfilled(this.result)})}else if(this.state === REJECTED){
+++             runAsynctask(()=>{onRejected(this.result)})}else if(this.state === PENDING){this.#handlers.push({onFulfilled:()=>{
+++                 runAsynctask(()=>{onFulfilled(this.result)})}, onRejected:()=>{
+++                 runAsynctask(()=>{onRejected(this.result)})}})}}}// ------------- 测试代码 -------------console.log(1)const p = new TestPromise((resolve, reject) => {console.log(2)resolve(3)// reject("rejected")})p.then(res=>{console.log(res)})console.log(4)

五、链式编程

分析

promise.then().then()
promise可以一直then方法
核心:
1、then方法需要返回是支持.then调用的(promise实例)
2、根据 pending、fulfilled、rejected三种状态支持链式编程
3、在这个promise实例获取上一个then的返回值并处理{1、处理返回值2、处理异常3、处理返回promise4、处理重复引用}

1、处理返回值和处理异常

then方法中新建一个promise实例 获取返回值 处理异常
then(onFulfilled, onRejected){onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>xonRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}// 1. 返回新Promise实例const p2 = new TestPromise((resolve, reject) => {if (this.state === FULFILLED) {runAsynctask(() => {// 2. 获取返回值try {const x = onFulfilled(this.result)//    2.1 处理返回值resolve(x)} catch (error) {//    2.2 处理异常console.log('捕获异常', error)reject(error)}})} else if (this.state === REJECTED) {runAsynctask(() => {onRejected(this.result)})} else if(this.state === PENDING){this.#handlers.push({onFulfilled:()=>{runAsynctask(()=>{onFulfilled(this.result)})}, onRejected:()=>{runAsynctask(()=>{onRejected(this.result)})}})}})return p2}// ------------- 测试代码 -------------const p = new TestPromise((resolve, reject) => {resolve("resolve")// reject("rejected")})p.then(res=>{console.log(res)// throw 'throw error'return 2}).then(res=>{console.log(res)},err=>{console.log(err)})

2、处理返回promise

如果promise.then()里面返回依旧是一个promise,这个时候需要怎么处理?

const p = new TestPromise((resolve, reject) => {resolve(1)})p.then(res => {return new TestPromise((resolve, reject) => {resolve(2)// reject('error')})}).then(res => {console.log('p2:', res) // 2}, err => {console.log('p2:', err) // err})
处理思路:
1、拿到返回值、判断是不是promise实例
2、调去这个promise实例的then方法就可以了
class TestPromise{···then(onFulfilled, onRejected){···const p2 = new TestPromise((resolve, reject) => {if (this.state === FULFILLED) {runAsynctask(() => {try {const x = onFulfilled(this.result)// 1.处理返回Promise+++                      if (x instanceof TestPromise) {// 2. 调用then方法// x.then(res => console.log(res), err => console.log(err))
+++                           x.then(res => resolve(res), err => reject(err))} else {resolve(x)}} catch (error) {console.log('捕获异常', error)reject(error)}})} else if (this.state === REJECTED) {···} else if(this.state === PENDING){···}})return p2}}

3、处理返回promise重复调用

 const p = new Promise((resolve, reject) => {resolve("resolve")})const p2 = p.then(res=>{// throw 'throw error'return p2})p2.then(res=>{},err=>console.log('err:', err))

原生的promise会有重复调用的错误提示
在这里插入图片描述

思路:对promise进行比较、并抛出异常
if (this.state === FULFILLED) {runAsynctask(() => {try {const x = onFulfilled(this.result)// 1. 处理重复引用if (x === p2) {// console.log('返回了p2')// 2. 抛出错误 Chaining cycle detected for promise #<Promise>throw new TypeError('Chaining cycle detected for promise #<Promise>')}if (x instanceof TestPromise) {x.then(res => resolve(res), err => reject(err))} else {resolve(x)}} catch (error) {reject(error)}})

4、rejected状态

抽取公共方法处理promise和重复调用
//  抽取函数function resolvePromise(p2, x, resolve, reject) {if (x === p2) {throw new TypeError('Chaining cycle detected for promise #<Promise>')}if (x instanceof TestPromise) {x.then(res => resolve(res), err => reject(err))} else {resolve(x)}}

处理返回值

const p2 = new TestPromise((resolve, reject) => {if (this.state === FULFILLED) {runAsynctask(() => {try {// 获取返回值const x = onFulfilled(this.result)resolvePromise(p2, x, resolve, reject)} catch (error) {reject(error)}})} else if (this.state === REJECTED) {runAsynctask(() => {// 1、处理异常
+++                     try {// 获取返回值
+++                       const x = onRejected(this.result)resolvePromise(p2, x, resolve, reject)} catch (error) {reject(error)}})}

5、pending状态

 } else if(this.state === PENDING){this.#handlers.push({onFulfilled:()=>{runAsynctask(()=>{try {// 获取返回值const x = onFulfilled(this.result)resolvePromise(p2, x, resolve, reject)} catch (error) {reject(error)} })}, onRejected:()=>{runAsynctask(()=>{try {// 获取返回值const x = onRejected(this.result)resolvePromise(p2, x, resolve, reject)} catch (error) {reject(error)}})}})}···// ------------- 测试代码 -------------const p = new TestPromise((resolve, reject) => {setTimeout(() => {resolve("resolve")}, 2000);})const p2 = p.then(res => {throw 'error'// return p2// return 2//   return new TestPromise((resolve, reject) => {//     reject('Promise-error')//   })})p2.then(res=>{return console.log('err:', err)}, err => {console.log('p2-err:', err)})

六、实例方法-catch -finally

实例方法-catch

我们先看下官网对catch的讲述
在这里插入图片描述
由此我们可以得出:

// catch是什么? 是语法糖!!!
new Promise(() => {
}).catch(() => {
})
// 等同于
new Promise(() => {
}).then(null, () => {
})
那么这个实力方法时处理以下两点1、then中没有写err函数const p = new TestPromise((resolve, reject) => {reject('reject-error')})p.then(res => {console.log('res:', res)}).catch(err => {console.log('err:', err)})2、创建函数时的异常 没有resolve reject
const p = new TestPromise((resolve, reject) => {throw 'throw-error'})

实现:

1、在类then方法下面新建catch方法
/**
* catch方法* 1. 内部调用then方法* */catch(onRejected) {// 1. 内部调用then方法return this.then(undefined, onRejected)}2、在constructor构造函数中 处理异常constructor(func) {// pending->fulfilledconst resolve = (result) => {if (this.state === PENDING) {this.state = FULFILLEDthis.result = resultthis.#handlers.forEach(({ onFulfilled }) => {onFulfilled(this.result)})}}// pending->rejectedconst reject = (result) => {if (this.state === PENDING) {this.state = REJECTEDthis.result = resultthis.#handlers.forEach(({ onRejected }) => {onRejected(this.result)})}}// 2. 处理异常
+++    try {func(resolve, reject)} catch (error) {// console.log('error:', error)reject(error)}}

实例方法-finally

以下时官网promise对finnally的介绍

在这里插入图片描述

在这里插入图片描述
由此可知,其内部调取then(onFinally, onFinally)

/**
* finally方法* 1. 内部调用then方法* */
finally(onFinally) {return this.then(onFinally, onFinally)
}

七、静态方法-resolve -reject-race-all-allSettled-any

注意:静态的是指向类自身,而不是指向实例对象,主要是归属不同,这是静态属性,静态方法的核心
也就是说类可以访问到,但实例对象访问不到、继承类也可以访问到

静态方法-resolve

官网介绍:
在这里插入图片描述
由此可知:resolve是把一个值转换为promise实例,如果本身就是promise实例那么直接返回
在这里插入图片描述
实现:

/*** 静态方法-resolve*  1. 判断传入值*  2.1 Promise直接返回*  2.2 转为Promise并返回(fulfilled状态)* */static resolve(value) {// 1. 判断传入值if (value instanceof TestPromise) {// 2.1 Promise直接返回return value}// 2.2 转为Promise并返回(fulfilled状态)return new TestPromise((resolve) => {resolve(value)})}

测试

 // ------------- 测试代码 手写Promise  -------------TestPromise.resolve(new TestPromise((resolve, reject) => {resolve('resolve')// reject('reject')// throw 'error'})).then(res => {console.log('res:', res)}, err => {console.log('err:', err)})TestPromise.resolve('hahah').then(res => {console.log(res)})

静态方法-reject

在这里插入图片描述
由此可知静态方法-reject为传递一个reject的promise对象,实现:

	/*** 静态方法-reject* 1. 返回rejected状态的Promise* */static reject(value) {// 1. 返回rejected状态的Promisereturn new TestPromise((undefined, reject) => {reject(value)})}
// 测试:
// ------------- 测试代码 手写Promise  -------------TestPromise.reject('error').catch(res => {console.log(res)})

静态方法-race

在这里插入图片描述
从官网可知,race接收一个promise对象数组,返回第一个最快执行完的promise,无论它是rejected还是fuilled
注意:如果传递的不是promise,会把值默认转换成promise对象,并执行resolve
注意:如果是传递的不是数组,会报以下错误
在这里插入图片描述

/*** 静态方法-race* 1. 返回Promise* 2. 判断是否为数组 错误信息:Argument is not iterable* 3. 等待第一个敲定* */static race(promises) {// 1. 返回Promisereturn new TestPromise((resolve, reject) => {// 2. 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError('Argument is not iterable'))}// 3. 等待第一个敲定promises.forEach(p => {// p.thenTestPromise.resolve(p).then(res => { resolve(res) }, err => { reject(err) })})})}
注意:因为返回时一个promise对象,所以 promises.forEach数组遍历中,
最快的一个会调用resolve或reject,
只要其中一个执行,promises对象便不会在执行其他的
// ------------- 测试代码 手写Promise  -------------const p1 = new TestPromise((resolve, reject) => {setTimeout(() => {resolve(1)}, 1000)})const p2 = new TestPromise((resolve, reject) => {setTimeout(() => {reject(2)}, 2000)})// TestPromise.race([p1, p2]).then((res) => {TestPromise.race([p1, p2, 'hahah']).then((res) => {// TestPromise.race().then((res) => {console.log('res:', res)}, err => {console.log('err:', err)})

静态方法-all

分析:
在这里插入图片描述

promise.all([promise1,promise2,promise3])
all方法接收一个promise数组,
如果都是resolve 并返回传入数组顺序的promise的res数组
当有rejected时,返回第一个rejected

思路:
在这里插入图片描述

 /*** 静态方法-all*  1. 返回Promise实例*  2. 判断是否为数组 错误信息:Argument is not iterable*  3. 空数组直接兑现*  4. 处理全部兑现*    4.1 记录结果*    4.2 判断全部兑现*  5. 处理第一个拒绝* */static all(promises){// 1. 返回Promisereturn new TestPromise((resolve, reject) => {// 2. 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError('Argument is not iterable'))}// 3. 空数组直接兑现promises.length === 0 && resolve(promises)// 4. 处理全部兑现 // 思路(promise异步执行顺序,返回数组和原来的传入数组顺序可能不同索引 // 所以我们可以根据传入数组的索引值对最终数组进行数组排序)// 4.1 记录结果const results = []let count = 0promises.forEach((p, index)=>{TestPromise.resolve(p).then(res=>{results[index] = res// 4.2 判断全部兑现// 为什么不能用 results.length进行判断,因为如果第一个执行完返回的是第三项results[3] = res 当前数组情况是[ , , res]// 我们用count次数进行判断count++count===promises.length && resolve(results)}, err=>{// 5. 处理第一个拒绝reject(err)})})})}// ------------- 测试代码 手写Promise  -------------const p1 = TestPromise.resolve(1)const p2 = new TestPromise((resolve, reject) => {setTimeout(() => {resolve(2)// reject('error')}, 1000)})const p3 = 3const p4 = new TestPromise((resolve, reject) => {setTimeout(() => {// resolve(4)reject('error-1234')}, 2000)})TestPromise.all([p1, p2, p3, p4]).then(res => {// TestPromise.all().then(res => {//   TestPromise.all([]).then(res => {console.log('res:', res)}, err => {console.log('err:', err)})

静态方法-allSettled

分析:
在这里插入图片描述
我们promise测试一下
在这里插入图片描述

由此发现:它和all方法类似,依旧是等待所有的promise敲定返回,顺序也是传入顺序,
但不是第一个rejected抛出,而是全部执行完后以 status:fulfilled,value:value返回resolve,以status:rejected,reason:value返回rejected的数组

实现:
在这里插入图片描述

/*** 静态方法-allSettled* 1. 返回Promise* 2. 数组判断 错误信息: Argument is not iterable* 3. 为空直接敲定* 4. 等待全部敲定*  4.1 记录结果*  4.2 处理兑现{status:'fulfilled',value:''}*  4.3 处理拒绝{status:'rejected',reason:''}* */static allSettled(promises) {// 1. 返回Promisereturn new TestPromise((resolve, reject) => {// 2. 数组判断if (!Array.isArray(promises)) {return reject(new TypeError('Argument is not iterable'))}// 3. 为空直接敲定promises.length === 0 && resolve(promises)// 4. 等待全部敲定// 4.1 记录结果const results = []let count = 0promises.forEach((p, index) => {TestPromise.resolve(p).then(res => {// 4.2 处理兑现{status:'fulfilled',value:''}results[index] = { status: FULFILLED, value: res }count++count === promises.length && resolve(results)}, err => {// 4.3 处理拒绝{status:'rejected',reason:''}results[index] = { status: REJECTED, reason: err }count++count === promises.length && resolve(results)})})})}
// ------------- 测试代码 手写Promise -------------const p1 = TestPromise.resolve(1)const p2 = 2const p3 = new TestPromise((resolve, reject) => {setTimeout(() => {reject(3)}, 1000)})TestPromise.allSettled([p1, p2, p3]).then(res => {// TestPromise.allSettled().then(res => {// TestPromise.allSettled([]).then(res => {console.log('res:', res)}, err => {console.log('err:', err)})

静态方法-any

在这里插入图片描述
在这里插入图片描述

由此可知,any是接收一个promise数组(可以是常量),如果存在一个成功,则直接返回第一个成功的resolve
如果没有成功的,则返回所有的拒绝原因(与传入顺序一致)
注意:空值和空数组都会报错

实现
在这里插入图片描述

 /*** 静态方法-any* 1. 返回Promise,数组判断 错误信息: Argument is not iterable* 2. 空数组直接拒绝 AggregateError([错误原因1..],All promises were rejected)* 3. 等待结果*  3.1 第一个兑现*  3.2 全部拒绝*/static any(promises) {// 1. 返回Promise,数组判断return new TestPromise((resolve, reject) => {if (!Array.isArray(promises)) {return reject(new TypeError('Argument is not iterable'))}// 2. 空数组直接拒绝promises.length === 0 && reject(new AggregateError(promises, 'All promises were rejected'))// 3. 等待结果const errors = []let count = 0promises.forEach((p, index) => {TestPromise.resolve(p).then(res => {// 3.1 第一个兑现resolve(res)}, err => {// 3.2 全部拒绝errors[index] = errcount++count === promises.length && reject(new AggregateError(errors, 'All promises were rejected'))})})})}
// ------------- 测试代码 手写Promise -------------const p1 = new TestPromise((resolve, reject) => {setTimeout(() => {reject(1)}, 2000)})// const p2 = 2const p2 = TestPromise.reject(2)const p3 = new TestPromise((resolve, reject) => {setTimeout(() => {// resolve(3)reject(3)}, 1000)})TestPromise.any([p1, p2, p3]).then(res => {// TestPromise.any().then(res => {// TestPromise.any([]).then(res => {console.log('res:', res)}, err => {console.dir(err)})

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/114881.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线程是如何进行创建的

对于任何一个进程来讲&#xff0c;即便我们没有主动去创建线程&#xff0c;进程也是默认有一个主线程的。线程是负责执行二进制指令的&#xff0c;它会根据项目执行计划书&#xff0c;一行一行执行下去。进程要比线程管的宽多了&#xff0c;除了执行指令之外&#xff0c;内存、…

Go包介绍与初始化:搞清Go程序的执行次序

Go包介绍与初始化&#xff1a;搞清Go程序的执行次序 文章目录 Go包介绍与初始化&#xff1a;搞清Go程序的执行次序一、main.main 函数&#xff1a;Go 应用的入口函数1.1 main.main 函数1.2 main.main 函数特点 二、包介绍2.1 包介绍与声明2.2 非 main包的 main 函数2.3 包的命名…

【vSphere 8 自签名 VMCA 证书】企业 CA 签名证书替换 vSphere VMCA CA 证书Ⅰ—— 生成 CSR

目录 替换拓扑图证书关系示意图说明 & 关联博文1. 默认证书截图2. 使用 certificate-manager 生成CSR2.1 创建存放CSR的目录2.2 记录PNID和IP2.3 生成CSR2.4 验证CSR 参考资料 替换拓扑图 证书关系示意图 本系列博文要实现的拓扑是 说明 & 关联博文 因为使用企业 …

身份证读卡器ubuntu虚拟机实现RK3399 Arm Linux开发板交叉编译libdonsee.so找不到libusb解决办法

昨天一个客户要在RK3399 Linux开发板上面使用身份证读卡器&#xff0c;由于没有客户的开发板&#xff0c;故只能用本机ubuntu虚拟机来交叉编译&#xff0c;用客户发过来的交叉编译工具&#xff0c;已经编译好libusb然后编译libdonsee.so的时候提示找不到libusb&#xff0c;报错…

详解使用sklearn实现一元线性回归和多元线性回归

[Open In Colab] 文章目录 1. 线性回归简介2. 使用sklearn进行一元线性回归3. 线性回归的coef_参数和intercept_参数4. 使用sklearn实现多元线性回归4.1 利用PolynomialFeatures构造输入4.2 进行多元线性回归 5. 总结 import numpy as np import matplotlib.pyplot as plt1. 线…

Leetcode.19 删除链表的倒数第 N 个结点

题目链接 Leetcode.19 删除链表的倒数第 N 个结点 mid 题目描述 给你一个链表&#xff0c;删除链表的倒数第 n n n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输…

Unity中Shader阴影的接收

文章目录 前言一、阴影接受的步骤1、在v2f中添加UNITY_SHADOW_COORDS(idx),unity会自动声明一个叫_ShadowCoord的float4变量&#xff0c;用作阴影的采样坐标.2、在顶点着色器中添加TRANSFER_SHADOW(o)&#xff0c;用于将上面定义的_ShadowCoord纹理采样坐标变换到相应的屏幕空间…

Node.js、Vue的安装与使用(Linux OS)

Vue的安装与使用&#xff08;Linux OS&#xff09; Node.js的安装Vue的安装Vue的使用 操作系统&#xff1a;Ubuntu 20.04 LTS Node.js的安装 安装Node.js Node.js官方下载地址 1.选择合适的系统架构&#xff08;可通过uname -m查看&#xff09;版本安装 2.下载文件为tar.xz格…

uniapp自定义右击菜单

效果图&#xff1a; 代码&#xff1a; 1、需要右击的view: <view class"answer-box" contextmenu.stop.prevent.native"showRightMenu($event, item, content)"> </view>2、右击弹出层&#xff1a; <view v-if"visible" :styl…

智慧矿山:让AI算法提高未戴安全带识别率!

未穿戴安全带识别AI算法&#xff0c;作为智慧矿山的重要应用之一&#xff0c;不仅可以有效提高矿山工作人员的安全意识&#xff0c;还可以降低事故发生的概率。然而&#xff0c;识别准确率的提高一直是该算法面临的挑战之一。为了解决这个问题&#xff0c;研究人员不断努力探索…

JavaEE初阶学习:Servlet

1.Servlet 是什么 Servlet 是一种 Java 程序&#xff0c;用于在 Web 服务器上处理客户端请求和响应。Servlet 可以接收来自客户端&#xff08;浏览器、移动应用等&#xff09;的 HTTP 请求&#xff0c;并生成 HTML 页面或其他格式的数据&#xff0c;然后将响应发送回客户端。S…

【C++】C++11新特性

文章目录 一、C发展简介二、C11简介三、列表初始化1.统一使用{}初始化2.initializer_list类 四、变量的类型推导1.auto2.decltype3.nullptr 五、范围for循环六、STL中一些变化七、final与override八、新的类功能1.新增默认成员函数2.成员变量的缺省值3.default 和 delete4.fina…

LABVIEW 安装教程(超详细)

目录 LabVIEW2017&#xff08;32/64位&#xff09;下载地址&#xff1a; 一 .简介 二.安装步骤&#xff1a; LabVIEW2017&#xff08;32/64位&#xff09;下载地址&#xff1a; 链接&#xff1a; https://pan.baidu.com/s/1eSGB_3ygLNeWpnmGAoSwcQ 密码&#xff1a;gjrk …

JAVA面经整理(MYSQL篇)

索引: 索引是帮助MYSQL高效获取数据的排好序的数据结构 1)假设现在进行查询数据&#xff0c;select * from user where userID89 2)没有索引是一行一行从MYSQL进行查询的&#xff0c;还有就是数据的记录都是存储在MYSQL磁盘上面的&#xff0c;比如说插入数据的时候是向磁盘上面…

C++ 类和对象(六)赋值运算符重载

1 运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c; 也具有其返回值类型&#xff0c;函数名字以及参数列表&#xff0c;其返回值类型与参数列表与普通的函数类似。 函数名字为&#xff1a;关键字operator后面接需…

css之Flex弹性布局(父项常见属性)

文章目录 &#x1f415;前言&#xff1a;&#x1f3e8;定义flex容器 display:flex&#x1f3e8;在flex容器中子组件进行排列&#x1fa82;行排列 flex-direction: row&#x1fa82;将行排列进行翻转排列 flex-direction: row-reverse&#x1f3c5;按列排列 flex-direction: col…

No170.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

mstsc改端口为33389

windows 远程默认端口3389不太安全&#xff0c;改成33389防下小人 把下面的2个文本存在后缀.reg的文件&#xff0c;双击导入注册表&#xff0c;"PortNumber"dword:0000826d 这个就是33389对应的端口号的16进制值&#xff0c;要想自己改成其它的换下值即可 Windows …

人工智能、机器学习、深度学习的区别

人工智能涵盖范围最广&#xff0c;它包含了机器学习&#xff1b;而机器学习是人工智能的重要研究内容&#xff0c;它又包含了深度学习。 人工智能&#xff08;AI&#xff09; 人工智能是一门以计算机科学为基础&#xff0c;融合了数学、神经学、心理学、控制学等多个科目的交…

LeetCode讲解篇之77. 组合

文章目录 题目描述题解思路题解代码 题目描述 题解思路 遍历nums&#xff0c;让当前数字添加到结果前缀中&#xff0c;递归调用&#xff0c;直到前缀的长度为k&#xff0c;然后将前缀添加到结果集 题解代码 func combine(n int, k int) [][]int {var nums make([]int, n)fo…