我的 Python 函数返回的数据,第一次遍历好好的、第二次却空空如也,我对着生成器只能消费一次这个坑排查了大半天的复盘

写了个数据处理脚本,一个函数返回一批记录,调用方先遍历一遍统计总数打进度、再遍历一遍写数据库,逻辑顺得不能再顺。结果诡异:日志清清楚楚"共 5000 条",数据库里却一条都没写进去。盯着代码反复看,两次遍历代码一模一样,凭什么第一次数出 5000、第二次一条没有?甚至怀疑数据库连接,查半天没问题。排查大半天才撞上 Python 对新手极隐蔽的坑:生成器只能被消费一次。根因是函数里用了 yield,它就成了生成器函数,返回的不是 list 而是生成器对象;生成器惰性+一次性,只存"执行到哪了"的游标、不存数据,第一次 sum 统计就把它从头消费到尾耗尽了,第二次 for 时游标已在末尾、一条不产出,于是"数 5000 写 0"。这篇从生成器的本质(惰性+一次性、为何只能一次)、要多次遍历就 list 物化/大数据单次才保留生成器/一次遍历里顺便统计的正解、可迭代对象 vs 迭代器的辨析(iter(list)给新迭代器、iter(gen)返回自己)、一次性迭代器速查(map/filter/zip/文件/reversed)、生成器适用场景与权衡、决策图与铁律,到附上一组亲手验证生成器耗尽、list 可反复、itertools.tee 复制的实验代码。核心领悟:别凭"看起来像"就当成那个东西,要理解它本质上是什么、在哪些维度其实不同;鸭子类型的危险正在于忽略表面相似本质不同的关键差异;亲手跑实验把抽象规则变成可复现现象,才能真正记住。

我的 Python 函数返回的数据,第一次遍历好好的、第二次却空空如也,我对着生成器只能消费一次这个坑排查了大半天的复盘

那是我写的一个数据处理脚本。一个函数返回一批处理过的记录,调用方先遍历一遍统计总数、打印个进度,然后再遍历一遍把每条写进数据库。逻辑顺得不能再顺,我连测都没细测就跑了。结果诡异的事发生了:日志里清清楚楚显示"共 5000 条",可数据库里,一条都没写进去。我盯着代码反复看:遍历两次的代码一模一样,凭什么第一次能数出 5000 条、第二次就一条都没有?我甚至怀疑是数据库连接的问题,查了半天连接好好的。排查了大半天,最后才撞上 Python 那个对新手极其隐蔽的坑:生成器(generator)只能被消费一次。这篇就把这场"数据凭空消失"的事故,从头复盘一遍。

故障现场:数出 5000 条,写入 0 条

先看现场。问题就藏在那个我以为"返回了一批数据"的函数里:

# 我的函数: 我以为它"返回了一批记录"
def get_records():
    for line in open("data.txt"):
        record = parse(line)
        if record.is_valid():
            yield record          # ← 用了 yield, 这其实是个【生成器函数】!

# 调用方: 遍历两次
records = get_records()           # 我以为 records 是"一批数据"(像 list)

# 第一次遍历: 统计总数
count = sum(1 for _ in records)   # ← 这一步把生成器【消费光了】!
print(f"共 {count} 条")            # 输出: 共 5000 条  ✓ 看起来没问题

# 第二次遍历: 写入数据库
written = 0
for record in records:            # ← 此时 records 已经空了! 一条都不剩
    db.insert(record)
    written += 1
print(f"写入 {written} 条")         # 输出: 写入 0 条  ✗ 一条都没写!

# 现象拼图:
#   - get_records() 里用了 yield, 所以它是【生成器函数】,
#     调用它返回的不是 list, 而是一个【生成器对象】。
#   - 生成器是"惰性"的: 它不预先把所有数据算出来存着,
#     而是"边遍历边生成"、且【只能从头到尾遍历一次】。
#   - 第一次 sum(...) 遍历, 把生成器从头走到尾、消费殆尽。
#   - 第二次 for, 生成器已经走到尽头了, 没有"重置/回到开头"这回事,
#     所以直接结束、一条都不产出。
#   - 我误把"只能用一次的生成器", 当成了"能反复遍历的 list"。

看清真相后,我哭笑不得。问题的根源,是 get_records() 里用了 yield——这让它变成了一个生成器函数,调用它返回的不是 list,而是一个生成器对象而生成器有一个致命(对不了解的人而言)的特性:它是"惰性"的,边遍历边生成、且只能从头到尾遍历一次我的第一次 sum(1 for _ in records),把这个生成器从头走到了尾、消费殆尽;等到第二次 for record in records 时,生成器早已走到尽头,没有"重置回开头"这回事,于是直接结束、一条都不产出。我把一个"只能用一次的生成器",当成了"能反复遍历的 list",这才导致了"数出 5000、写入 0"的诡异现象。

第一件事:搞懂生成器到底是什么、为什么只能用一次

要解决它,得先真正理解生成器(generator)的本质,以及它"只能消费一次"背后的原理。

生成器(generator)详解

# 什么是生成器?
#   - 函数里只要有 yield, 它就是"生成器函数"。
#   - 调用生成器函数, 不会执行函数体, 而是立刻返回一个"生成器对象"。
#   - 生成器是一种【迭代器(iterator)】。

# 生成器的核心特性: 惰性 + 一次性
#   1. 惰性(lazy): 不预先算好所有值, 而是"每次要一个, 才算一个"。
#      → 省内存! 处理10亿条数据也不会把它们全装进内存。
#   2. 一次性(单向): 只能从头到尾遍历一次, 遍历完就"耗尽"了。
#      → 没有"回到开头"、没有"再来一遍"。耗尽后再遍历, 直接是空。

# 为什么只能一次? —— 它没存数据, 只存"状态"
#   - list 是"已经算好的全部数据", 存在内存里, 你想遍历几次都行。
#   - 生成器只保存"我执行到哪了"这个状态(一个游标), 不保存产出过的值。
#   - 一旦游标走到末尾, 就没有数据可产出了, 也无法倒回去。

# 哪些东西也是"一次性"的迭代器?(同样的坑)
#   - 生成器表达式: (x for x in data)   ← 注意和列表推导 [x...] 区别!
#   - map() / filter() / zip() 的返回(Python 3): 都是一次性迭代器!
#   - 文件对象 open(...): 遍历一次就到文件末尾了。

# list vs 生成器:
#   - [x for x in data]  → 列表: 立即算好全部, 占内存, 可反复遍历。
#   - (x for x in data)  → 生成器: 惰性, 省内存, 只能遍历一次。

# 核心: 生成器是惰性+一次性的迭代器, 只存"执行状态"不存数据, 遍历一次即耗尽,
#   无法重置或重复遍历; map/filter/zip/文件对象在Python3里也都是一次性的。

原来,生成器的"只能用一次",是它本质决定的。函数里只要有 yield,它就是生成器函数;调用它返回一个生成器对象,而生成器是一种迭代器它有两个核心特性:惰性(不预先算好所有值,每次要一个才算一个,因此极省内存,处理 10 亿条也不爆)一次性(只能从头到尾遍历一次,遍历完就耗尽,没有"回到开头")为什么只能一次?关键在于:它没存数据,只存"状态"——list 是"已算好的全部数据"存在内存里、想遍历几次都行;而生成器只保存"我执行到哪了"这个游标,不保存产出过的值,一旦游标走到末尾,就没数据可产出、也无法倒回更要警惕的是,很多东西同样是"一次性"的迭代器:生成器表达式 (x for x in data)(注意和列表推导 [x...] 的区别!)、map()/filter()/zip() 在 Python 3 里的返回、文件对象——它们都遍历一次就耗尽。

第二件事:正解——按需求选 list 还是生成器

搞懂了原理,正解就清晰了:要多次遍历就 list() 物化;只遍历一次且数据量大就保留生成器;别把一次性迭代器当可复用集合

# ====== 正解一: 要多次遍历, 就 list() 物化成列表 ======
records = list(get_records())     # ✓ 立刻把生成器跑完, 结果存进 list

count = len(records)              # list 可以 len(), O(1)
print(f"共 {count} 条")
for record in records:           # ✓ list 可以反复遍历, 第二次依然有数据
    db.insert(record)
# → 数据被实实在在装进了内存 list, 想遍历几次都行。

# ====== 正解二: 只遍历一次 + 数据量大, 才保留生成器(发挥惰性优势)======
# 如果数据有10亿条、只需要处理一遍、不想全装进内存:
total = 0
for record in get_records():     # 边生成边处理, 内存友好
    db.insert(record)
    total += 1                    # 用一个计数器在遍历中统计, 而不是遍历两次
print(f"处理 {total} 条")
# → 这种"大数据 + 单次遍历"场景, 生成器才是正解(别 list, 会爆内存)。

# ====== 正解三: 既要省内存、又要统计, 就在一次遍历里把事都做了 ======
count = 0
total_amount = 0
for record in get_records():     # 只遍历这一次
    db.insert(record)
    count += 1
    total_amount += record.amount # 顺便统计, 不用再遍历
print(f"共{count}条, 总额{total_amount}")
# → 把"多次遍历各做一件事", 合并成"一次遍历做所有事"。

# ====== 正解四: 警惕 map/filter/zip 也是一次性的 ======
result = map(transform, data)
print(len(list(result)))         # 想 len 得先 list... 但 list 后 result 就空了
for x in result:                 # ✗ 空的! 上面 list(result) 已耗尽它
    ...
# ✓ 修法: 一开始就 result = list(map(transform, data)), 之后随便用。

# ====== 正解五: 想"判断生成器是不是 list"避免误用 ======
# 拿不准一个对象能否反复遍历时:
# - list/tuple/dict/set 可反复遍历(它们是"可迭代对象", 每次iter给新迭代器)
# - 生成器/map/filter/zip/文件 是"迭代器自身", 一次性
# 实在不确定, 接收到就 list() 一下(除非确定是大数据要省内存)。

# 核心: 多次遍历/要len → list()物化; 大数据单次遍历 → 保留生成器(省内存);
#   需要统计就在一次遍历里顺便做; 警惕map/filter/zip/文件也是一次性迭代器。

修复的核心,是"根据'要遍历几次'和'数据量多大',在 list 和生成器之间做对选择"正解一:要多次遍历就 list() 物化——立刻把生成器跑完、结果存进 list,之后 len()、反复 for 都没问题(这就是我这次该用的解法)。正解二:只遍历一次 + 数据量大,才保留生成器——10 亿条只处理一遍时,生成器的惰性才是正解(list 会爆内存)。正解三:既要省内存又要统计,就在一次遍历里把事都做了——把"多次遍历各做一件事"合并成"一次遍历做所有事"(用计数器顺便统计)。正解四:警惕 map/filter/zip 也是一次性的——list(result) 之后 result 就空了,要么一开始就 list(map(...))正解五:拿不准能否反复遍历时——list/tuple/dict/set 可反复遍历,生成器/map/filter/zip/文件是一次性;不确定就接收后 list() 一下(除非确定是大数据)。归根结底:多次遍历/要 len 就 list() 物化;大数据单次遍历保留生成器;需要统计就在一次遍历里顺便做;警惕 map/filter/zip/文件也是一次性。

第三件事:可迭代对象 vs 迭代器,这对概念别再混

排查时我才真正分清了两个一直含混的概念:可迭代对象(Iterable)和迭代器(Iterator)。它们的区别正是这个坑的根。

可迭代对象(Iterable) vs 迭代器(Iterator)

# 可迭代对象(Iterable):
#   - 能被 for 遍历的东西。实现了 __iter__(), 每次调用返回一个【新的迭代器】。
#   - 例: list, tuple, dict, set, str ... 它们本身不是迭代器!
#   - 关键: 每次 for 它, 都会 __iter__() 拿一个【全新的迭代器】从头开始,
#     所以可以反复遍历。

# 迭代器(Iterator):
#   - 实现了 __next__()(和返回自身的 __iter__())。
#   - 它保存"遍历的当前状态", 每次 next() 吐一个值, 直到 StopIteration。
#   - 例: 生成器, map/filter/zip 对象, 文件对象, iter(list)的结果。
#   - 关键: 它【就是那个游标本身】, 遍历完就到头了, 没有"新的从头开始"。

# 一张图理解为什么 list 能反复遍历、生成器不能:
#   list  --(每次for, __iter__)-->  新迭代器A(从头) ... 用完丢弃
#         --(再次for, __iter__)-->  新迭代器B(从头) ... 又能遍历
#   生成器 它本身就是迭代器, for它 = 用它自己这个游标,
#         用完了, 再for = 游标已在末尾 = 空。

# 验证小实验:
nums = [1, 2, 3]
print(iter(nums) is iter(nums))   # False: list每次给"新"迭代器
gen = (x for x in nums)
print(iter(gen) is iter(gen))     # True:  生成器iter返回的是它自己!
#   → 这就是为什么 list 能反复遍历, 生成器只能一次。

# 核心: 可迭代对象(list等)每次for都生成新迭代器, 故可反复遍历;
#   迭代器(生成器/map/filter/文件)本身就是游标, 一次性, 用完即尽。

这一对概念分清后,所有的困惑都烟消云散了。可迭代对象(Iterable):能被 for 遍历、实现了 __iter__(),每次调用都返回一个全新的迭代器(list、tuple、dict、set、str 都是);正因每次 for 都拿一个全新迭代器从头开始,所以能反复遍历迭代器(Iterator):实现了 __next__()保存遍历的当前状态(生成器、map/filter/zip、文件对象都是);它就是那个游标本身,遍历完就到头了,没有"新的从头开始"一个小实验一秒看懂:iter(list) is iter(list)False(list 每次给"新"迭代器),而 iter(gen) is iter(gen)True(生成器的 iter 返回它自己)——这就是 list 能反复遍历、生成器只能一次的根本原因。归根结底:可迭代对象每次 for 都生成新迭代器故可反复遍历;迭代器本身就是游标,一次性、用完即尽。下面这张图,是这次生成器耗尽导致数据消失的成因与解法:

第四件事:常见"一次性迭代器"速查

这次踩坑后,我把 Python 里那些"看着像集合、其实只能用一次"的东西整理成一张表,贴在心里防误用。

对象 能反复遍历吗 类型 想复用怎么办
list / tuple / dict / set ✓ 可反复 可迭代对象 本身就能反复用
生成器函数(含yield)的返回 ✗ 一次性 迭代器 list() 物化
生成器表达式 (x for x in ...) ✗ 一次性 迭代器 改用 [x for x in ...]
map() / filter() ✗ 一次性(Py3) 迭代器 list(map(...))
zip() / enumerate() ✗ 一次性 迭代器 list(zip(...))
文件对象 open() ✗ 一次到文件尾 迭代器 readlines() 或 seek(0)
reversed() ✗ 一次性 迭代器 list(reversed(...))

这张表,把"哪些能反复用、哪些只能用一次"一网打尽了。记忆诀窍:用方括号 [] 或本来就是容器的(list/dict/set),能反复遍历;凡是"惰性生成"的(yield、圆括号生成器表达式、map/filter/zip/enumerate/reversed、文件),都是一次性它给我的启发是:Python 为了"省内存、高效率",在很多地方默默地用了"惰性迭代器"替代"立即求值的列表"(比如 Python 3 把 map/filter/range/dict.keys() 都改成了惰性的)。这个设计很聪明、很高效,但它对使用者有一个隐含的要求:你得知道"你手里这个东西,到底是'已经算好的数据'还是'一个待消费的惰性流'"一旦把"惰性流"误当成"数据",就会踩这次的坑。所以我现在的习惯是:拿到一个"序列样"的东西,先在心里问一句"它能遍历第二次吗";不确定、且数据不大,就 list() 一下落袋为安

第五件事:生成器的好处,以及它适合的场景

这次虽然被生成器坑了,但它绝不是"坏东西"。我也梳理了它真正发光的场景,免得因噎废食。

场景 用 list 用生成器
处理超大/无限数据流 ✗ 内存爆炸 ✓ 惰性,恒定内存
只需遍历一次 △ 多占内存 ✓ 省内存,最合适
需要多次遍历 ✓ 可反复用 ✗ 第二次就空
需要随机访问/索引/len ✓ 支持 ✗ 不支持
流水线处理(链式转换) 每步都生成中间list ✓ 惰性串联,不产中间数据
提前结束(找到就停) △ 可能已算全部 ✓ 惰性,用多少算多少

这张表,让我看清了生成器真正的价值。它的高光场景是:处理超大/无限数据流(恒定内存)、只需遍历一次、流水线式链式处理(惰性串联不产中间 list)、提前结束的查找(用多少算多少)而它不适合的,正是我这次的场景:需要多次遍历、需要随机访问/索引/len——这些就该用 list。它给我的最大启发是:生成器(惰性求值)和 list(立即求值),是一对经典的"空间与灵活性"的权衡:生成器用"只能遍历一次、不能随机访问"的限制,换来了"极省内存、可处理无限流"的能力;list 用"占用全部内存"的代价,换来了"可反复遍历、随机访问"的灵活没有谁更好,只有"匹配场景"才最好更深一层,这让我体会到:理解一个特性,不能只记"它怎么用",更要理解"它用什么换什么"(它的设计权衡);因为正是那个""(为了省内存而牺牲了可重复遍历),藏着它最容易坑人的地方,也藏着它最适合的舞台看懂了权衡,才能既用对它的长处、又躲开它的陷阱。

第六件事:拿到一个"序列"时,我现在的判断习惯

现在再拿到一个要遍历的东西,我不再想当然地当 list 用,而是按这张图先判断它的本质:

这张图的精髓,是"先判断它是'一次性迭代器'还是'可反复容器',再决定怎么用"第一问是 "它从哪来":含 yield 的函数、生成器表达式、map/filter/zip/文件,都是一次性迭代器;list/dict/set 字面量是可反复遍历的容器。如果是一次性的,再问 "要遍历几次":多次/要 len/要索引就 list() 物化;只一次再看数据量,大就保留生成器(顺便在遍历里统计)、不大就 list() 更省心而最后那一步,是我现在的硬习惯:遍历前确认"我要用的数据真的还在"(尤其当这个对象在别处可能已经被遍历过)。这套习惯,让我处理序列时,从"想当然当 list 用"变成了"先看清它的本质"——核心始终是:遍历一个东西前,先搞清它是"已经算好的数据"还是"只能消费一次的惰性流"。

我立下的几条规矩

这场"数据凭空消失"的事故,换来了我写 Python 时,刻进骨子里的几条铁律:

  1. 含 yield 的函数返回的是生成器,不是 list。它惰性、只能遍历一次。
  2. 要多次遍历就 list() 物化。别拿一次性迭代器当可复用集合用。
  3. 大数据/无限流只遍历一次才保留生成器。发挥惰性省内存的优势,别盲目 list 爆内存。
  4. 需要统计就在一次遍历里顺便做。别为了数个数再单独遍历一遍(把它耗尽)。
  5. 警惕 map/filter/zip/文件/reversed 都是一次性的。Python 3 默认惰性。
  6. 分清可迭代对象与迭代器。前者每次 for 给新迭代器可反复,后者本身是游标用完即尽。
  7. 拿不准就 list() 落袋为安。除非确定是大数据要省内存。

附:亲手验证"生成器耗尽"的一组实验

口说无凭。下面这段代码,让你亲眼看见生成器第二次遍历为空、以及 list 化后就正常,跑一遍胜过看十遍解释:

# ====== 实验1: 生成器第二次遍历是空的 ======
def gen():
    for i in range(3):
        yield i

g = gen()
print("第一次遍历:", list(g))   # [0, 1, 2]
print("第二次遍历:", list(g))   # []  ← 空了! 生成器已耗尽
print()

# ====== 实验2: 改用 list, 就能反复遍历 ======
data = list(gen())             # 立刻物化成 [0, 1, 2]
print("第一次:", list(data))    # [0, 1, 2]
print("第二次:", list(data))    # [0, 1, 2]  ← 依然有! list 可反复遍历
print()

# ====== 实验3: 复现我的事故 —— 先统计再处理 ======
def get_records():
    for i in range(5):
        yield f"record-{i}"

records = get_records()
count = sum(1 for _ in records)        # 把生成器消费光
print(f"统计到 {count} 条")              # 统计到 5 条
written = [r for r in records]          # 此时已空
print(f"处理到 {len(written)} 条")       # 处理到 0 条  ← 复现"数5写0"!
print()

# ====== 实验4: 正确做法 —— 先 list 物化 ======
records = list(get_records())          # ✓ 先物化
count = len(records)
written = [r for r in records]          # 还在!
print(f"统计 {count} 条, 处理 {len(written)} 条")  # 统计 5 条, 处理 5 条 ✓
print()

# ====== 实验5: map 也是一次性的(同样的坑)======
m = map(str, range(3))
print("map第一次:", list(m))    # ['0', '1', '2']
print("map第二次:", list(m))    # []  ← map 也耗尽了!

# ====== 实验6: 用 itertools.tee 复制迭代器(需要两次遍历但又想保持惰性)======
import itertools
g1, g2 = itertools.tee(gen(), 2)       # 把一个生成器"复制"成两个独立的
print("tee g1:", list(g1))             # [0, 1, 2]
print("tee g2:", list(g2))             # [0, 1, 2]  ← 各自独立可遍历

# 核心: 生成器/map第二次遍历为空(已耗尽); list物化后可反复遍历;
#   既要惰性又要遍历两次, 用 itertools.tee 复制。亲手跑一遍, 印象深刻。

这组实验,把"生成器只能用一次"这个抽象的坑,变成了可以亲手运行、亲眼验证的现象实验 1 直接展示了核心现象:同一个生成器,第一次 list(g)[0,1,2],第二次就成了 [];实验 3 一比一复现了我的事故("统计 5 条、处理 0 条");实验 4 证明 list() 物化就能既统计又处理;实验 5 提醒 map 同样是一次性的;实验 6 还给了个进阶招——itertools.tee 能把一个迭代器"复制"成多个独立的,适合"既想保持惰性、又要遍历两次"的场景这,正是我想用这组实验,留给每个学 Python 的人的最后一课:对于任何"反直觉、难记住、容易栽跟头"的语言特性,与其反复背诵规则,不如动手写一组最小实验,把它的行为亲眼""出来当我亲眼看着控制台打印出那个刺眼的 [] 时,"生成器只能用一次"这件事,就再也不会从我脑子里溜走了——因为亲手验证过的知识,和读到过的知识,在记忆里的分量完全不同:前者是"我见过它发生",后者只是"我听说过"把抽象的规则,变成具体的、可复现的实验现象——这是我对抗"那些总也记不住的坑"最有效的办法,也是把别人的经验真正变成自己的本能,最扎实的一步。

写在最后

回头看,这场由"生成器只能消费一次"引发的、数据凭空消失的事故,真正教给我的,远不止"记得 list()"这一个技巧。它让我对一类极其普遍的认知陷阱,有了警觉:很多东西,"看起来像"某种我们熟悉的东西,用起来在某些情况下也确实像,于是我们就想当然地以为它"就是"那种东西,把对那种东西的全部假设,都套了上去。我手里的生成器,"看起来"就像一批数据:我能 for 它、能 sum 它,和用 list 一模一样;于是我理所当然地以为它"就是"一批数据,能反复遍历——直到第二次遍历的空结果,狠狠地提醒我:它"像" list,但它"不是" list;它在"能否重复遍历"这个我没留意的维度上,和 list 有着本质的不同这让我领悟到一个朴素却深刻的道理:判断一个事物,不能只凭它"表面上像什么""在我试过的场景里表现得像什么",而要去理解它"本质上是什么""它和我以为的那个东西,在哪些维度上其实不同""鸭子类型"(看起来像鸭子就当鸭子)在很多时候是便利的,但它的危险,恰恰在于会让我们忽略那些"表面相似、本质不同"的关键差异;而 bug,往往就诞生在这些被忽略的差异里。对任何"我以为我懂"的东西,多问一句"它和我以为的,真的完全一样吗?有没有哪个维度其实不同?"——这份不轻易"想当然"的审慎,是写出可靠代码的底层素养。这,是我用一次"数出 5000、写入 0"的事故,换来的、关于 Python、也关于"看穿表面相似"的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次拿到一个要遍历两次的东西时,先愣一下问问"它能遍历第二次吗",那我对着那个空荡荡的第二次循环熬的这大半天,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

我的 Agent 调用一次查询工具就把上万行结果原样塞回上下文,从此推理越来越笨、还越来越贵,我对着工具返回结果的治理排查了大半天的复盘

2026-6-2 6:05:25

技术教程

我把对象的方法直接传给 setTimeout 当回调,运行到一半就报 this 是 undefined,我对着 JavaScript 的 this 指向丢失排查了大半天的复盘

2026-6-2 6:16:13

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索