JavaScript-一个经典的坑:条件式函数声明同名变量,块作用域,直接赋值,变量提升

本文深入探讨了JavaScript中块级作用域与函数声明提升的复杂性。通过分析代码示例,解释了为何在不同执行顺序下,全局变量e的值会是函数而非预期的数值。重点讲解了预解析阶段函数声明的行为差异,以及在ES5和ES6环境下块级作用域的影响。同时,文章提醒读者注意块级作用域内函数声明的提升顺序与赋值操作的相互作用,这对理解JavaScript代码的执行逻辑至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    var e = 1
    if (true) {
      function e() {}
      e = 2
    }
    console.log(e);

问题1:为什么打印结果是函数?

问题2:为什么e=2的赋值操作失效了?

虽然代码只有几行,但是里面涉及的知识点异常的多.

代码运行环境: 谷歌浏览器(其它浏览器可能有不同的行为表现)

第一个知识点:

JS代码预解析与变量提升和函数声明提升。在全局作用域和块级作用域下,函数声明提升与函数声明语句执行有不同的行为。

  1. 在全局作用域下的函数声明语句,预解析时函数名与函数值直接挂到GO对象上,值就是声明的函数,在执行代码时不会再执行函数声明语句
  2. 在块级作用域下的函数声明语句,块作用域里代码执行前的预解析时,函数名与函数值挂到VO对象上,值是声明语句的函数,在执行代码时还会再执行函数声明语句。这里执行函数声明语句时只做一件事:就是把函数名此时的值赋值给GO对象里的同名变量
  3. 块级作用域里的函数声明在整个程序代码执行前,会把函数名挂到GO对象上,不论嵌套有多深,值为undefined,而它的真正的值,取决于块级函数声明执行时抛出的值

在执行代码前JS会对var声明的变量进行提升,除此之外还会对非函数作用域里的函数声明进行全局变量提升(对所谓块作用域进行全局变量提升是es5的行为,对es5来说没有块作用域,所以es6概念里的块作用域下的函数声明必须被提升到全局。但是如今es6引入了块作用域的概念,所以没有把块作用域下的函数声明值挂载到GO对象里,浏览器的这种怪异行为是为了兼容以前es5时代的代码)。(这一段话非常重要,是理解这段代码运行结果的基础)

预解析之后,在全局作用域里会出现一个变量e,且值为undefined。

这段代码中,var e 会在全局声明一次e,function e 也会在全局声明一次e(这里执行的是es5的行为,es5没有块作用域),都是相同变量名,且值都为undefined,可以理解为是等价的,没有谁覆盖谁。

关键在于后面的代码执行,会如何改变全局变量e的值?

代码执行到 if 语句里后:

最为关键的行为来了:块级作用域里的代码执行前,JS先对里面的函数声明进行提升,挂载到块级作用域里的VO对象里(这里执行的是es6的行为)。然后执行function e(){}这段代码,这段代码执行时候,会把自己的变量名e的值赋值给全局变量里的同名变量(这里是为了统一以上es5行为和es6行为导致的值的变化)。(这里非常重要,彻底改变了全局变量e的值)

执行 if 里的第二行代码:因为执行上下文的关系,e = 2的操作会先在块作用域里查到变量e,块作用域里有被函数声明的变量e,所以块作用域里的e被赋值成了2。

执行最后的打印代码:在全局里打印e,打印结果是全局e里的值,该值给赋值成了函数,所以打印出来是一个函数。

关于问题2:其实赋值操作没有失效,只是没有像我们想当然的那样以为会把函数值覆盖掉,或者说我们一直认为e=2操作的是全局变量,其实操作的是块作用域里的函数声明的变量e。

拓展知识点:

在预解析时,块级作用域下,函数声明会被提升到全局,且值为undefined。

在全局作用域下,函数声明的值为该函数。

主要是因为块级作用域的关系,把函数声明的行为割裂成了两种情况。而这个问题就是因为es6引入块级作用域引起的。

作用域里的声明提升在代码执行之前。

如果把以上代码修改一下,变成这样: 猜猜最后打印结果是什么?

    var e = 1
    if (true) {
      e = 2
      function e() {}
    }
    console.log(e);

e=2赋值操作放在函数声明之前。

前面的执行和上一段代码一样,关键在于块作用域里的执行顺序发生了变化,最后打印出来的结果上上一段代码完全不一样。

块级作用域里的代码执行前,作用域里的函数声明先会被挂载到VO对象里(拓展:全局作用域里,代码执行前函数声明先被挂载到GO对象里)

然后,e=2赋值操作会修改VO对象里函数名e的值,此时函数名e的值为2。

执行到函数声明语句,它会把VO对象里的函数名的值赋值给GO对象里的同名变量,此时GO对象里的e值就是2。

最后打印结果就是2,你猜对了吗?

### 地面实况边界预测边界统计 在目标检测领域,地面实况边界ground truth bounding boxes)是指标注数据集中由人工精确标记的目标位置矩形区域。这些边界提供了对象的真实位置信息,在评估模型性能时作为标准参照。 对于预测边界(predicted bounding boxes),这是指通过训练好的神经网络对测试图片进行推理得到的对象定位结果。为了衡量预测相对于实际的位置准确性,通常会计算两者之间的交并比(Intersection over Union, IoU)。IoU 是一种广泛使用的度量方法来评价两个边界重叠程度的好坏[^1]。 除了 IoU 外,还可以考虑其他一些统计数据: - **中心点偏移**:测量预测中心坐标与真实中心坐标的距离差异。 - **宽高比例误差**:比较预测宽度/高度同真实相应维度的比例差距。 - **置信度得分分布**:分析不同阈值下正样本被正确识别的概率密度函数曲线变化情况。 此外,损失函数的选择也会影响最终输出的质量。例如,在某些实现中可能会采用 L1 Loss 或者 GIoU Loss 来优化回归任务中的边界拟合效果[^2]。 ```python def calculate_statistics(gt_boxes, pred_boxes): """ 计算一组真实预测间的多种统计指标 参数: gt_boxes (list): 真实列表 [(x_min,y_min,x_max,y_max)] pred_boxes (list): 预测列表 [(x_min,y_min,x_max,y_max,score)] 返回: dict: 含有各项统计信息的结果字典 """ stats = {} ious = [] center_offsets = [] for gt_box, pred_box in zip(gt_boxes, pred_boxes): # Calculate IOUs between ground truths and predictions intersection_area = max(0,min(pred_box[2],gt_box[2])-max(pred_box[0],gt_box[0])) * \ max(0,min(pred_box[3],gt_box[3])-max(pred_box[1],gt_box[1])) union_area = ((pred_box[2]-pred_box[0])*(pred_box[3]-pred_box[1]) +(gt_box[2]-intersection_area) iou = intersection_area / float(union_area) ious.append(iou) # Compute offset from prediction to GT centers dx = abs((pred_box[0]+pred_box[2])/2-(gt_box[0]+gt_box[2])/2) dy = abs((pred_box[1]+pred_box[3])/2-(gt_box[1]+gt_box[3])/2) center_offset = math.sqrt(dx*dx + dy*dy) center_offsets.append(center_offset) avg_iou = sum(ious)/len(ious) if len(ious)>0 else 0. avg_center_offset = sum(center_offsets)/len(center_offsets) if len(center_offsets)>0 else 0. stats['average_IOU'] = round(avg_iou,4) stats['avg_center_distance'] = round(avg_center_offset,4) return stats ```
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值