ES6标准下在if中进行函数声明

ES5中规定,函数只能在顶层作用域或函数作用域之中声明,不能在块级作用域声明。

// 情况一
if (true) {function f() {}
}// 情况二
try {function f() {}
} catch(e) {// ...
}

上面两种函数声明,根据 ES5 的规定都是非法的。但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

function f() { console.log('I am outside!'); }(function () {if (false) {// 重复声明一次函数ffunction f() { console.log('I am inside!'); }}f();
}());

上面代码在 ES5 中运行,会得到“I am inside!”,因为在if内声明的函数f会被提升到函数头部,实际运行的代码如下。

// ES5 环境
function f() { console.log('I am outside!'); }(function () {function f() { console.log('I am inside!'); }if (false) {}f();
}());

ES6 就完全不一样了,理论上会得到“I am outside!”。因为块级作用域内声明的函数类似于let,对作用域之外没有影响。

但是,如果你真的在 ES6 浏览器中运行一下上面的代码,是会报错的,这是为什么呢?

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }(function () {if (false) {// 重复声明一次函数ffunction f() { console.log('I am inside!'); }}f();
}());
// Uncaught TypeError: f is not a function

原来,如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6 在附录B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。

  1. 允许在块级作用域内声明函数。
  2. 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  3. 同时,函数声明还会提升到所在的块级作用域的头部。

根据这三条规则,浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var声明的变量。上面的例子实际运行的代码如下。

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {var f = undefined;if (false) {function f() { console.log('I am inside!'); }}f();
}());
// Uncaught TypeError: f is not a function

再看一个例子

console.log("第一次输出a: ", a) //输出 本地a
if (true) {// 这里js隐式的把function a的定义放到这里来了,此刻这里有一个 块aa = 1 // 将 块a的值 由函数修改为1console.log("第二次输出a: ",a) // 此时输出的是 块a的值function a() {} // 执行到function a的声明处,此时会将if块中的a同步到外部去,也就是把块a赋值给了本地a, 本地a此刻从undefined被修改为了1a = 2 // 此处修改的是 块a, 块a的值由1修改为2console.log("第三次输出a: ", a) // 输出块a的值
}
console.log("第四次输出a: ",a) // if执行完毕,没有块了,此时输出的是本地a的值

在if语句处打一个断点。当程序执行到if语句时(linenumber 2),本地a的定义是undefined,且不存在块,说明2成立,函数声明a被提升到全局作用域。

当语句执行进入到if时(linenumber4),此时就会产生块,debug停留在a=1;这句时(此时赋值还未进行),块a已经变为了function,3成立,if块中的函数声明被提升到了所在块的顶部。但此时本地a还是undefined:

当debug停留在a=2;这句时(linenumber 7,赋值还未进行),此时块中执行了function a的函数声明,该声明被同步提升到了块外,影响了本地a,本地a此时从undefined变为了1,这也是2的证实,执行到具体的“声明”语句时,全局就有了值,这与ES6中使用var声明变量时的效果是一样的。

当debug执行到if块的最后一句console.log时(linenumber8),打印的是块中a的内容(就近原则,或者说块a遮蔽了本地a),所以输出的结果是2。

 当debug执行到最后一句console.log时(linenumber 10),此时if语句已经执行完毕,块没有了,所以打印的只能是本地a的内容,本地a的内容此时的值是1

===========================华丽的分割线====================================

块内的函数声明会有一次全局性提升(例如例子中的function a),在作用域顶部声明了一个与函数同名的变量(也就是vscode调试中的本地变量a),初始值是undefined。进入块之后,函数声明又会被提升到块的顶部(也就是块变量a,初始值是function)。此时本地一个变量a(值是undefined),块内一个变量(值是function)。然后块内代码继续执行,每次执行到块内的函数声明那句话时,就会进行一次块内变量为本地同名变量的赋值,就好像块内与本地同名变量的同步。

//本地变量a,初始值是undefined
console.log(`1 ${window.a},${a}`);//undefined,undefined
{  //块变量a,初始值是function a{}console.log(`2 ${window.a},${a}`);//undefined,function a(){}function a(){} // 同步console.log(`3 ${window.a},${a}`);//function a(){},function a(){}a = 42;//为块变量a赋值console.log(`4 ${window.a},${a}`);//function a(){},42function a(){}//同步console.log(`5 ${window.a},${a}`);//42,42
}//块变量a消失,此时仅有本地变量a
console.log(`6 ${window.a},${a}`);//42,42

注意,这个示例代码在本地VSCode上会有严重的错误提示(function a不能重复定义!!!),但是在Chrome中可以正常运行,运行的结果就如提示中所示。

考虑到环境导致的行为差异太大,在MDN上有关于块中定义函数时的建议:

应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

函数声明语句并非真正的语句,JS 规范只是允许它们作为顶级语句。它们可以出现在全局代码中,或者内嵌在其他函数中。相比之下,函数表达式是更大意义上表达式的一部分。函数表达式不会出现函数提升,放在哪里就在哪里执行,不会像函数声明那样随意放置具有误导性。

上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。

本文大量参考了阮一峰大神的文章,获益匪浅:ES6 的块级作用域

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mongodb作业

1.创建一个数据库 名字grade use grade2.数据库中创建一个集合名字 class db.createCollection("class")3.集合中插入若干数据 db.class.insert([{name:"zhang",age:10,sex:m,hobby:[a,b,c]}])4.查找 查看班级所有人信息 db.class.find()查看班级中年…

【C++】将类对象转换成基本数据类型

2023年7月19日,周三晚上: 今天晚上看源码的时候,突然在某个类里面看到了“operator bool() const;”,我完全想不起来这是啥意思了,于是今晚重新学习了一下 目录 类型转换函数的定义类型转换函数的作用 类型转换函数的…

王道计算机网络学习笔记(5)——传输层和应用层

前言 文章中的内容来自B站王道考研计算机网络课程,想要完整学习的可以到B站官方看完整版。 五:传输层 5.1:传输层基本概述 传输层的功能: 1传输层提供进程和进程之间的逻辑通信 2复用和分用 微信和QQ都使用传输层的协议进行发…

R语言ggplot2——柱形图

BMIread.table(/Users/zhangzhishuai/Downloads/33 lesson33 ggplot2散点图(一)/33_ggplot2/BMI.txt, header T,row.names 1,sep \t) library(ggplot2) BMI$namerownames(BMI) ggplot(BMI,aes(xname,yheight)) geom_bar(stat identity # identity:数…

Vue3基础知识

文章目录 第一章 vue3 安装1.1安装1.2开启服务器1.3 使用图形化界面1.4 Vite1.5 vue3项目打包1.6 vue3 创建项目1.6.1 vue create命令1.6.2 创建一个项目 第二章 vue3 基础2.1 vue3 的目录结构2.2 vue3 的起步2.2.2 data 选项2,2.3 methods 2.3 Vue3 指令2.4 vue3 模板语法2.4.…

【NLP】使用BERT构建一个情绪分析模型

一、说明 在本教程中,我们将深入挖掘BERT,一个著名的基于“transformer”的模型,并提供一个动手示例来微调用于情绪分析的基本BERT模型。本文提供了有关给定主题的信息摘要。本文属于应用性文章,不应该被视为原创研究。本文中包含的信息和代码可能会受到各种在线文…

vue3和tauri直接下载Binary 数组的二进制文件内容到本地

通过发送url请求,直接获取到一个文件的Binary 数组内容,然后通过tauri的api:writeBinaryFile保存文件到本地电脑。 发送请求的时候,要加上响应类型:responseType: ResponseType.Binary 然后等返回的响应内容&#xf…

Chat GPT是什么,初学者怎么使用Chat GPT,需要注意些什么

目录 Chat GPT是什么 初学者怎么使用Chat GPT 使用Chat GPT需要注意什么 一些简单的prompt示例 Chat GPT是什么 Chat GPT是由OpenAI开发的一种大型语言模型,它基于GPT(Generative Pre-trained Transformer)架构。GPT是一种基于深度学习的…

应对突发流量,如何快速为自建 K8s 添加云上弹性能力

作者:庄宇 以 Kubernetes 为代表的容器技术带来的是一种应用交付模式的变革,其正迅速成为全世界数据中心的统一 API。 为了保证业务持续稳定、用户访问不中断,高可用、高弹性等能力是应用架构设计不变的追求,多集群架构天然具备…

AJAX基础教程

1、引言 就是异步刷新,AJAX要和XMLHttpRequest对象、JavaScript/DOM、CSS、XML一起使用 2、XMLHttpRequest对象 XMLHttpRequest对象,异步的与服务器交换数据,这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新…

【无标题】

Linux下不能连接ppa.launchpad.net的问题(中科大软件源) 警告:操作前请先做好备份。 1. 替换ppa源成中科大源 每个ppa都会在/etc/apt/sources.list.d/目录下创建一个list文件,修改: vi /etc/apt/sources.list.d/ubuntu-toolch…

JAVA找BUG之OOM

大家好哇,我是梦辛工作室的灵,在最近的工作中,又遇到了一个bug,系统运行一段时间后就会频繁OOM,然后直接假死或退出,一度折磨着我,后面不停的翻日志查GC,最后才终于确认位置&#xf…

UNI-APP开发中遇到的坑

UNI-APP开发中遇到的坑 一、页面无法回退二、列表页面下拉刷新不显示数据 一、页面无法回退 在页面中我们一般会在顶部左侧放置一个返回按钮&#xff0c;用户点击返回按钮则关闭当前页面&#xff0c;返回上一层页面。 <uni-nav-bar left-icon"back" title"添…

Python爬虫——urllib_ajax请求的post请求

爬取肯德基官网的门店位置信息&#xff08;现在已经进不去了&#xff0c;所以现在返回的全是-1000&#xff09;&#xff1a; import urllib.request import urllib.parsedef create_request(page):base_url http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?opcnamehea…

使用 uiautomator2+pytest+allure 进行 Android 的 UI 自动化测试

目录 前言&#xff1a; 介绍 pytest uiautomator2 allure 环境搭建 pytest uiautomator2 allure pytest 插件 实例 初始化 driver fixture 机制 数据共享 测试类 参数化 指定顺序 运行指定级别 重试 hook 函数 断言 运行 运行某个文件夹下的用例 运行某…

【软件测试面试】腾讯数据平台笔试题-接口-自动化-数据库

数据库题 答案&#xff1a; Python编程题 答案&#xff1a; 接口参数化题 答案&#xff1a; 接口自动化题 答案&#xff1a; 以下是我收集到的比较好的学习教程资源&#xff0c;虽然不是什么很值钱的东西&#xff0c;如果你刚好需要&#xff0c;可以评论区&#…

高斯误差线性单元激活ReLU以外的神经网络

高斯误差线性单位&#xff08;GELU&#xff09;激活函数由加州大学伯克利分校的Dan Hendrycks和芝加哥丰田技术研究所的Kevin Gimpel于2018年引入。激活函数是触发神经元输出的“开关”&#xff0c;随着网络的深入&#xff0c;其重要性也随之增加。最近几周&#xff0c;机器学习…

create database创建数据库失败

瀚高数据库 目录 环境 症状 问题原因 解决方案 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5.7 症状 1、执行如下sql语句创建数据库报错。 create database printcdburn with encodingUTF8 OWNERprintcdburn LC_COLLATEzh_CN.UTF-…

github 最简单的使用步骤(个人学习记录~)

github 使用步骤&#xff1a; (11条消息) github新手用法详解&#xff08;建议收藏&#xff01;&#xff01;&#xff01;&#xff09;_github详解_怪 咖的博客-CSDN博客 1.获取ssh密钥 打开输入&#xff1a;ssh-keygen -t rsa -C “git账号” 输入之后一路Enter&#xff08…

谈谈VPN是什么、类型、使用场景、工作原理

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 前言 本文将讲解VPN是什么、以及它的类型、使用场景、工作原理。 目录 一、VPN是什么&#xff1f; 二、VPN的类型 1、站点对站点VPN 2、…