JavaScript的迭代器和生成器

1. 迭代器Iterator

1. 基本概念

JavaScript 表示集合的对象大致有Object,Array,Map,Set四种,并且这四种类型的数据之间可以相互以成员嵌套(如Array的成员可以是Object,而Map又可以嵌入Object的成员中),为了处理所有不同的数据结构,就需要统一的接口机制。

迭代器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

Iterator 的作用有三个

  1. 为各种数据结构,提供统一、简便的访问接口;
  2. 使数据结构的成员能够按某种次序排列;
  3. ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

2. Iterator 的遍历过程

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  2. 第一次调用指针对象的next方法,将指针指向数据结构的第一个成员。第二次调用,指向数据结构的第二个成员..........
  3. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。

如下,模拟Iterator 的遍历过程:

const it = makeIterator(['a', 'b'])
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
    let nextIndex = 0
    return {
        next: function () {
            return nextIndex < array.length ? 
            { value: array[nextIndex++], done: false } 
            : { value: undefined, done: true }
        }
    }
}

注意:Iterator() 实际上不能被显示的构造。

它通常由集合对象内置迭代器方法( Symbol.iterator )返回。如果你需要显示的创建迭代器对象,推荐使用生成器Generator来创建。


3. 默认的Iterator

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性。一种数据结构只要部署了 Iterator 接口(或者说具有Symbol.iterator属性),我们就称这种数据结构实现了可迭代协议,是“可遍历的(iterable)”,(原型链上的对象具有该方法也可)。

Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。

let arr = ['a', 'b', 'c'];
typeof arr[Symbol.iterator]
// "function"
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

4. Iterator接口的应用场合

  1.  for...of循环

  2. 解构赋值,对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法

  3. 扩展运算符,扩展运算符(...)也会调用默认的 Iterator 接口。所以利用扩展运算符我们可以将任何部署了 Iterator 接口的数据结构,转为数组。

  4. 类数组对象,如字符串 ,DOM NodeList 对象、arguments对象,它们也原生具有 Iterator 接口。

  5. 其他场合,由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。

for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
Promise.all()
Promise.race()

注意: 普通的对象是不能直接使用for...of的,因为它没有默认部署 Iterator 接口。

let obj = { a: 1, b: 2 }
for (let key of obj) {
    console.log(key)// TypeError: obj is not iterable
}

你可以使用for...in来遍历键名,或者使用Object.keys方法将对象的键名生成一个数组,然后遍历这个数组。还可以使用 Generator 函数将对象重新包装一下(下面会详细介绍)

tips:for...in是专门为遍历对象而设计的,它会自动遍历原型链。不适合用来遍历数组


2. 生成器Generator

1. 基本语法

生成器 ( Generator )函数,简单来讲就是一种可中断函数,它可以使用yield暂停函数的执行,并在之后再次从暂停的地方继续执行。使用next()来启动函数的执行,从上一次暂停的位置开始。

与一般函数不同的是,它使用  function* 标识符来声明,如

function* tasks() {
    yield 1
    yield 2 + 3
    yield 3 - 1
}
const it = tasks()
it.next() // { value: 1, done: false }
it.next() // { value: 5, done: false }
it.next() // { value: 1, done: false }
it.next() // { value: undefined, done: true }

实际上,当我们执行 Generator 函数时,它会返回一个迭代器对象,而调用这个迭代器的next()方法,它会返回yield右侧代码的执行结果。


2. 理解Generator

生成器 ( Generator ) 函数实际上是ES6提供的一种异步编程解决方案(后面介绍),语法行为与传统函数完全不同。可以理解为 “ Generator 是一个内部暂停,封装了多个状态的状态机”。而yeild 也正如它的英文意思“产出” ,用来产出内部的状态。

next()方法返回的对象中,value属性就是yield表达式的值,done属性表示遍历是否结束。当Generator中的状态都被遍历完后,无论再next多少次,结果都是{ value:undefined,done:true}

注意:

1. 这个“产出” 是惰性的。yeild 右边的表达式 ( 如上面的2+3,3-1)也只有当调用next()方法,内部指针指向该语句时,才会执行。

2. 如果,Generator内部不使用 yeild,那么它就只是一个单纯的暂缓执行函数

function* f() {
    console.log('执行了!')
}
var generator = f()
setTimeout(function () {
    generator.next()
}, 2000)

上面代码中,函数f如果是普通函数,在为变量generator赋值时就会执行。但是,函数f是一个 Generator 函数,就变成只有调用next方法时,函数f才会执行。

3. yield表达式只能用在 Generator 函数里面,用在其他地方都会报错


3. Generator 与 Iterator的关系

上面说过,任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。

1. 由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

var myIterable = {}
myIterable[Symbol.iterator] = function* () {
    yield 1
    yield 2
    yield 3
}

;[...myIterable] // [1, 2, 3]

2. 与迭代器一样,for...of循环可以遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法。 

function* foo() {
  yield 1;
  yield 2;
  return 3;
}
for (let v of foo()) {
  console.log(v);
}
// 1 2 3 

3. 包括解构赋值,Array.from()等,迭代器能够应用的场合,生成器基本都能插上一脚


4. next 的参数

yield 的“产出”默认为其表达式的结果,没有则为undefined。但next方法可以带一个参数,此时,该参数就会被当作上一个yield表达式的返回值。

function* f() {
    for (var i = 0; true; i++) {
        var reset = yield i
        if (reset) {
            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 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。但通过next方法的参数,就能够实现在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,来调整函数行为。


5. Generator 的实例方法

除了上面提到的next方法,Generator函数返回的迭代器对象,都还还有throw和return两个方法。

1. throw,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。

function* tasks() { 
   try {
        yield 1
    } catch (err) {
        console.log('catched by generator *tasks', err)
    }
    return 2
}
const t = tasks()
t.next().value // 1
t.next().value // 2
t.throw('err 1') // catched by generator *tasks err 1

注意:如果该错误没有被 generator 本身 catch 住,则会往外暴露给外层,也就是 generator 的调用方。如果调用方也没有 catch 住,则正常抛错。

function* tasks() {
    yield 1
    return 2
}
const t = tasks()
t.next().value // 1
try {
    t.throw('err 2')
} catch (err) {
    console.log('catched by generator *tasks caller', err)
    //catched by generator *tasks caller err 2
}

2. return,强制 Generator 函数完成,并固定返回值,且其返回 IteratorResult 中的 done 将为 true

function* tasks() {
    yield 1
    yield 2
    return 3
}
const t = tasks()
t.next()
const obj = t.return('终结任务')
console.log(obj) // { value: '终结任务', done: true }

3.  next,throw 和 return,三者本质上是做了同一件事 —— 让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式。


7.  yeild* 表达式

如果在 Generator 函数内部,调用另一个 Generator 函数。需要在前者的函数体内部,自己手动完成遍历。如果有多个 Generator 函数嵌套,写起来就非常麻烦。

function* foo() {
    yield 'a'
    yield 'b'
}

function* bar() {
    yield 'x'
    // 手动遍历 foo()
    for (let i of foo()) {
        console.log(i)
    }
    yield 'y'
}
for (let v of bar()) {
    console.log(v)//x a b y
}

所以,ES6 提供了yield*表达式,作为解决办法,用来在一个 Generator 函数里面执行另一个 Generator 函数。如下:

function* bar() {
    yield 'x'
    yield* foo()
    yield 'y'
}
for (let v of bar()) {
    console.log(v) //x a b y
}

8. Generator的简单应用

1. 作为对象的属性

let obj = {
    *myGeneratorMethod() {
        //   ···
    },
    //或
    myGeneratorMethod: function* () {
        // ···
    }
}

 2. 部署 Iterator 接口,利用 Generator 函数,可以在任意对象上部署 Iterator 接口。

function* iterEntries(obj) {
    let keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
        let key = keys[i]
        yield [key, obj[key]]
    }
}
let myObj = { foo: 3, bar: 7 }
for (let [key, value] of iterEntries(myObj)) {
    console.log(key, value) // foo 3  bar 7
}  

3. 作为数据结构

Generator 可以看作是数据结构,更确切地说,可以看作是一个数组结构,因为 Generator 函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口。

function* doStuff() {
  yield fs.readFile.bind(null, 'hello.txt');
  yield fs.readFile.bind(null, 'world.txt');
  yield fs.readFile.bind(null, 'and-such.txt');
}

 9.  Generator的注意事项

1. Generator不能与new操作符一起使用。会报错

function* task() {}
const t = new task() // TypeError: task is not a constructor

2. Generator返回的总是遍历器对象,而不是this对象

function* genenrator() {}
genenrator.prototype.hello = function () {
    return 'hi!'
}
let iterator = genenrator()
iterator instanceof g // true
iterator.hello() // 'hi!'

上面的代码说明,Generator是和普通构造函数有相似之处的。它的实例能够访问到原型上的属性,但不能访问this实例属性,如下:

function* genenrator() {
    this.hello = 'hi!'
}
let iterator = genenrator()
iterator.hello // undefined

3. Generator的异步应用

JavaScript 异步编程,参考:异步编程(Promise详解)

在ES6之前,异步编程解决方案最常用的应该就是Promise,链式调用使得异步编程的逻辑变得更加清晰,优化了回调地狱的问题,让异步任务的多个执行行看的更加清除了。

但与此同时,也有着代码冗余的问题,原本的任务被 Promise 包装了一下后,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚。

那么,有没有更好的写法呢?


1. 异步操作的同步化表达

Generator 函数有暂停执行的效果,意味着可以把异步操作写在yield表达式里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield表达式下面,反正要等到调用next方法时再执行。所以,Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数。

function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()
// 卸载UI
loader.next()

上面代码中,初次调用loadUI时,该函数不会执行,仅返回一个迭代器。对该迭代器调用next,会显示Loading界面(showLoadingScreen),并异步加载数据(loadUIDataAsynchronously)。等到数据加载完成,再一次使用next方法,则会隐藏Loading界面。

可以看到,这种写法的好处是所有Loading界面的逻辑,都被封装在一个函数,按部就班非常清晰。

Ajax 是典型的异步操作,通过 Generator 函数部署 Ajax 操作,可以用同步的方式表达。

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。


2. 控制流管理

对于多层步骤的异步任务,如果我们采用回调函数,那很大概率会演变成臭名昭著的“回调地狱”。

step1(function (value1) {
    step2(value1, function (value2) {
        step3(value2, function (value3) {
            step4(value3, function (value4) {
             //.....
            });
        });
    });
});

如果采用promise的链式调用,虽然改成了比较清晰的直线形式,但是加入了大量的Promise语法,一眼看过去全是Promise API(then,catch),操作本身的语义反而不容易看出来。

Promise.resolve(step1)
    .then(step2)
    .then(step3)
    .then(step4)
    .then(function (value4) {
       //.....
    }.catch(err)

如果采用Generator,就能更进退一步改善代码陨星流程。

function* longRunningTask(value1) {
  try {
    var value2 = yield step1(value1);
    var value3 = yield step2(value2);
    var value4 = yield step3(value3);
    var value5 = yield step4(value4);
    // ........
  } catch (e) {
 //.....
  }
}

3. Generator异步管理的缺陷

仔细观察1和2中的例子,可以发现,虽然 Generator 函数可以让异步操作写得更清晰、更加同步化,但它在实际使用中仍有一些缺点,尤其是在流程控制方面:

1. 手动执行每个阶段

在使用 Generator 函数时,异步操作的每个阶段(yield 的地方)都需要手动调用 next() 方法来向下执行。这意味着,如果你有多个 yield,你需要按顺序调用多次 next(),在复杂的流程中显得很繁琐,并且需要判断何时执行第一阶段、何时执行第二阶段......

2. 不支持自动处理错误

如果异步操作在 yield 处抛出错误,需要手动处理,否则可能会导致整个 Generator 函数无法正常继续。

3. 回调地狱问题并未完全解决

虽然 Generator 通过 yield 来暂停执行,使异步操作写得更像同步代码,但如果流程复杂,还是可能需要回调函数来执行每个 next() 调用,从而可能导致回调嵌套。

为了解决这些问题,W3C官方,在ES2017标准中引入了async 函数,进一步简化了异步代码的写法,使得流程管理更方便。


4. async

1. 基本概念

本质上来说,async就是 Generator 函数的语法糖

async function asyncTask() {
    try {
        const result1 = await fetchData1();
        const result2 = await fetchData2(result1);
        const result3 = await fetchData3(result2);
        return result3;
    } catch (error) {
        console.error(error);
    }
}

与Generator一比较就会发现,在语法上,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

但相比于Generator函数,它又进行了进一步优化:

1. 更好的语义,async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

来一组示例,直观的感受

实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,依次远程读取一组 URL,然后按照读取的顺序输出结果。

Promise 的写法如下。

unction logInOrder(urls) {
  // 远程读取所有URL
  const textPromises = urls.map(url => {
    return fetch(url).then(response => response.text());
  });

  // 按次序输出
  textPromises.reduce((chain, textPromise) => {
    return chain.then(() => textPromise)
      .then(text => console.log(text));
  }, Promise.resolve());
}

async 函数写法如下

async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}

啥也不说,async牛皮!代码极大的简化了

不过这里有有一个不公平的地方,上面的async写法中,操作都是继发的,需要等上一个操作完成,才会执行下一个操作,效率太差。现在换成并发请求

async function logInOrder(urls) {
  // 并发读取远程URL
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // 按次序输出
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}

哈哈,还是async赢了!!!

2. 自动执行流程,使用 async 和 await 后,JavaScript 引擎会自动控制异步代码的执行顺序,不需要手动调用 next()。

3. 更好地处理错误,async/await 可以使用 try...catch 语句进行错误捕获,处理错误的逻辑更加清晰。

4. 更广的适用性,yield命令后面只能是Thunk 函数(有兴趣的参考:Thunk 函数 - 阮一峰) 或是Promise 对象 ,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

5.  返回值是 Promise,async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。


2. 基本用法

1. async函数返回一个 Promise 对象,可以使用then方法添加回调函数。

2. async函数内部return语句返回的值,会成为then方法回调函数的参数。

但是需要注意:只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。         

function asynchronous() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1000)
        }, 1000)
    })
}
async function testFn() {
    return await asynchronous()
}
testFn().then((res) => {
    console.log(res) // 1000
})

3. async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject           

async function testFn() {
    try {
        throw new Error('error') //被内部catch捕获
    } catch (error) {
        console.log(error.message) //error
    }
    throw new Error('我被外部catch抓住了') //被外部catch 捕获
}
testFn()
    .then((res) => {
        console.log(res) // 1000
    })
    .catch((error) => {
        console.log(error.message) //我被外部catch抓住了
    })

4. 正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。            

async function f() {
  // 等同于  return 123;
  return await 123;
}
f().then(v => console.log(v))// 123

5. 顶层await

早期的语法规定是,await命令只能出现在 async 函数内部,否则都会报错。

const data = await fetch('https://api.example.com');

但从ES2022开始,允许在模块的顶层独立使用await命令,使得上面那行代码不会报错了。它的主要目的是使用await解决模块异步加载的问题。

注意!!!!

浏览器目前无法识别ES2022,如果需要使用,你需要借助webpack,vite等构建工具。

注意!!!!

顶层await只能用在 ES 模块,不能用在 CommonJS 模块。这是因为 CommonJS 模块的require()是同步加载,如果有顶层await,就没法处理加载了。


若有错误或描述不当的地方,烦请评论或私信指正,万分感谢 😃

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习常用开源数据集介绍【持续更新】

DIV2K 介绍&#xff1a;DIV2K是一个专为 图像超分辨率&#xff08;SR&#xff09; 任务设计的高质量数据集&#xff0c;广泛应用于计算机视觉领域的研究和开发。它包含800张高分辨率&#xff08;HR&#xff09;训练图像和100张高分辨率验证图像&#xff0c;每张图像都具有极高…

Pinia-状态管理

Pinia-状态管理 特点&#xff1a; 1. 轻量和模块化 Pinia 是一个轻量级的状态管理库&#xff0c;支持模块化管理&#xff0c;即可以将应用的状态分成多个 store 以实现更好的组织。使用 Pinia&#xff0c;可以定义多个 store&#xff0c;每个 store 都是一个独立的模块&#x…

向量模型Jina Embedding: 从v1到v3论文笔记

文章目录 Jina Embedding: 从v1到v3Jina Embedding v1数据集准备训练过程 Jina Embedding v2预训练修改版BERT在文本对上微调在Hard Negatives上微调 Jina Embedding v2 双语言预训练修改版BERT在文本对上微调用多任务目标微调 Jina Embedding v3预训练在文本对上微调训练任务相…

修改HarmonyOS鸿蒙图标和名字,打包后安装到真机,应用图标丢失变成透明,修改名字也不生效,还是默认的labeL解决方案教程

HarmonyOS鸿蒙打包hap 安装应用到桌面没有图标&#xff0c;用hdc安装到真机&#xff0c;打包后应用图标丢失变成透明&#xff0c;名字也还是默认的label的bug&#xff0c;以下是解决方案 以下是修改方案&#xff1a; 1、修改应用名字&#xff1a; 2、修改应用图标&#xff1a…

3个模型的交互式多模型IMM,基于EKF的目标跟踪实例(附MATLAB代码)

文章目录 3个模型的IMM源代码运行结果代码介绍总结 3个模型的IMM 代码实现了基于 I M M IMM IMM&#xff08;Interacting Multiple Model&#xff09;算法的目标跟踪。它使用三种不同的运动模型&#xff08;匀速直线运动、左转弯和右转弯&#xff09;来预测目标的位置&#x…

Webservice 客户端 生成代码 cxf方式 jdk方式 wsdl保存到本地后,生成客户端代码

详解视频&#xff0c;如果看不懂图片&#xff0c;请看这个视频 客户端三种方式 jdk cxf 客户单 wsdl保存到本地后&#xff0c;生成客户端代码

轮廓图【HTML+CSS+JavaScript】

给大家分享一个很好看的轮播图&#xff0c;这个也是之前看到别人写的效果感觉很好看&#xff0c;所以后面也自己实现了一下&#xff0c;在这里分享给大家&#xff0c;希望大家也可以有所收获 轮播图效果&#xff1a; 视频效果有点浑浊&#xff0c;大家凑合着看&#xff0c;大家…

Windows上安装Redis

1.下载Redis 下载有2中选择&#xff1a; 官方redis官方下载地址&#xff1a; https://redis.io/download&#xff0c; 选择适合Windows的版本下载。 redis 64位下载地址&#xff1a; https://github.com/ServiceStack/rediswindows/tree/master/downloads&#xff0c; 我们下…

计算机视觉实验一:图像基础处理

1. 图像的直方图均衡 1.1 实验目的与要求 (1)理解直方图均衡的原理与作用; (2)掌握统计图像直方图的方法; (3)掌握图像直方图均衡的方法。 1.2 实验原理及知识点 直方图均衡化是通过灰度变换将一幅图象转换为另一幅均衡直方图&#xff0c;即在每个灰度级上都具有相同的象素…

第8章 利用CSS制作导航菜单作业

1.利用CSS技术&#xff0c;结合链接和列表&#xff0c;设计并实现“山水之间”页面。 浏览效果如下&#xff1a; HTML代码如下&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>山水之间</title><…

dhcp池没有空闲ip导致手机无法获得ip

得到用户反馈&#xff0c;一个高速项目部的wifi无法接入&#xff0c;让排查原因。 反馈有的手机能接入&#xff0c;有的接入不了。查看ac界面发现有个终端获得的ip是169.254.xxx.xxx。 ip地址是169.254.96.17显然是手机打开wlan开关后&#xff0c;鉴权通过后dhcp过程&#xff0…

AJAX和JSON

一.AJAX技术 1.1 AJAX介绍 Ajax 即“Asynchronous Javascript And XML”&#xff08;异步 JavaScript 和 XML&#xff09;&#xff0c;是指一种创建 交互式、快速动态应用的网页开发技术&#xff0c;无需重新加载整个网 页的情况下&#xff0c;能够更新页面局部数据的技术。 通…

c++包装器/适配器 function--通俗易懂

1.为什么要有function 在下面的场景下 useF类模版要实例化出三份&#xff0c;影响效率 仿函数:c仿函数--通俗易懂-CSDN博客 lambda表达式&#xff1a;c lambda表达式--通俗易懂-CSDN博客 template<class F, class T> T useF(F f, T x) {static int count 0;cout <…

项目模块十五:HttpResponse模块

一、模块设计思路 存储HTTP应答要素&#xff0c;提供简单接口 二、成员变量 int _status; // 应答状态码 unordered_map<string, string> _headers; // 报头字段 string _body; // 应答正文 bool _redirect_flag; // 是否重定向信息 stri…

【sqlmap使用】

sqlmap简介 sqlmap 目录结构 sqlmap常用参数 sqlmap实现注入 测试注入点&#xff0c;检测到注入点后&#xff0c;直接爆数据库名 python sqlmap.py –u http://172.16.12.2/7/9/strsql.php --data "usernameadmin" --dbs注意sqlmap在使用过程中可能会出现几个需要…

Java已死,大模型才是未来?

作者&#xff1a;不惑_ 引言 在数字技术的浪潮中&#xff0c;编程语言始终扮演着至关重要的角色。Java&#xff0c;自1995年诞生以来&#xff0c;便以其跨平台的特性和丰富的生态系统&#xff0c;成为了全球范围内开发者们最为青睐的编程语言之一 然而&#xff0c;随着技术的…

Rust 力扣 - 59. 螺旋矩阵 II

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 使用一个全局变量current记录当前遍历到的元素的值 我们只需要一圈一圈的从外向内遍历矩阵&#xff0c;每一圈遍历顺序为上边、右边、下边、左边&#xff0c;每遍历完一个元素后current 我们需要注意的是如果上…

MFC工控项目实例二十八模拟量信号每秒采集100次

用两个多媒体定时器&#xff0c;一个定时0.1秒计时&#xff0c;另一个定时0.01秒用来对模拟量信号采集每秒100次。 1、在SEAL_PRESSUREDlg.h中添加代码 class CSEAL_PRESSUREDlg : public CDialo { public:CSEAL_PRESSUREDlg(CWnd* pParent NULL); // standard constructor&a…

用插值公式实现滚动进度条动画效果

我们在日常前端开发时在动画的选择上基本都是css&#xff0c;通过css的animation即可满足大部分的开发场景&#xff0c;如果遇到了特殊而比较不容易实现的效果就会考虑到用js来实现&#xff0c;而本次的主题&#xff0c;就是围绕用js来做一个比较不常见的特殊动画效果。 假设我…

【1个月速成Java】基于Android平台开发个人记账app学习日记——第4天,注册登录逻辑代码

24.11.03 1.输入手机号跳转功能 第一个要设计的功能是&#xff0c;输入手机号以后跳转到另一个页面&#xff0c;输入获取得到的验证码页面。先拿这个功能练练手。 首先看一下此时的完整项目结构&#xff1a; 主要是添加了2个活动类和对应的界面&#xff0c;下面看详细的代码…