ES6:Generator函数详解
ES6:Generator函数详解
- 1、 概念
- 2、yield表达式
- 2.1 yield 语句与 return 语句区别
- 2.2 Generator函数不加yield语句,这时变成了一个单纯的暂缓执行函数
- 2.3 yield 表达式只能用在 Generator 函数里面,用在其它地方都会报错
- 2.4 yield 表达式如果用在另一个表达式中,必须放在圆括号里面
- 2.5 yield 表达式用作参数或放在赋值表达式的右边,可以不加括号
- 3、与 Iterator 接口的关系
- 4、next() 方法的参数
- 5、for...of循环
- 6、Generator.prototype.throw()
- 7、Generator.prototype.return()
- 8、yield*表达式
- 9、Generator函数应用举例
- 9.1 利用 Generator 函数和 for...of 循环实现斐波那契数列
- 9.2 异步操作的同步化表达
- 9.3 逐行读取文本文件
- 9.4 控制流管理
- 9.5 Genarator 对任意对象部署Iterator接口
- 9.6 Genarator 对数组部署Iterator接口
- 10、promise、generator和aysnc/await区别
- 10.1 promise
- 10.2 Generator
- 10.3 async/await
1、 概念
Generator
函数式ES6
提供的一种异步编程解决方案,语法行为与传统函数完全不同。对于Generator
函数有多种理解角度。
从语法上:可以把它理解成一个状态机,封装了多个内部状态;
从形式上:Generator
函数是一个普通函数,但是有两个特征:一是function
命令与函数名之间有一个星号*
;二是函数体内部使用yield
语句定义不同的内部状态。
// 传统函数
function fn() {return 'hello world'
}
fn() // 'hello world',一旦调用立即执行// Generator函数
function* helloWorldGenerator () {yield 'hello'yield 'world'return 'ending'
}const hw = helloWorldGenerator() // 调用 Generator函数,函数并没有执行,返回的是一个Iterator对象
console.log(hw.next()) // {value: 'hello', done: false},value 表示返回值,done 表示遍历还没有结束
console.log(hw.next()) // {value: 'world', done: false},value 表示返回值,done 表示遍历还没有结束
console.log(hw.next()) // {value: 'ending', done: true},value 表示返回值,done 表示遍历还没有结束
console.log(hw.next()) // {value: undefined, done: true},value 表示返回值,done 表示遍历结束
上面的代码中可以看到传统函数和Generator
函数的运行是完全不同的,传统函数调用后立即执行并输出了返回值;Generator
函数则没有执行而是返回一个Iterator
对象,并通过调用Iterator
对象的next
方法来遍历,每次调用Iterator
对象的next
方法时,内部的指针就会从函数的头部或上一次停下来的地方开始执行,直到遇到下一个 yield
表达式或return
语句暂停。换句话说,Generator
函数是分段执行的,yield
表达式是暂停执行的标记,而 next
方法可以恢复执行。
执行过程如下:
- 第1次调用,
Generator
函数开始执行,直到遇到第一条yield
语句为止。next
方法返回一个对象,它的value
属性就是当前yield
语句的值hello
,done
属性的值false
表示遍历还没有结束。 - 第2次调用,
Generator
函数从上次yield
语句停下的地方,一直执行到下一条yield
语句。next
方法返回的对象的value
属性就是当前yield
语句的值world
,done
属性的值false
表示遍历还没有结束。 - 第 3 次调用,
Generator
函数从上次yield
语句停下的地方,一直执行到return
语句(如果没有return
语句,就执行到函数结束)。next
方法返回的对象的value
属性就是紧跟在return
语句后面的表达式的值(如果没有return
语句,则value
属性的值为undefined
),done
属性的值true
表示遍历已经结束。 - 第 4 次调用,此时
Generator
函数已经运行完毕,next
方法返回的对象的value
属性为undefined
,done
属性为true
。以后再调用next
方法,返回的都是这个值。
2、yield表达式
由于 Generator
函数返回的遍历器对象只有调用 next
方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield
语句就是暂停标志。
遍历器对象的 next
方法的运行逻辑如下。
1.遇到 yield
语句就暂停执行后面的操作,并将紧跟在 yield
后的表达式的值作为返回的对象的 value
属性值。
2.下一次调用 next
方法时再继续往下执行,直到遇到下一条 yield
语句。
3.如果没有再遇到新的 yield
语句,就一直运行到函数结束,直到 return
语句为止,并将 return
语句后面的表达式的值作为返回对象的value
属性值。
4.如果该函数没有 return
语句,则返回对象的 value
属性值为undefined
。
注意:只有调用next方法且内部指针指向该语句时才会执行yield语句后面的表达式,因此,等于为JavaScript提供了手动的“惰性求值”的语法功能。
function* gen() {yield 123 + 456;
}
上面的代码中,yield 后面的表达式 123 + 456 不会立即求值,只会在 next 方法将指针移到这一句时才求值。
2.1 yield 语句与 return 语句区别
yield 语句与 return 语句既有相似之处,又有区别。相似之处在于都能返回紧跟在语句后的表达式的值。区别在于每次遇到 vield 函数暂停执行,下一次会从该位置继续向后执行而 return 语句不具备位置记忆的功能。一个函数里面只能执行一次(或者说一条) return语句,但是可以执行多次(或者说多条) yield 语句。正常函数只能返回一个值,因为只能执行一次 return 语句;Generator 函数可以返回一系列的值,因为可以有任意多条 yield 语句。从另一个角度看,也可以说 Generator 生成了一系列的值,这也就是其名称的来历(在英语中generator”这个词是“生成器”的意思)。
2.2 Generator函数不加yield语句,这时变成了一个单纯的暂缓执行函数
function* f () {console.log('执行了')
}
var generator = f()
setTimeout(function () {generator.next()
}, 2000)
上面的代码中,函数 f 如果是普通函数,在为变量 generator 赋值时就会执行。但是函数 f是一个 Generator 函数,于是就变成只有调用 next 方法时才会执行。
2.3 yield 表达式只能用在 Generator 函数里面,用在其它地方都会报错
{(function (){yield 1;})()// SyntaxError: Unexpected number// 在一个普通函数中使用yield表达式,结果产生一个句法错误
}
2.4 yield 表达式如果用在另一个表达式中,必须放在圆括号里面
{function* demo() {console.log('Hello' + yield); // SyntaxErrorconsole.log('Hello' + yield 123); // SyntaxErrorconsole.log('Hello' + (yield)); // OKconsole.log('Hello' + (yield 123)); // OK}
}
2.5 yield 表达式用作参数或放在赋值表达式的右边,可以不加括号
{function* demo() {foo(yield 'a', yield 'b'); // OKlet input = yield; // OK}
}
3、与 Iterator 接口的关系
任意一个对象的 Symbol.iterator
方法等于该对象的遍历器对象生成函数,调用该函数会返回该对象的一个遍历器对象。
由于 Generator
函数就是遍历器生成函数,因此可以把 Generator
赋值给对象的Symbol.iterator 属性,从而使得该对象具有 Iterator
接口。
var myIterable = {}
myIterable[Symbol.iterator] = function* () {yield 1yield 2yield 3
}for(let value of myIterable) {console.log(value)}// 1// 2// 3
[...myIterable] // [1, 2, 3]
上面的代码中,Generator
函数赋值给 Symbol.iterator
属性,从而使得 myIterable
对象具有了 Iterator
接口,可以被...
运算符遍历。
4、next() 方法的参数
yield
语句本身没有返回值,或者说总是返回 undefined
。next
方法可以带有一个参数,该参数会被当作上一条 yield
语句的返回值。
function* f () {for (var i = 0; true; i++) {var reset = yield iif (reset) {console.log('执行了')i = -1}}
}
var g = f()
g.next() // {value: 0, done: false}
g.next() // {value: 1, done: false}
g.next(true) // {value: 0, done: false}
上面的代码先定义了一个可以无限运行的 Generator
函数 f
,如果 next
方法没有参数,每次运行到 yield
语句时,变量 reset
的值总是 undefined
。当next
方法带有一个参数 true
时,当前的变量 reset
就被重置为这个参数(即 true
),因而 i
会等于-1
,下一轮循环就从-1
开始递增。
这个功能有很重要的语法意义。Generator
函数从暂停状态到恢复运行,其上下文状态(context
)是不变的。通过 next
方法的参数就有办法在Generator
函数开始运行后继续向函数本内部注入值。也就是说,可以在 Generator
函数运行的不同阶段从外部向内部注入不同的值,从而调整函数行为。
再看一个例子。
function* foo (x) {var y = 2 * (yield (x + 1))var z = yield (y / 3)return (x + y + z)
}var a = foo(5)
a.next() // 首次调用next,函数只会执行到 “yield(5+1)” 暂停,并返回 {value: 6, done: false}
a.next() // 第二次调用next,没有传递参数,所以 y的值是undefined,那么 y/3 当然是一个NaN,所以应该返回 {value: NaN, done: false}
a.next() // 同样的道理,z也是undefined,6 + undefined + undefined = NaN,返回 {value: NaN, done: true}var b = foo(5)
b.next() // 正常的运算应该是先执行圆括号内的计算,再去乘以2,由于圆括号内被 yield 返回 5 + 1 的结果并暂停,所以返回{value: 6, done: false}
b.next(12) // 上次是在圆括号内部暂停的,所以第二次调用 next方法应该从圆括号里面开始,就变成了 let y = 2 * (12),y被赋值为24,所以第二次返回的应该是 24/3的结果 {value: 8, done: false}
b.next(13) // 参数2被赋值给了 z,最终 x + y + z = 5 + 24+ 13 = 42,返回 {value: 42, done: true}
5、for…of循环
for...of
循环可以自动遍历 Generator
函数生成的 Iterator
对象,且此时不再需要调用next
方法。
function* foo () {yield 1yield 2yield 3yield 4yield 5return 6
}
for (let v of foo()) {console.log(v)
}
// 1 2 3 4 5
上面的代码使用 for...of
循环依次显示 5条 yield
语句的值。
注意:一旦 next 方法的返回对象的 done 属性为 true,for…of 循环就会终止,且不包含该返回对象,所以上面的 return 语句返回的 6 不包括在 for…of 循环中。
6、Generator.prototype.throw()
Generator
函数返回的遍历器对象都有一个 throw
方法,可以在函数体外抛出错误,然后在 Generator
函数体内捕获。
var g = function* () {try (yield;} catch (e) {console.log('内部捕获’,e);}
};
var i=g();
i.next();try {i.throw('a');i.throw('b');
} catch (e) {console.log('外部捕获’,e);
}
//内部捕获a
// 外部捕获b
上面的代码中,遍历器对象 i
连续抛出两个错误。第一个错误被 Generator
函数体内的catch
语句捕获。i
第二次抛出错误,由于 Generator
函数内部的 catch
语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了 Generator
函数体,被函数体外的 catch
语句捕获。
7、Generator.prototype.return()
Generator
函数返回的遍历器对象还有一个 return
方法,可以返回给定的值,并终结Generator
函数的遍历。
function* gen() {yield 1;yield 2;yield 3;
}
var g = gen();
g.next () // { value: 1, done: false }
g.return('foo') // { value:"foo",done: true }
g.next() // { value: undefined, done: true }
上面的代码中,遍历器对象 g
调用 return
方法后,返回值的 value
属性就是 return
方法的参数 foo
。同时,Generator
函数的遍历终止,返回值的 done
属性为 true
,以后再调用 next
方法,done
属性总是返回 true
。
如果 return
方法调用时不提供参数,则返回值的 vaule
属性为 undefined
。
function* gen() {yield 1;yield 2;yield 3;
}
var g = gen();
g.next() // { value: 1,done: false }
g.return() // { value: undefined,done: true }
8、yield*表达式
如果在 Generator
函数内部调用另一个 Generator
函数,默认情况下是没有效果的。
function* foo() {yield 'a';yield'b';
}
function* bar() {yield'x';foo();yield 'y';
}
for (let v of bar()){console.log(v);
}
//"x"
//"y"
上面的代码中,foo
和 bar
都是 Generator
函数,在 bar
里面调用 foo
是不会有效果的。
这时就需要用到 yield*
语句,用来在一个 Generator
函数里面执行另一个 Generator
函数。
function* bar() {yield 'x';yield* foo();yield'y';
}
// 等同于
function* bar() [yield 'x';yield 'a';yield 'b';yield 'y';
}
// 等同于
function* bar() {yield 'x';for (let v of foo()) {yield v;}yield 'y';
}
for (let v of bar()) {console.log(v);
}
// 'x';
// 'a';
// 'b';
// 'y';
9、Generator函数应用举例
9.1 利用 Generator 函数和 for…of 循环实现斐波那契数列
function* fibonacci() {let [prev,curr] = [0,1];for (;;) {[prev, curr] = [curr, prev + currl;yield curr;}
}
for (let n of fibonacci()) {if (n > 1000) break;console.log(n);
}
// 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
9.2 异步操作的同步化表达
Generator
函数的暂停执行的效果,意味着可以把异步操作写在yield
表达式里面,等到调用next
方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield
表达式下面,反正要等到调用next
方法时再执行。所以,Generator
函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
function* main() {var result = yield request('http://some.url');var resp = JSON.parse(result);console.log(resp.value);
}
function request(url) {makeAjaxCall(url, function(response){it.next(response);});
}
var it = main();
it.next();
上面代码的main
函数,就是通过 Ajax
操作获取数据。可以看到,除了多了一个yield
,它几乎与同步操作的写法完全一样。
注意,makeAjaxCall
函数中的next
方法,必须加上response
参数,因为yield
表达式,本身是没有值的,总是等于undefined
。
9.3 逐行读取文本文件
function* numbers() {let file = new FileReader("numbers.txt");try {while(!file.eof) {yield parseInt(file.readLine(), 10);}} finally {file.close();}
}
上面的代码打开文本文件,使用 yield
语句可以手动逐行读取文件。
9.4 控制流管理
如一个多步操作非常耗时,采用回调的话:
step1(function (value1) {step2(value1, function(value2) {step3(value2, function(value3) {step4(value3, function(value4) {// Do something with value4});});});
});
采用Promise改写上面的代码如下:
Promise.resolve(step1).then(step2).then(step3).then(step4).then(function (value4) {// Do something with value4}, function (error) {// Handle any error from step1 through step4}).done();
上面的代码已经把回调函数改成了直线执行的形式,但是加入了大量 Promise
的语法。Generator
函数可以进一步改善代码运行流程。
function* longRunningTask(valuel) {try {var value2 = yield stepl(valuel);var value3 = yield step2(value2);var value4 = yield step3(value3);var value5 = yield step4(value4);// Do something with value4} catch (e) {// Handle any error from stepl through step4}
}
9.5 Genarator 对任意对象部署Iterator接口
function* deployObjectInterface(obj){let keys = Object.keys(obj);for(let i=0; i<keys.length; i++){let key = keys[i];yield [key, obj[key]];}
}
let obj = {name:"jow", age:21 };
for(let[key, value] of deployObjectInterface(obj)){console.log(key, value);
}
// name jow
// age 21
9.6 Genarator 对数组部署Iterator接口
function* deployArrayInterface(arr){var nextIndex = 0;while(nextIndex < arr.length){yield arr[nextIndex++];}
}
var arr = deployArrayInterface(['name', 'age']);console.log(arr.next()); // {value: "name", done: false}
console.log(arr.next().value); // name
console.log(arr.next().done); // falseconsole.log(arr.next().value); // age
console.log(arr.next().done); // trueconsole.log(arr.next().value); // undefined
console.log(arr.next().done); // true
10、promise、generator和aysnc/await区别
三者都是异步编程的解决方案,不同的是,promise
为较早出来的,其次generator
,最后为async/await
,三者象征了前端进行解决异步编程的进化路程。
10.1 promise
promise比较简单,也是最常用的,主要就是将原来用回调函数异步编程的方法转成relsove和reject触发事件;对象内含有四个方法,then()异步请求成功后catch()异步请求错误的回调方法finally()请求之后无论是什么状态都会执行resolve()将现有对象转换为Promise对象all()此方法用于将多个Promise实例包装成一个新的promise实例。race()也是将多个Promise实例包装成一个新的promise实例reject()返回一个状态为Rejected的新Promise实例。优点:让回调函数变成了规范的链式写法,程序流程可以看的很清楚缺点:编写的难度比传统写法高,阅读代码也不是一眼可以看懂
10.2 Generator
generator是一个迭代生成器,其返回值为迭代器(lterator),是ES6标准引入的新的数据类型,主要用于异步编程,它借鉴于Python中的generator概念和语法;generator函数内有两个重要方法,1 yield表达式 2.next()Generator 函数是分段执行的,yield表达式是暂停执行的标记,而 next方法可以恢复执行优点:1.利用循环,每调用一次,就使用一次,不占内存空间 2.打破了普通函数执行的完整性
缺点: 需要用next()方法手动调用,直接调用返回无效iterator 2.
10.3 async/await
async:异步函数
await:同步操作es7中提出来的异步解决方法,是目前解决异步编程终极解决方案,以promise为基础,其实也就是generator的高级语法糖,本身自己就相当于一个迭代生成器(状态机),它并不需要手动通过next()来调用自己,与普通函数一样async就相当于generator函数中的*,await相当于yield,async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。
function getSomething() {return "something";
}async function testAsync() {return Promise.resolve("hello async");
}async function test() {//await是在等待一个async函数完成const v1 = await getSomething();//await后面不仅可以接Promise,还可以接普通函数或者直接量const v2 = await testAsync();console.log(v1, v2);
}
相关文章:

ES6:Generator函数详解
ES6:Generator函数详解 1、 概念2、yield表达式2.1 yield 语句与 return 语句区别2.2 Generator函数不加yield语句,这时变成了一个单纯的暂缓执行函数2.3 yield 表达式只能用在 Generator 函数里面,用在其它地方都会报错2.4 yield 表达式如果…...

前端小练-产品宣传页面
文章目录 前言页面结构固定钉头部轮播JS特效 完整代码总结 前言 经过一个月的爆肝,花费了一个月(期间还花费了将近半个月的时间打比赛,还要备研)算是把数二高数的内容强化了一遍,接下来刷熟练度即可,可惜的…...

arm学习之stm32设备树学习-中断控制led灯亮灭+字符设备指令控制led灯亮灭
中断控制led灯亮灭 驱动文件源码 led-key.c #include<linux/init.h> #include<linux/module.h> #include<linux/of.h> #include<linux/of_gpio.h> #include<linux/gpio.h> #include<linux/of_irq.h> #include<linux/interrupt.h> s…...

快速开发框架若依的基础使用详解
Hi I’m Shendi 快速开发框架若依的基础使用详解 最近在为公司制作新的项目,经过了一段时间的技术沉淀,我开始尝试接触市面上用的比较多的快速开发框架,听的最多的当属若依吧 于是就选用了若依 介绍 为什么选?目的是为了提高开发…...

RabbitMQ 教程 | 第4章 RabbitMQ 进阶
👨🏻💻 热爱摄影的程序员 👨🏻🎨 喜欢编码的设计师 🧕🏻 擅长设计的剪辑师 🧑🏻🏫 一位高冷无情的编码爱好者 大家好,我是 DevO…...

小程序如何从分类中移除商品
有时候商家可能需要在商品分类中删除某些商品,无论是因为商品已下架、库存不足还是其他原因。在这篇文章中,我们将介绍如何从分类中移除商品。 方式一:分类管理中删除商品。 进入小程序管理后台,找到分类管理,在分…...

P1219 [USACO1.5] 八皇后 Checker Challenge
题目 思路 非常经典的dfs题,需要一点点的剪枝 剪枝①:行、列,对角线的标记 剪枝②:记录每个皇后位置 代码 #include<bits/stdc.h> using namespace std; const int maxn105; int a[maxn];int n,ans; bool vis1[maxn],vis…...

如何在不使用脚本和插件的情况下手动删除 3Ds Max 中的病毒?
如何加快3D项目的渲染速度? 3D项目渲染慢、渲染卡顿、渲染崩溃,本地硬件配置不够,想要加速渲染,在不增加额外的硬件成本投入的情况下,最好的解决方式是使用渲云云渲染,在云端批量渲染,批量出结…...

SpringCloud Gateway 在微服务架构下的最佳实践
作者:徐靖峰(岛风) 前言 本文整理自云原生技术实践营广州站 Meetup 的分享,其中的经验来自于我们团队开发的阿里云 CSB 2.0 这款产品,其基于开源 SpringCloud Gateway 开发,在完全兼容开源用法的前提下&a…...

Android studio修改app图标
步骤如下: 1.右键app名称→New→ImageAsset 2. 进行下面的配置 图源:https://blog.csdn.net/Qingshan_z/article/details/126661650 3.配置分辨率 4.图标自动保存在mipmap文件夹下 再启动就更换成功了!!! 参考&…...

<C++> 三、内存管理
1.C/C内存分布 我们先来看下面的一段代码和相关问题 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] {1, 2, 3, 4};char char2[] "abcd";const char *pChar3 "abcd";int *ptr1…...

大模型开发(十五):从0到1构建一个高度自动化的AI项目开发流程(上)
全文共5600余字,预计阅读时间约13~20分钟 | 满满干货(附全部代码),建议收藏! 本文目标:提出一种利用大语言模型(LLMs)加快项目的开发效率的解决思路,本文作为第一部分,主要集中在如何完整的执行引导Chat模…...

HarmonyOS 开发基础(二)组件拼凑简单登录页面
一、简单登录页面 Entry Component /* 组件可以基于struct实现,组件不能有继承关系,struct可以比class更加快速的创建和销毁。*/ struct Index {State message: string Hello Worldbuild() {// https://developer.harmonyos.com/cn/docs/documentation/…...

flutter minio
背景 前端 经常需要上传文件 图片 视频等等 到后端服务器, 如果到自己服务器 一般会有安全隐患。也不方便管理这些文件。如果要想使用一些骚操作 比如 按照前端请求生成不同分辨率的图片,那就有点不太方便了。 这里介绍以下 minio,࿰…...

ChatGPT:人工智能交互的新时代
ChatGPT的背景和发展: ChatGPT是OpenAI公司在GPT-3基础上的进一步升级。GPT(Generative Pre-trained Transformer)是一种基于Transformer架构的深度学习模型,它能够处理自然语言,实现自动对话、写作等任务。而ChatGPT在…...

C. Binary String Copying - 思维
分析: 赛时我是直接模拟的,tle然后mle,补提,发现规律,每一个改变的字符串都只会对应一个需要改变的区间,例如第一个样例前两个101100 -> 011100和101100 -> 011100,对应区间在确定改变的范…...

哈工大计算机网络课程网络安全基本原理详解之:密钥分发中心与公钥认证中心
哈工大计算机网络课程网络安全基本原理详解之:密钥分发中心与公钥认证中心 在介绍密钥分发中心的概念前,先来回顾一下之前介绍的身份认证协议AP4.0:利用随机数R来避免“回放攻击”,并借助于对称加密算法来保证R的加密传输和解密&…...

md5sum
概念作用及原理 md5sum是一种常用的哈希算法,用于计算数据的MD5哈希值。MD5(Message Digest Algorithm 5)是一种广泛使用的加密散列函数,用于将任意长度的数据映射为固定长度的哈希值(通常是128位)。这个哈…...

图文档数字化:实现高效管理的几大步骤
在当今数字化时代,企业越来越意识到数字化管理对于图文档的重要性。传统的纸质文件管理往往效率低下,容易出现丢失和混乱的情况。为了提高工作效率、降低成本并确保数据安全,许多企业选择采用PDM(产品数据管理)系统来实…...

服务器磁盘占用过高分析
使用命令 du -sh查看出占用较大的目录,如下: 目录大小处理办法/usr/local/mysql/data16G转移部分文件/usr/local/nacos2.1G维持现状/logs4.4G删除部分文件/var1.9G删除部分文件 du -sh命令使用示例: du -sh /* 查看根目录各文件(夹)占用大小…...

【C语言】通讯录3.0 (文件存储版)
前言 通讯录是一种记录联系人信息的工具,包括姓名、电话号码、电子邮件地址、住址等。 文章的一二三章均于上一篇相同,可以直接看第四章改造内容。 此通讯录是基于通讯录2.0(动态增长版)的基础上进行增加文件操作功能,…...

【C#常用操作】
excel相关操作 using Excel Microsoft.Office.Interop.Excel;public Excel.Application app; public Excel.Workbooks wbs; public Excel.Workbook wb; public Excel.Worksheets wss; public Excel.Worksheet ws;/// <summary> /// 取得打开excel句柄 /// </summary…...

深入理解CountDownLatch计数器
入理解CountDownLatch计数器 其他知识点 Java 多线程基础 深入理解aqs ReentrantLock用法详解 深入理解信号量Semaphore 深入理解并发三大特性 并发编程之深入理解CAS 深入理解CountDownLatch Java 线程池 使用用法 CountDownLatch用法详解 CountDownLatch实现原理 下面例子来…...

从SQL注入绕过最新安全狗WAF中学习fuzz
前言 SQL注入并不是很精通,通过实战绕过WAF来进行加强SQL注入能力,希望对正在学习的师傅能有一丝帮助。 安装 安装前言 我是本地搭建的环境进行测试的 环境是windows11phpstudy2018sqli-labs phpstudy的安装我不再复述,这里简单说一下安全…...

C语言每日一题:12《数据结构》相交链表。
题目: 题目链接 思路一: 1.如果最后一个节点相同说明一定有交点。 2.使用两个循环获取一下长度,同时可以获取到尾节点。 3。注意初始化lenA和lenB为1,判断下一个节点是空是可以保留尾节点的。长度会少一个,尾节点没有…...

【Spring框架】SpringMVC
目录 什么是Spring MVC实现客户端和程序之间的“连接”1.1 RequestMapping1.2GetMapping1.3PostMapping 获取参数2.1.1 获取单个参数2.1.2 获取多个参数2.1.3 获取对象2.2 后端参数重命名2.3 RequestBody 接收JSON对象2.4 获取URL中参数PathVariable2.5 上传⽂件RequestPart2.6…...

HDFS中namenode安全模式
HDFS中namenode安全模式 安全模式的现象探究step1step2step3step4 安全模式的概述控制进入时间和离开条件安全模式自动进入离开安全模式手动进入离开 安全模式的现象探究 step1 HDFS集群在停机状态下,使用hdfs -daemon命令逐个进程启动集群,观察现象首…...

blender凹凸感和置换形变
一、怎么做出凹凸感 需要三个部分的内容: 1、一个基础的纹理:告诉计算机需要用一个什么样的纹理做凹凸,纹理一般采用黑白,在计算机里面,从 0 - 1之间的值可以用从黑到白之间不同的灰度来表示因此,有一张黑白…...

力扣 343. 整数拆分
题目来源:https://leetcode.cn/problems/integer-break/description/ C题解1:动态规划。dp[i] 代表数字i拆分后得到的最大乘积。递归公式为拆分后两个数的最大乘积相乘,即 dp[i] max(dp[i], dp[j] * dp[i-j])。对于n2或3需要另外讨论。 cla…...

【JavaWeb】正则表达式
🎄欢迎来到边境矢梦的csdn博文,本文主要讲解Java 中正则表达式 的相关知识🎄 🌈我是边境矢梦,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下🫰🫰&am…...