序
毫无疑问,JavaScript是一种解释性语言,但它在运行时是否只从上到下解析?
事实上,一些现象证明事实并非如此。根据《JavaScript权威指南》和互联网上的相关信息,JavaScript存在“预解析”行为。了解这个特性非常重要,否则在实际开发中可能会遇到很多无法解决的问题,甚至会导致程序bug。为了分析这一现象,并作为我自己研究的总结,本文逐渐带领大家认识JavaScript的“预解析”。如果我的观点是错误的,我还是希望改正。
在ES6之前,变量由var声明,并且会有变量的预解析(和函数的预解析)。我相信很多学生在刚开始学习JavaScript的时候,完全被预解析搞糊涂了。虽然在ES6中引入了let和const,但是现阶段ES6还没有完全普及,很多比较老的代码还是按照ES5标准甚至ES3标准编写的。
首先,在内存中显示变量和函数
像其他语言一样,JavaScript中的变量类型有基本数据类型和引用数据类型。基本数据类型包括:未定义、空、布尔、字符串、数字;引用数据类型主要是对象(包括{}、[]、/$/、日期、函数等)。).
var num=24var obj={name:'iceman ',age :24 };func(){ console . log(' hello world ');}当浏览器加载html页面时,首先会提供一个全局JavaScript代码执行的环境,称为全局作用域。
基本数据类型按值操作,引用数据类型按地址操作。
根据上述原理,上述代码在内存中的模型是:
记忆模型。巴布亚新几内亚
基本类型直接存储在堆栈内存中,而对象存储在堆内存中,变量只保存对象的地址。因此,obj保存对象的地址oxff44,func保存地址oxff66。
根据上述代码执行:
console . log(func);console . log(func());第一行输出整个函数的定义部分(函数本身):
第一行代码输出结果。
如上所述,func存储一个指向堆内存的地址,堆内存保留函数的定义。
第二行输出func函数的返回结果:
第二行代码输出结果. png。
func函数没有返回值,所以输出未定义。
注意:函数的返回值是返回后写的任何东西。如果没有返回,默认返回值是未定义的。
二、预分析
通过对上述记忆模型的理解,我们可以更好地理解预解析的机制。所谓预解析,就是在当前范围内,在执行JavaScript代码之前,默认情况下浏览器会先用var和函数声明提前声明或定义所有变量。
2.1.声明和定义
var num=24这段简单的代码实际上是两个步骤:声明和定义。
免责声明:var num告诉浏览器在全局范围内有一个num变量。如果一个变量只被声明而没有被赋值,那么默认值是未定义的。定义:num=12定义就是给变量赋值。2.2.解析前由var声明的变量和由function声明的函数之间的区别
var声明的变量不同于预解析中函数声明的函数。var声明的变量只是提前声明,而function声明的函数是提前声明,同时定义的。也就是说,var声明的变量和function声明的函数的区别在于是否在声明的同时定义。
2.3.预解析仅在当前范围内发生
在程序开始时,只有window下的变量和函数是预解析的,只有当函数被执行时,函数中的变量才会被预解析。
console . log(num);var num=24console . log(num);func(100,200);函数func(num1,num 2){ var total=num 1 num 2;console.log(总计);}
输出结果。巴布亚新几内亚
第一次输出num时,因为预解析,只声明为undefined,所以会输出undefined;当num第二次输出时,它已经被定义,所以输出24。
由于函数的声明和定义是同时进行的,func()在func函数定义声明之前被调用,但仍然可以正常调用,正常输出300。
记忆模型。巴布亚新几内亚
三.范围链
首先了解以下三个概念:
函数内部的作用域成为私有作用域,窗口所在的作用域称为全局作用域。在全局范围下声明的变量是全局变量;“在私有范围内声明的变量”和“函数的形式参数”是私有变量;在私有范围内,当执行代码时,会遇到一个变量。首先,需要确定它是否是私有变量。如果是私有变量,则与外部任何事物无关。如果不是私有,则在当前范围的上级范围内搜索。如果没有上级范围,会一直搜索到窗口。这是范围链。
函数执行时,首先会形成一个新的私有作用域,然后执行如下:
如果有有形的参数,先给形式参数赋值;私有范围内的预解析;私有范围内的代码自上而下执行函数,形成新的私有范围,保护内部私有变量不受外部干扰(私有不能在外部修改,私有不能在外部修改),这就是闭包的概念。
console.log(总计);var total=0;func func(num 1,num 2){ console . log(total);var total=num1 num2console.log(总计);}func(100,200);console.log(总计);执行上述代码时,第一次输出total时会输出undefined(因为预解析)。当func(100,200)被执行时,函数体中的内容将被执行。此时,func函数将形成一个新的私有范围。根据上述步骤:
首先给形式参数num1和num2赋值,分别为100和200;在func中预解析代码;因为func中的代码是在func函数中预解析的,所以func函数中的总变量将被预解析。函数中第一次输出total时,输出undefined,然后分配total,第二次输出total时,输出300。因为函数体中有var声明的变量total,所以函数体中的输出total不是全局范围内的total。
最后一次输出total时,输出0,这里输出全局范围内的total。
console.log(总计);var total=0;func func(num 1,num 2){ console . log(total);total=num1 num2console.log(总计);}func(100,200);console.log(总计);代码稍微变形后,func函数中的total没有使用var声明,所以total不是私有的,所以它会在全局范围内寻找total,也就是说,这里出现的所有total实际上都在全局范围之下。
第四,全球范围内有var和无var的区别
在全局范围内用var声明变量是可以预解析的,所以赋值前执行不会报错;当声明一个没有var的变量时,它不能被预解析,所以如果它在赋值之前被执行,它将报告一个错误。
console . log(num 1);var num1=12console . log(num 2);num2=12
输出结果。巴布亚新几内亚
num2=12相当于给window添加一个num2属性名,属性值为12;
var num1=12相当于给全局范围增加了一个全局变量num1,但不仅如此,还相当于给窗口增加了一个属性名num,属性值为12;
问题:如果一个变量出现在私有范围内,如果不是私有的,就在上级范围内查找。如果上级没有,继续向上看,直到找到窗口。没有窗户怎么办?
获取值:console.log(总计);-错误报告不明参考错误:未定义总数
设定值:总计=100;-相当于向窗口添加一个属性名total,属性值为100
function fn(){//console . log(total);//未捕获的引用错误:未定义总计总计=100;} fn();console.log(总计);注意:在JS中,如果上面的代码在没有任何特殊处理的情况下报告错误,下面的代码将不再执行
动词(verb的缩写)预分析中的一些异常机制
5.1无论条件是否成立,有var的都要提前申报
if(!(“window”中的“num”){ var num=12;} console . log(num);//undefinedJavaScript在预解析时忽略所有if条件,因为在ES6之前没有块级作用域的概念。在这个例子中,num将首先被预解析,预解析将把这个变量作为window的一个属性添加到window中。然后window中的' num '返回true,如果是否定的,那么代码执行不会进入if块,num也不会被falsesigned,最后console.log(num)将作为未定义输出。
5.2只有“=”左侧是预解析,右侧表示不参与预解析
fn();//-undefined();//Uncated TypeError : fn不是function var fn=function(){ console . log(' ok ');} fn();- 'ok '函数fn(){ console . log(' ok ');} fn();-'ok '建议:尝试使用var fn=.声明变量时。
5.3自执行功能:定义和执行一起完成
(function(num){ console . log(num);})(100);自治函数定义的函数在全局范围内没有预解析。当代码执行到这个位置时,定义和执行一起完成。
补充:定义自执行函数的其他方法
~ function(num){ }(100)function(num){ }(100)-function(num){ }(100)!函数(num) {}(100)5.4 return下的代码仍将被预解析
函数fn(){ console . log(num);//-未定义的返回函数(){ };var num=100} fn();虽然函数体中的return下面的代码不再被执行,但是它需要被预解析。返回的代码是我们的返回值,所以它不是预解析的。
5.5名称已经申报,不需要重新申报,需要重新分配
var fn=13函数fn(){ console . log(' ok ');} fn();//unsighttypeerror : fn不是函数经典主题
fn();//- 2函数fn(){ console . log(1);} fn();//-2 var fn=10;//-fn=10 fn();//-10()-Uncated TypeError : fn不是函数function fn(){ console . log(2);} fn();摘要
以上就是JavaScript中的预解析。希望本文的内容能给你的学习或工作带来一些帮助。有问题可以留言交流。