当前位置:网站首页>JS的解析与执行过程
JS的解析与执行过程
2022-04-23 05:54:00 【迷失的骆驼】
转载自:https://www.cnblogs.com/foodoir/p/5977950.html
全局中的解析和执行过程
预处理:创建一个词法环境(LexicalEnvironment,在后面简写为LE),扫描JS中的用声明的方式声明的函数,用var定义的变量并将它们加到预处理阶段的词法环境中去。
一、全局环境中如何理解预处理
比如说下面的这段代码:
var a = 1;//用var定义的变量,以赋值
var b;//用var定义的变量,未赋值
c = 3;//未定义,直接赋值
function d(){//用声明的方式声明的函数
console.log('hello');
}
var e = function(){//函数表达式
console.log('world');
}
在预处理时它创建的词法作用域可以这样表示:
LE{ //此时的LE相当于window
a:undefined
b:undefined
没有c
d:对函数的一个引用
没有e
}
强调:1、预处理的函数必须是JS中用声明的方式声明的函数(不是函数表达式),看例子:
d();
e();
function d(){//用声明的方式声明的函数
console.log('hello');
}
var e = function(){//函数表达式
console.log('world');
}
输出结果分别是:hello;报错e is not a function
2、是用var定义的变量,看例子:
console.log(a);//undefined
console.log(b);//undefined
console.log(c);//报错:c is not defined
var a = 1;
var b;
c = 3;
二、命名冲突的处理
来看下面的代码:你觉得输出结果是什么?
console.log(f);
var f = 1;
function f(){
console.log('foodoir');
}
console.log(f);
function f(){
console.log('foodoir');
}
var f = 1;
console.log(f);
var f = 1;
var f = 2;
console.log(f);
function f(){
console.log('foodoir');
}
function f(){
console.log('hello world');
}
你可能跟我开始一样,觉得输出的是foodoir,这样你就错了,你应该继续看看前面讲的预处理的问题。第一段代码输出的结果应该是:function f(){console.log('foodoir');}。
看到第二段代码,你可能想都没想就回答结果是1,并且你还告诉原因说javascript里面的函数没有传统意义的重载。是的javascript里面的函数是没有重载,但是第二段代码的运行结果仍然是:function f(){console.log('foodoir');}。(原因后面作解释)
如果你还觉得第三段代码的结果是2或者是1,那么我建议你回到前面看看关于预处理的例子。第三段的结果为:undefined。
第四段代码的结果为function f(){console.log('hello world');}
原因:处理函数声明有冲突时,会覆盖;处理变量声明有冲突时,会忽略。在既有函数声明又有变量声明的时候,你可以跟我一样像CSS中的权重那样去理解,函数声明的权重总是高一些,所以最终结果往往是指向函数声明的引用。
三、全局函数的执行
来看下面的例子:
1 console.log(a);
2 console.log(b);
3 console.log(c);
4 console.log(d);
5 var a = 1;
6 b = 2;
7 console.log(b);
8 function c(){
9 console.log('c');
10 }
11
12 var d = function(){
13 console.log('d');
14 }
15 console.log(d);
1、我们先分析全局预处理的情况,结果如下:
LE{
a:undefined
没有b
c:对函数的一个引用
d:undefined
}
此时,我们可以得到前四行代码得到的结果分别为:
undefined
报错
function c(){console.log('c');
undefined
2、当执行完预处理后,代码开始一步步被解析(将第二行报错的代码注释掉)
在第6行代码执行完,LE中的a的值变为1;
LE{
a:1
没有b
c:对函数的一个引用
d:undefined
}
第7行代码执行完,LE中就有了b的值(且b的值为2,此时b的值直接变为全局变量);
LE{
a:1
b:2
c:对函数的一个引用
d:undefined
}
第10行代码执行完,
LE{
a:1
b:2
c:指向函数
d:undefined
}
第14行代码执行完,此时
LE{
a:1
b:2
c:指向函数
d:指向函数
}
关于b变为全局变量的例子,我们在控制台中输入window.b,可以得到b的结果为2。
结果如图:
补充:运用词法的作用域,我们可以很好的解释一个带多个参数的函数只传递一个参数的例子。
function f(a,b){
}
f(1);
它的词法作用域可以这样解释:
LE{
a:1
b:undefined
}
函数中的解析和执行过程
函数中的解析和执行过程的区别不是很大,但是函数中有个arguments我们需要注意一下,我们来看下面的例子:
function f(a,b){
alert(a);
alert(b);
var b = 100;
function a(){}
}
f(1,2);
我们先来分析函数的预处理,它和全局的预处理类似,它的词法结构如下:
LE{
b:2
a:指向函数的引用
arguments:2
}
//arguments,调用函数时实际调用的参数个数
再结合之前的那句话:处理函数声明有冲突时,会覆盖;处理变量声明时有冲突,会忽略。
故结果分别为:function a(){}和2
当传入的参数值有一个时:
function f(a,b){
alert(a);
alert(b);
var b = 100;
function a(){}
}
f(1);
这个时候的词法结构如下:
LE{
b:undefined
a:对函数的一个引用
arguments:1
}
故结果分别为:function a(){}和undefined
还有一个需要注意的地方有:如果没有用var声明的变量,会变成最外部LE的成员,即全局变量
function a(){
function b(){
g = 12;
}
b();
}
a();
console.log(g);//12
控制台结果:
有了前面的基础,我们就可以对JS的作用域和作用域链进行深入的了解了。
关于JS作用域和作用域链
console.log(a);//undefined
console.log(b);//undefined
console.log(c);//c is not defined
console.log(d);//d is not defined
var a = 1;
if(false){
var b = 2;
}else{
c = 3;
}
function f(){
var d = 4;
}
有了前面的基础我们很容易就可以得到前三个的结果,但是对于第四个却很是有疑问,这个时候,你就有必要看一看关于javascript作用域的相关知识了。
在编程语言中,作用域一般可以分为四类:块级作用域、函数作用域、动态作用域、词法作用域(也称静态作用域)
块级作用域
在其它C类语言中,用大括号括起来的部分被称为作用域,但是在javascript并没有块级作用域,来看下面一个例子:
for(var i=0;i<3;i++){
//
}
console.log(i);
它的结果为3,原因:执行完for循环后,此时的i的值为3,在后面仍有效
函数作用域
没有纯粹的函数的作用域
动态作用域
来看下面的例子:
function f(){
alert(x);
}
function f1(){
var x = 1;
f();
}
function f2(){
var x = 1;
f();
}
f1();
f2();
如果说存在动态作用域,那么结果应该是分别为1、2,但是最终结果并不是我们想要的,它的结果为:x is not defined。所以javascript也没有动态作用域
词法作用域(也称静态作用域)
我们可以在函数最前面声明一个x=100
var x=100;
function f(){
alert(x);
}
function f1(){
var x = 1;
f();
}
function f2(){
var x = 1;
f();
}
f1();
f2();
结果为分别弹出两次100。说明javascript的作用域为静态作用域 ,分析:
function f(){
alert(x);
}
// f [[scope]] == LE == window
//创建一个作用域对象f [[scope]],它等于创建它时候的词法环境LE(据前面的知识我们又可以知道此时的词法环境等于window)
function f1(){
var x = 1;
f();//真正执行的时候(一步一步往上找)LE ->f.[[scope]] == window
}
在词法解析阶段,就已经确定了相关的作用域。作用域还会形成一个相关的链条,我们称之为作用域链。来看下面的例子:
function f(){
alert(x);
}
// f [[scope]] == LE == window
//创建一个作用域对象f [[scope]],它等于创建它时候的词法环境LE(据前面的知识我们又可以知道此时的词法环境等于window)
function f1(){
var x = 1;
f();//真正执行的时候(一步一步往上找)LE ->f.[[scope]] == window
}
最终结果为:100
来看一个经典的例子:
//定义全局变量color,对于全局都适用,即在任何地方都可以使用全局变量color
var color = "red";
function changeColor(){
//在changeColor()函数内部定义局部变量anotherColor,只在函数changeColor()里面有效
var anotherColor = "blue";
function swapColor(){
//在swapColor()函数内部定义局部变量tempColor,只在函数swapColor()里面有效
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
//这里可以访问color、anotherColor和tempColor
console.log(color); //blue
console.log(anotherColor); //red
console.log(tempColor); //blue
}
swapColor();
//这里只能访问color,不能访问anotherColor、tempColor
console.log(color); //blue
console.log(anotherColor); //anotherColor is not defined
console.log(tempColor); //tempColor is not defined
}
changeColor();
//这里只能访问color
console.log(color); //blue
console.log(anotherColor); //anotherColor is not defined
console.log(tempColor); //tempColor is not defined
还需要注意的是:new Function的情况又不一样
var x= 123;
function f(){
var x = 100;
//g.[[scope]] == window
var g = new Function("","alert(x)");
g();
}
f();
//结果为:123
小结:
以f1{ f2{ x}}为例,想得到x,首先会在函数里面的词法环境里面去找,还没找到去父级函数的词法环境里面去找……一直到window对象里面去找。
这时候,问题来了。。。。
问题1:到这里看来如果有多个函数都想要一个变量,每次都要写一个好麻烦啊,我们有什么方法可以偷懒没?
方法:将变量设置为全局变量
问题2:不是说要减少全局用量的使用么?因为在我们做大项目的时候难免要引入多个JS库,变量间的命名可能会有冲突,且出错后不易查找,这个时候我们该怎么办呢?
方法:将变量设置在一个打的function中,比如下面这样:
function(){
var a = 1;
var b = 2;
function f(){
alert(a);
}
}
问题3:照你的这种方法我们在外面又访问不到了,怎么办?
方法:我们使用匿名函数的方法,示例如下:
(function(){
var a = 1,
b = 2;
function f(){
alert(a);
}
window.f = f;
})();
f();
//结果为:1
版权声明
本文为[迷失的骆驼]所创,转载请带上原文链接,感谢
https://blog.csdn.net/xufang461010923/article/details/94354951
边栏推荐
- Notes on advanced points of C language 2
- Notes on advanced points of C language 4
- js根据名字将数组对象中名字相同的项组成一个相同的数组
- [learn] HF net training
- 汇编 32位无符号加法计算器
- 死区时间的分析与设置
- 统计字符串中每个字符出现的次数
- 深蓝学院激光slam理论与实践 -第二章(里程计标定)作业
- 基于VGG对五种类别图片的迁移学习
- [UDS unified diagnosis service] i. diagnosis overview (2) - main diagnosis protocols (K-line and can)
猜你喜欢
MOS管特性和导通过程
Analysis and setting of dead time
CUDA环境安装
死区时间的分析与设置
Detailed explanation and application of PN junction and diode principle
锚点定位——如何设置锚点居页面顶部距离,锚点定位并距离顶部一定偏移
Error in created hook: “ReferenceError: “Promise”未定义“
Introduction to nonparametric camera distortion model
在visual stdio中运行qt程序
[UDS unified diagnosis service] i. diagnosis overview (2) - main diagnosis protocols (K-line and can)
随机推荐
Detailed explanation and application of PN junction and diode principle
ROS包nmea_navsat_driver读取GPS、北斗定位信息笔记
HDU-Memory Control
Makefile基础、常用函数及通用Makefile
[opencv] use filestorage to read and write eigenvectors
[stepping on the pit] MELD in win11 wsl2 cannot be used normally. Problem repair
[UDS unified diagnosis service] i. diagnosis overview (2) - main diagnosis protocols (K-line and can)
Makefile foundation, common functions and general makefile
死区时间的分析与设置
[UDS unified diagnostic service] IV. typical diagnostic service (5) - function / component test function unit (routine function unit 0x31)
Sdoi2009-hh Necklace
查漏补缺(四)
查漏补缺(六)
C语言 #和##的使用
微信小程序之改变数组中某值,对象中某值的方法
sqlite编译
The difference between single quotation mark, double quotation mark and back quotation mark in shell script
Node的数据库编程
QT add qserialport class to realize serial port operation
JS中 t, _ => 的解析