go词法作用域陷进
问题
// 创建一些目录,再将目录删除
// 错误写法
var rmdirs []func()
for _, dir := range tempDirs() {
os.MkdirAll(dir, 0755)
rmdirs = append(rmdirs, func(){
os.RemoveAll(dir)
})
}
// 正确写法
var rmdirs []func()
for _, d := range tempDirs() {
dir := d // 这一步赋值比较关键,是重点
os.MkdirAll(dir, 0755)
rmdirs = append(rmdirs, func() {
os.RemoveAll(dir)
})
}
for _, rmdir := range rmdirs{
rmdir()
}
分析
你可能会感到困惑,为什么要在循环体中用循环变量d赋值一个新的局部变量,而不是直接使用循环变量dir
问题的原因在于循环变量的作用域。在上面的程序中,for循环语句引入了新的词法块,
循环变量dir在这个词法块中被声明。在该循环中生成的所有函数值都共享相同的循环变量。
需要注意,函数值中记录的是循环变量的内存地址,而不是循环变量某一个时刻的值。以dir为例
后续的迭代会不断更新dir的值,当删除操作执行时,for循环已完成,dir中存储的值
等于最后一次迭代的值。这意味着,每次对os.RemoveAll的调用删除的都是相同的目录
通常,为了解决这个问题,我们会引入一个与循环变量同名的局部变量,作为循环变量
的副本。比如下面的变量dir,虽然看起来很怪,但很有用
for _, dir := range tempDirs(){
dir := dir
}
当然这个问题不仅存在基于range循环,下面的例子中,对循环变量i的使用也存在同样的问题
var rmdirs []func()
dirs := tempDirs()
for i := 0;i<len(dirs);i++{
os.MkdirAll(dirs[i], 0755)
rmdirs = append(rmdirs, func(){
os.RemoveAll(dirs[i])
})
}
如果你使用go语句或者defer语句会经常遇到此类问题。这不是go或defer本身导致的,
而是因为它们都会等待循环结束后,再执行函数值。
go词法作用域陷进的更多相关文章
- JavaScript夯实基础系列(一):词法作用域
作用域是一组规则,规定了引擎如何通过标识符名称来查询一个变量.作用域模型有两种:词法作用域和动态作用域.词法作用域是在编写时就已经确定的:通过阅读包含变量定义的数行源码就能知道变量的作用域.Jav ...
- Javascript中的词法作用域、动态作用域、函数作用域和块作用域(四)
一.js中的词法作用域和动态作用域 词法作用域也就是在词法阶段定义的作用域,也就是说词法作用域在代码书写时就已经确定了. js中其实只有词法作用域,并没有动态作用域,this的执 ...
- 你不知道的Javascript(上卷)读书笔记之二 ---- 词法作用域
在前一篇文章中,我们把作用域定义为"管理.维护变量的一套规则",接下来是时候来深入讨论一下Js的作用域问题了,首先我们要知道作用域一般有两种主要的工作类型,一种是词法作用域,一种是 ...
- Coursera系列-R Programming第三周-词法作用域
完成R Programming第三周 这周作业有点绕,更多地是通过一个缓存逆矩阵的案例,向我们示范[词法作用域 Lexical Scopping]的功效.但是作业里给出的函数有点绕口,花费了我们蛮多心 ...
- JavaScript闭包之“词法作用域”
大家应该写过下面类似的代码吧,其实这里我想要表达的是有时候一个方法定义的地方和使用的地方会相隔十万八千里,那方法执行时,它能访问哪些变量,不能访问哪些变量,这个怎么判断呢?这个就是我们这次需要分析的问 ...
- 深入理解javascript作用域系列第二篇——词法作用域和动态作用域
× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...
- JavaScript 使用词法作用域,没有动态作用域
function foo() { console.log( a ); } function bar() { var a = 3; foo(); } var a = 2; bar(); 上面的代码,控制 ...
- JavaScript高级之词法作用域和作用域链
主要内容: 分析JavaScript的词法作用域的含义 解析变量的作用域链 变量名提升时什么 一.关于块级作用域 说到JavaScript的变量作用域,与咱们平时使用的类C语言不同. ...
- 《你不知道的JavaScript》读书笔记(二)词法作用域
JavaScript 采用的是 词法作用域 的工作模型. 定义 词法化:大部分标准语言编译器的第一个工作阶段叫词法化(单词化),这个过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词 ...
- js词法作用域规则
function foo() {console.log( a ); // 2不是3} function bar() {var a = 3;foo();} var a = 2;bar(); js中的作用 ...
随机推荐
- [转帖]chrome历史版本及重大变化(维基百科)
Google Chrome是Google LLC开发的免费 网络浏览器.开发过程分为不同的"发布渠道",每个发布渠道都在单独的开发阶段进行构建.Chrome提供了4种渠道:稳定版, ...
- [转帖]cx_Oracle.DatabaseError: ORA-28040
背景: python第三方库cx-Oracle连接Oracle数据库报错 ORA-28040 cx_Oracle.DatabaseError: ORA-28040: No matching authe ...
- [转帖]内存随机访问也比顺序慢,带你深入理解内存IO过程
https://zhuanlan.zhihu.com/p/86513504 平时大家都知道内存访问很快,今天来让我们来思考两个问题: 问题1: 内存访问一次延时到底是多少?你是否会进行大概的估算? 例 ...
- [转帖]Linux如何查看网关地址
转至:https://baijiahao.baidu.com/s?id=1733537078943023051&wfr=spider&for=pc 服务器之间的通信是通过ip地址来 ...
- [转帖]Linux命令拾遗-top中的%nice是啥
https://www.cnblogs.com/codelogs/p/16060663.html 简介# 这是Linux命令拾遗系列的第八篇,本篇主要介绍top命令中nice%这个指标的含义以及进程优 ...
- 龙芯中标麒麟 上面安装libgdiplus的方法
其实方法与之前的blog 基本上完全一样 但是发现有一个问题 安装完libgdiplus之后必须重启一下才能有效果... CentOS 安装libgdi的方法 1. 安装必须的包 1 yum ins ...
- Object.getOwnPropertyDescriptor()属性
console.log(Object.getOwnPropertyDescriptor(person,'a')); // writable: 是否可以修改 // value: 对象值 // confi ...
- grafana+prometheus+loki的使用
grafana官网:https://grafana.com/zh-cn/grafana/ grafana下载:https://grafana.com/grafana/download?pg=graf& ...
- uni-app接口请求封装
首先根目录下新建文件夹取名随意,这里我取名common(意为:常见的.共有的) 然后新建request.js文件,贴入以下代码 let server_url = ''; //请求根路径(服务器地址) ...
- Git企业开发控制理论和实操-从入门到深入(五)|标签管理
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 然后就是博主最近最花时间的一 ...