为什么说JavaScript预解释是一种毫无节操的机制详析


Posted in Javascript onNovember 18, 2018

前言

JavaScript是一门解释型的语言 , 想要运行JavaScript代码需要两个阶段

  • 编译阶段: 编译阶段就是我们常说的JavaScript预解释(预处理)阶段,在这个阶段JavaScript解释器将完成把JavaScript脚本代码转换到字节码
  • 执行阶段: 在编译阶段JavaScript解释器借助执行环境把字节码生成机械码,并从上到下按顺序执行

本文就重点介绍预解释,框架图如下:

为什么说JavaScript预解释是一种毫无节操的机制详析

一、什么是预解释

在js中,带var 和function关键字的需要预解释:

那什么是预解释?就是在js代码执行之前,先申明好带有var 关键字和带有function关键字的变量,在内存里先安排好。

预解释:JavaScript代码执行之前,浏览器首先会默认的把所有带var和function的进行提前的声明或者定义

1.理解声明和定义

声明(declare):如var num;=>告诉浏览器在全局作用域中有一个num的变量了;如果一个变量只是声明了但是没有赋值,默认的值是undefined

定义(defined):如num=12;=>给我们的变量进行赋值。

2.对于带var和function关键字的在预解释的时候操作不一样的

var =>在预解释的时候只是提前的声明

function =>在预解释的时候提前的声明+定义都完成了

3.预解释只发生在当前的作用域下。

例如:开始只对window下的进行预解释,只有函数执行的时候才会对函数中的进行预解释

二、作用域链

1.如何区分私有变量和全局变量?

1)在全局作用域下声明(预解释的时候)的变量是全局变量

2)只有函数执行会产生私有的作用域,比如for(){}、if(){}和switch(){}都不会产生私有作用域

3)在"私有作用域中声明的变量(var 声明)"和"函数的形参"都是私有的变量。在私有作用域中,代码执行的时保遇到了一个变量,首先我们需要确定它是否为私有的变量,如果是私有的变量,那么和外面的没有在何的关系;如果不是私有的,则往当前作用域的上级作用域进行查找,如果上级作用域也没有则继续查找,一直找到window为止,这就是作用域链。

我们举个例子来区别私有变量和全局变量:

//=>变量提升:var a;var b;var c;test=AAAFFF111;
var a=10,b=11,c=12;
function test(a){
//=>私有作用域:a=10 var b;
a=1;//=>私有变量a=1
var b=2;//=>私有变量b=2
c=3;//=>全局变量c=3
}
test(10);
console.log(a);//10
console.log(b);//11
console.log(c);//3

判断是否是私有变量一个标准就是是否是在函数中var声明的变量和函数的形参都是私有的变量。本道题目在test函数中a是形参和var b定义的变量b都是私有变量。

2.函数传参

这是因为当函数执行的时候,首先会形成一个新的私有的作用域,然后按照如下的步骤执行:

1)如果有形参,先给形参赋值

2)进行私有作用域中的预解释

3)私有作用域中的代码从上到下执行

我们来看一道例题

var total=0;
function fn(num1,num2){
console.log(total);//->undefined 外面修改不了私有的
var total=num1 +num2;
console.log(total);//->300
}
fn(100,200);
console.log(total);//->0 私有的也修改不了外面的

3.JS中内存的分类

栈内存:用来提供一个供JS代码执行的环境,即作用域(全局作用域/私有的作用域)

堆内存:用来存储引用数据类型的值。对象存储的是属性名和属性值,函数存储的是代码字符串。

三、全局作用域下带var和不带var的区别

我们先来看以下两个例子:

//例题1
console.log(num);//->undefined
var num=12;
//例题2
console.log(num2);//->Uncaught ReferenceError:num2 is not defined 
num2=12;//不能预解释

当你看到var num=12时,可能会认为只是个声明。但JavaScript实际上会将其看成两条声明语句:var num;num=12;第一个定义声明是在预解释阶段进行的。第二个赋值声明会被留在原地等待执行阶段。num2=12 相当于给window增加了一个叫做num2的属性名,属性值是12;而var num=12 首先它相当于给全局作用域增加了一个全局变量num,它也相当于给window增加了一个属性名num2,属性值是12。两者最大区别:带var的可以进行预解释,所以在赋值的前面执行不会报错;不带var的是不能进行预解释的,在前面执行会报错;

接下来我们举例说明:

//例题1
var total=0;
function fn(){
console.log(total);//undefined
var total=100;
}
fn();
console.log(total);//0
//例题2
var total=0;
function fn(){
console.log(total);//0
total=100;
}
fn();
console.log(total);//100

例题1中带var变量在私有作用域中可以预解释,所以第一个console打出来的值为undefined。私有作用域中出现的一个变量不是私有的,则往上级作用域进行查找,上级没有则继续向上查找,一直找到window为止,例题2中不带var变量不是私有的,所以往上级找

四、预解释五大毫无节操的表现

1.预解释的时候不管你的条件是否成立,都要把带var的进行提前的声明。

请看下面这道例题:

if(!("num" in window)){
var num=12;//这句话会被提到大括号之外的全局作用域:var num;->window.num; 
}
console.log(num);//undefined

2.预解释的时候只预解释”=”左边的,右边的值,不参与预解释

请看下面这道例题:

fn();//报错
var fn=function (){ //window下的预解释:var fn;
console.log("ok");
};

3.自执行函数:定义和执行一起完成了。

自执行函数定义的那个function在全局作用域下不进行预解释,当代码执行到这个位置的时候定义和执行一起完成了。常见有以下几种形式:

(function(num){})(10);
~function(num){}(10);
+function(num){}(10);
-function(num){}(10);
!function(num){}(10);

4.函数体中return下面的代码虽然不再执行了,但是需要进行预解释;return后面跟着的都是我们返回的值,所以不进行预解释;

function fn(){
//预解释:var num;
console.log(num);//->undefined
return function(){};
var num=100;
}

5.函数声明和变量声明都会被提升。但是一个值得注意的细节(这个细节可以出现在有多个“重复”声明的代码中)是函数会首先被提升,然后才是变量。在预解释的时候,如果名字已经声明过了,不需要从新的声明,但是需要重新的赋值;
我们先来看下两个简单的例子:

//例题1
 function a() {}
 var a
 console.log(typeof a)//'function'
//例题2
 var c = 1
 function c(c) {
 console.log(c)
 var c = 3
 }
 c(2)//Uncaught TypeError: c is not a function

当遇到存在函数声明和变量声明都会被提升的情况,函数声明优先级比较高,最后变量声明会被函数声明所覆盖,但是可以重新赋值,所以上个例子可以等价为

function c(c) {
 console.log(c)
 var c = 3
 }
 c = 1
 c(2)

接下来我们看下两道比较复杂的题目:

//例题3
fn();
function fn(){console.log(1);};
fn();
var fn=10;
fn();
function fn(){console.log(2);};
fn();

1.一开始预解释,函数声明和赋值一起来,fn 就是function fn(){console.log(1);};遇到var fn=10;不会重新再声明,但是遇到function fn(){console.log(2);}就会从重新赋值,所以一开始fn()的值就是2

2.再执行fn();值不变还是2

3.fn重新赋值为10,所以运行fn()时报错,接下去的语句就没再执行。

//例题4
alert(a);
a();
var a=3;
function a(){
alert(10)
}
alert(a);
a=6;
a()

1.函数声明优先于变量声明,预解释时候,函数声明和赋值一起来,a就是function a(){alert(10)} ,后面遇到var a=3,也无需再重复声明,所以先弹出function a(){alert(10)}

2.a() ,执行函数,然后弹出10

3.接着执行了var a=3; 所以alert(a)就是显示3

4.由于a不是一个函数了,所以往下在执行到a()的时候, 报错。

参考文章

  • JavaScript中的预解析
  • http://heartwalker.cc/2016/04/04/js作用域其二-预解析/

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JavaScript 变量命名规则
Sep 23 Javascript
JavaScript eval() 函数介绍及应用示例
Jul 29 Javascript
html的DOM中document对象images集合用法实例
Jan 21 Javascript
javaScript基础语法介绍
Feb 28 Javascript
jquery html5 视频播放控制代码
Nov 06 Javascript
JS重载实现方法分析
Dec 16 Javascript
AngularJS动态绑定ng-options的ng-model实例代码
Jun 21 Javascript
使用vue + less 实现简单换肤功能的示例
Feb 21 Javascript
基于cropper.js封装vue实现在线图片裁剪组件功能
Mar 01 Javascript
JavaScript求一组数的最小公倍数和最大公约数常用算法详解【面向对象,回归迭代和循环】
May 07 Javascript
Vue使用Proxy监听所有接口状态的方法实现
Jun 07 Javascript
JavaScript实现栈结构Stack过程详解
Mar 07 Javascript
利用原生的JavaScript实现简单拼图游戏
Nov 18 #Javascript
Javascript实现动态时钟效果
Nov 17 #Javascript
使用Javascript简单计算器
Nov 17 #Javascript
JS实现图片切换效果
Nov 17 #Javascript
js实现导航跟随效果
Nov 17 #Javascript
JavaScript实现新年倒计时效果
Nov 17 #Javascript
JS实现倒计时图文效果
Nov 17 #Javascript
You might like
Terran剧情介绍
2020/03/14 星际争霸
Oracle 常见问题解答
2006/10/09 PHP
谈谈PHP语法(3)
2006/10/09 PHP
Yii2框架引用bootstrap中日期插件yii2-date-picker的方法
2016/01/09 PHP
PHP7匿名类的用法示例
2019/04/05 PHP
JavaScript Event学习第三章 早期的事件处理程序
2010/02/07 Javascript
轻量级 JS ToolTip提示效果
2010/07/20 Javascript
jQuery 操作option的实现代码
2011/03/03 Javascript
用jquery写的一个万年历(自写)
2014/01/20 Javascript
直接在JS里创建JSON数据然后遍历使用
2014/07/25 Javascript
NodeJS Web应用监听sock文件实例
2015/02/18 NodeJs
js+css实现回到顶部按钮(back to top)
2016/03/02 Javascript
vue脚手架vue-cli的学习使用教程
2017/06/06 Javascript
AngularJS实现表单元素值绑定操作示例
2017/10/11 Javascript
JS抛物线动画实例制作
2018/02/24 Javascript
Python实现在线程里运行scrapy的方法
2015/04/07 Python
浅谈Python 中整型对象的存储问题
2016/05/16 Python
使用Python & Flask 实现RESTful Web API的实例
2017/09/19 Python
Python 共享变量加锁、释放详解
2019/08/28 Python
解决pyecharts运行后产生的html文件用浏览器打开空白
2020/03/11 Python
Python类的动态绑定实现原理
2020/03/21 Python
pycharm如何设置官方中文(如何汉化)
2020/12/29 Python
美的官方商城:Midea
2016/09/14 全球购物
男女时尚与复古风格在线购物:RoseGal(全球免费送货)
2017/07/19 全球购物
PHP面试题及答案一
2012/06/18 面试题
C语言编程题
2015/03/09 面试题
新闻学专业应届生求职信
2013/11/08 职场文书
学习十八大坚定理想信念心得体会
2014/03/11 职场文书
公务员平时考核实施方案
2014/03/11 职场文书
餐厅筹备计划书
2014/04/25 职场文书
2015教师见习期工作总结
2014/12/12 职场文书
2015年宣传工作总结
2015/04/08 职场文书
南极大冒险观后感
2015/06/05 职场文书
创业计划书之儿童理发店
2019/09/27 职场文书
使用react+redux实现计数器功能及遇到问题
2021/06/02 Javascript
java实现面板之间切换功能
2022/06/10 Java/Android