深入探究js中的隐式变量声明
前两天遇到的问题,经过很多网友的深刻讨论,终于有一个相对可以解释的通的逻辑了,然后我仔细研究了一下相关的点,顺带研究了一下js中的隐式变量。
- 以下文章中提到的隐式变量都是指没有用var,let,const等关键字定义的变量。
- 以下文章中提到的var变量都是指用var声明定义的变量。
一遇到隐式变量,我们去百度一下,都会看见这样一句话,隐式变量是全局变量,在函数中用隐式变量也是全局变量,但是在函数中用var变量是局部变量,那我们来具体看下隐式变量到底与var变量有什么区别,下面我们来通过一些代码来探究隐式变量与var变量的区别。
1.隐式变量与var变量的区别
代码1:
var变量:
console.log(a);//undefined
var a = 100;
代码2:
隐式变量:
console.log(a);//Uncaught ReferenceError: a is not defined
a = 100;
看上面的代码我们发现var变量a是存在变量声明提升的,也就是代码1相当于下面的代码:
var a;
console.log(a);
a = 100;
这与我们以前了解的那个var变量是一样的,变量声明提升还是那个变量声明提升,并不会改变,这里如果想详细了解变量提升的,推荐这篇文章大家可以看一下。然后我们继续看代码2,隐式变量在前面打印变量a时会报错,那这里我们是不是可以猜测,隐式变量是不是不存在变量提升,当然以前好像也没有人说过隐式变量存在声明提升,可能我一厢情愿的认为隐式变量是全局变量就存在声明提升。然后我们继续看在函数中,代码块中声明的隐式变量是不是跟上面全局定义的隐式变量一样,不存在声明提升。
代码3:
函数中的var变量:
console.log(a);//Uncaught ReferenceError: a is not defined
function b() {
console.log(a);//undefined
var a = 100;
}
b();
代码4:
函数中的隐式变量:
console.log(a);//Uncaught ReferenceError: a is not defined
function b() {
console.log(a);//Uncaught ReferenceError: a is not defined
a = 100;
}
b();
我们来看上面的代码3和代码4,我们发现代码3,在第一行就报错,因为var在函数内部定义的变量属于局部变量,全局是访问不到的,然后把第一行注释掉再运行,发现第三行打印出的a是undefined,证明var变量在函数中进行了变量声明提升。我们来看代码4,在函数内定义了一个隐式变量,然后第一行就会报错,a是未定义,然后把第一行注释掉再运行,发现第三行也还是报错a未定义,这里是不是就说明隐式变量并不会有变量声明提升这种操作,换一句话说,就是隐式变量类似于函数一样,声明和定义是一起的,只有执行了这行代码,才能引用访问这个变量,这里推荐一篇关于变量生命周期的文章,可以细读一下,然后我们再看一下代码块中定义的var变量和隐式变量:
代码5:
代码块中的var变量:
console.log(a);//undefined
{
console.log(a);//undefined
var a = 100;
}
代码6:
代码块中的隐式变量:
console.log(a);//Uncaught ReferenceError: a is not defined
{
console.log(a);//Uncaught ReferenceError: a is not defined
a = 100;
}
然后我们看代码块中的var变量,是存在声明提升的,然后隐式变量是不存在声明提升的,所以上面访问都会报错。
- 结论:根据上面的一系列的探究我们基本可以确定一个结论,那就是隐式变量是不存在变量声明提升的。
2.代码块中的函数声明提升
上面探究完隐式变量我们再稍微看下代码块中的函数声明提升:
代码7:
console.log(a);//undefined
{
function a(){};
console.log(a);//ƒ a(){}
}
console.log(a);//ƒ a(){}
我们看上面的代码,我们都知道函数是js中的"一等公民",优先级是最高的,那在上面这个代码中,函数是否提升到了块作用域的外面呢,如果我说提升了,那你从块作用域最外面调用你会发现报错,a不是一个方法:
a();//Uncaught TypeError: a is not a function
{
function a(){};
}
然后这样之前的我就得出一个结论,函数声明提升并没有将函数提升到最外层,那就要问那上面我们打印的a为undefined,为什么不是报错a未定义呢,根据阮一峰老师的这篇文章的描述,我们可以得出一个结论:

- 块作用域内定义的函数在块作用域内会进行函数提升,提升到最上面,然后在最外层类似于var声明一个同名变量,默认值为undefined。
3.代码块中存在同名的隐式变量与函数的情况
然后我们再回过头来看代码块中同时存在同名的隐式变量和函数时会怎样:
首先我们来看上面这个代码,此时如果我们再最外层的上面打印a和b你会发现会打印出来是undefined:
代码8:
console.log(a,b);//undefined undefined
{
console.log(a);//ƒ a() {}
function a() {};
a = 50;
console.log(a);//50
}
console.log(a);//ƒ a() {}
{
console.log(b);//ƒ b() {}
b = 50;
function b() {};
console.log(b);//50
}
console.log(b);//50
我们看上面的代码,你会发现最前面打印a和b都是undefined,通过前面对隐式变量和函数声明的探究我们可以知道此时外面的a和b都是函数声明定义的,另外我们从侧面也可以看出这一点,vscode有一个功能,是可以查看变量是谁定义的,那我们来看一下:
此时我们发现vscode分析出来最上面的a和b都是a和b函数定义的,根据我们上面的探究,既然隐式变量不存在变量声明提升,那只能函数定义的,然后通过对函数的探究可以证实这一点,同时配上vscode分析出来的结果,也证实了这一点,首先可以确定,最外层的全局的a和b都是函数定义的。然后我们再继续往下看,最开始的a和b打印undefined没问题,
然后我们来看下上面图片上第16行打印的结果为什么是a方法,通过我上篇文章一行一行的调试你会发现,此时代码块中的a其实是被限制在块作用域里面的,并不是全局的变量,此时其实a = 50这行代码不是隐式变量,因为a已经被函数定义过了,那a = 50也就是对之前定义过的变量赋值了,所以此时
a = 50不是隐式变量,然后对之前定义的a赋值,在代码块中打印出来为50,然后出了块作用域打印出来的结果为方法a,通过代码一步步的调试你会发现全局的a,只有在执行a方法的代码时才会把块作用域赋的值同步到全局。
第一步:此时全局的a为undefined,此时a是代码块中函数定义的:

第二步:此时我们发现出来了一个块作用域,因为代码块中的函数提前了,此时代码块中的a的值为a方法,而全局的a还是undefined,没什么问题

第三步:此时我们会发现当函数a这一行代码走完之后,块作用域里面的a跟全局的a都变成了a方法,也就是说想要让块作用域中的a的值同步到全局,必须让代码执行到定义a方法的下一行才可以,要不然代码块中的a的值是不会同步到代码块外面的

第四步:到这一步我们会发现块作用域中的a变成了50,而全局的a还是a方法,根据上一步得到的结论,此时只要在a = 50这行代码后面再执行一次方法a,a = 50就会被同步到全局,此时验证一下也确实是这样

然后出块作用域你会发现打印a为a方法,此时你就不会好奇为什么打印结果是a方法了,因为块作用域中的a并没有同步到全局,而b打印50,是因为b = 50后面执行了一行b方法,将b同步到了全局。到这里我们也就了解为什么两个代码只是换了一下函数的位置打印结果过就完全不同了,然后看文章的时候如果发现有什么错误或写的不好的地方,还请指正,我会立即做出修改。
深入探究js中的隐式变量声明的更多相关文章
- 浅谈JS中 var let const 变量声明
浅谈JS中 var let const 变量声明 用var来声明变量会出现的问题: 1. 允许重复的变量声明:导致数据被覆盖 2. 变量提升:怪异的数据访问.闭包问题 3. 全局变量挂载到全局对象:全 ...
- EL表达式中引用隐式变量
除了在jsp中9大隐式变量(在前面文章也叫预定义变量)在转化成为servlet后_jspService中可以看到: public void _jspService(final javax.servle ...
- c语言中的隐式函数声明(转)
本文转自:http://www.jb51.net/article/78212.htm 在c语言里面开来还是要学习c++的编程习惯,使用函数之前一定要声明.不然,即使编译能通过,运行时也可能会出一些莫名 ...
- 万恶之源:C语言中的隐式函数声明
1 什么是C语言的隐式函数声明 在C语言中,函数在调用前不一定非要声明.如果没有声明,那么编译器会自己主动依照一种隐式声明的规则,为调用函数的C代码产生汇编代码.以下是一个样例: int main(i ...
- Js 中那些 隐式转换
曾经看到过这样一个代码: (!(~+[])+{})[--[~+""][+[]]*[~+[]]+~~!+[]]+({}+[])[[~!+[]*~+[]]] = sb , 你敢相信, ...
- js中的隐式转换
js中的不同的数据类型之间的比较转换规则如下: 1. 对象和布尔值比较 对象和布尔值进行比较时,对象先转换为字符串,然后再转换为数字,布尔值直接转换为数字 [] == true; //false [] ...
- Scala 中的隐式转换和隐式参数
隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义. 举例: 正常情况下"120"/12显然会报错,因为 String 类并没有实现 / 这个方法,我们无法去决定 Stri ...
- javascript中的隐式类型转化
javascript中的隐式类型转化 #隐式转换 ## "+" 字符串和数字 如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+将进行拼接操作. 如果其中一个操作数是对 ...
- C#中的隐式转换
你是否考虑过这个问题:为什么不同类型之间的变量可以赋值,而不需要强制转换类型?如: int i = 1; long l = i; object obj = 1; Exception exception ...
随机推荐
- Codeforces 1009D
题意略. 思路: 可知对于一个拥有n个点的图来说,它至少需要有n - 1条边来维持连通性,而且数字1恰好与后面的n - 1个数字互质: 至于n个点的图可以产生合法的互质边的个数的上限,我们可以通过莫比 ...
- Codeforces 814C
题意略. 思路: 尺取法,依然是要利用之前的结果. 感觉时间复杂度太高了,竟然也过了. #include<bits/stdc++.h> using namespace std; ; ]; ...
- 百度地图小Demo---获取当前地址以及拖拽显示地址
1.效果图 2.源码 主要使用百度地图的JavaScript API文件,以及一个JQuery文件. <!doctype html> <html lang="en" ...
- C# winform语音提示
1.SpeechSynthesizer文字转音频 项目添加引用:System.Speech using(SpeechSynthesizer speech = new SpeechSynthesizer ...
- 手把手教你用深度学习做物体检测(五):YOLOv1介绍
"之前写物体检测系列文章的时候说过,关于YOLO算法,会在后续的文章中介绍,然而,由于YOLO历经3个版本,其论文也有3篇,想全面的讲述清楚还是太难了,本周终于能够抽出时间写一些YOLO算法 ...
- ImportError: DLL load failed: %1 不是有效的 Win32 应用程序。
报错 Traceback (most recent call last): File "D:/PyCharm 5.0.3/WorkSpace/2.NLP/2.获取数据源和规范化/4.word ...
- 什么是Scrum?
转自:http://www.scrumcn.com/agile/scrum-knowledge-library/scrum.html SCRUM 是一个用于开发和维护复杂产品的框架 Scrum 是一个 ...
- codeforce303C-Minimum Modular-剪枝,暴力
Minimum Modular 题意:就是在一堆数字中,每一个数字对m取模不能等于这堆数字中的其他数字,同时给了K个机会可以删除一些数字.求最小的m: 思路:我一开始完全没思路,队长说的并查集什么的不 ...
- codeforces 816 E. Karen and Supermarket(树形dp)
题目链接:http://codeforces.com/contest/816/problem/E 题意:有n件商品,每件有价格ci,优惠券di,对于i>=2,使用di的条件为:xi的优惠券需要被 ...
- jQuery - 01. jQuery特点、如何使用jQuery、jQuery入口函数、jQuery和DOM对象的区别、jQuery选择器、
this指的是原生js的DOM对象 .css(“”):只写一个值是取值,写俩值是赋值 window.onload === $(document).ready(); $(“”):获取元素 标 ...