详解JS预解析原理


Posted in Javascript onJune 16, 2020

预解析的的不同机制

预解析也叫预声明,是提前解析声明的意思;预解析是针对变量和函数来说的;但是变量和function的的预解析是两套不同的机制;

  • 当浏览器加载我们的HTML页面的时候,首先会提供一个供JS代码执行的环境->全局作用域global(浏览器中的全局作用域,也叫顶级作用域是window)
  • JS中的内存空间分为两种:栈内存、堆内存
  1. 栈内存;提供JS代码执行的环境,存储基本数据类型的值;->全局作用域或者私有的作用域其实都是栈内存;
  2. 堆内存;存储引用数据类型的值(对象是把属性名和属性值储存进去,函数是把函数体内的代码当做字符串储存进去)
  • 在当前的作用域中,JS代码执行之前,浏览器首先会默认的把所有代var和function的进行提前的声明或者定义,->“预解析”(也叫变量提升)

var的预解析机制

var a=1

  • 1、代码运行之前,先扫描有没有带var关键字的变量名,有的话,为这个变量名在内存里开一个空间;这时候变量名a是不代表任何值的;用undefined来表示;undefined是一个标识符/记号,表示找不到这个变量名所代表的数据;不存在的意思;这个阶段叫变量的声明;
  • 2、当代码运行的时候,则给数据1开辟一个内存空间;
  • 3、让数据1和变量名a绑定在一起;变量类型指的就是数据类型;按照js语言的原理来说变量类型有undefined类型;但是数据类型是没有undefined这种数据类型的;只有”undecided”这种字符串类型(字符串类型是数据类型的一种);同理也没有unll这个数据类型,但是有”null”这种字符串类型;
var num;
//1、声明(declare):var num; ->告诉浏览器在当前作用域中有一个num的变量了,如果一个变量只是声明了但是没有赋值,默认的值是undefined
console.log(num);//->undefined
num = 12;
//2、定义(defined):num=12; ->给我们的变量进行赋值
console.log(num);//->12

//变量提前使用的话,就是undefined
console.log(testStr);//undefined
var testStr="22222222"

function 关键字的预解析步骤

function fn(){……}

在代码执行之前,把所有的带function关键字的脚本都扫描一遍,然后定义变量;并且同时给变量赋值;

  • 1、函数的定义只是保存一些字符串;预解析的时候在内存里保存fn大括号里面的字符串;
  • 2、代码运行时候,读到fn()时候,这个时候就是函数的运行;函数的运行,会先开辟一个堆内存把字符串当做代码在堆内存中再次运行,函数产生的作用域内还会再进行预解析和代码运行;

函数如果多次执行;会产生多个作用域;但是产生的多个作用域里面的内容都是相互独立的;互相没有关系;(在原型和原型链时候再仔细研究原理;)

fn(100,200);//->可以在上面执行,因为预解释的时候声明+定义就已经完成了 
function fn(num1, num2) { 
	var total = num1 + num2; 
	console.log(total);
}

总结

1、var和function关键字的在预解析的时候操作还是不一样的

  • var -> 在预解析的时候只是提前的声明了这个变量,只有当代码执行的时候才会完成赋值操作
  • function -> 在预解析的时候会提前的把声明加定义都完成了(在代码执行的时候遇到定义的代码直接的跳过)

2、预解析只发生在当前的作用域下,例如:开始只对window下的进行预解析,只有函数执行的时候才会对函数中的进行预解析;

[重要]刚开始只对window下的进行预解析,fn函数中目前存储的都是字符串,所以var total没啥实际的意义,所以不进行预解析-> “预解析是发生在当前作用域下的”

综合题;

console.log(obj);//->undefined
	var obj = {name: "xie", age: 25};
 function fn(num1, num2) {//代码执行到这一行的时候直接的跳过这一块的代码,因为在预解释的时候我们已经完成了声明加定义
 var total = num1 + num2;
 console.log(total);
 }
 var num1 = 12;
 fn(num1, 100);//执行fn,把全局变量num1的值赋值给形参num1,把100赋值给形参num2

下面是一个预解析思路

var a,
 b = 0,
 fn = function () {
 var a = b = 2;
 };
fn();
console.log(a, b);

把上面解析成下面就好理解了

var a;
window.b = 0;
window.fn = function () {
 //var a = b = 2;
 var a = 2;//a是私有的和全局没关系
 b = 2;//b是全局的
};
fn();//window.fn()
console.log(a, b);//undefined 2

预解析机制

  • 1、不管条件是否成立都要进行预解析
console.log(a);//->undefined
if (!!("a" in window)) {//"a" in window -> true
 var a = "xie";
}
console.log(a);//->xie

例子中的if是不成立的,预解析的时候,碰到非functon内的var,都会声明,无论你写在if else 还是别的判断里; 假设if语句起作用的话,那么第一次log(a)的时候,就会报错了(没有声明的变量,是不能直接用的,除非typeof ),而声明并且没有赋值的表现才是undefined;假设不成立; 最开始总结的预解析步骤:代码运行之前,先扫描有没有带var关键字的变量名,有的话,为这个变量名,在内存里开一个空间;预解释是发生在代码执行前的,所以if根本阻挡不了预解析;

  • 2、预解析只发生在”=“的左边,只把左边的进行预解析,右边的是值是不进行预解析的

匿名函数之函数表达式:把函数定义的部分当做值赋值给一个变量或者元素的事件

fn1();//->undefined() Uncaught TypeError: fn is not a function JS中只有函数可以执行 && JS上面的代码如果报错了,在不进行任何的特殊处理情况下我们下面的代码都不在执行了
var fn1 = function () {
 console.log("ok");
};
fn1();
//预解释的时候:fn=xxxfff000
fn2();//->"ok"
function fn2() {
 console.log("ok");
}
fn2();//->"ok"

预解析的时候:var fn1 = function()... ->fn的默认值是undefined;这里即使有function,也是不能进行预解释的

  • 3、函数体中return下面的代码都不在执行了,但是下面的代码需要参加预解析;而return后面的东西是需要处理的,但是由于它是当做一个值返回的,所以不进行预解析;
function fn() {
 console.log(total);
 return function sum() {};//return是把函数中的值返回到函数的外面,这里是把function对应的内存地址返回的到函数的外面,例如:return xxxfff111;函数体中return下面的代码都不在执行了
 var total = 10;
 console.log(total);
}
  • 4、匿名函数的function在全局作用域下是不进行预解析的;

匿名函数之自执行函数:定义和执行一起完成了;函数内的声明,只是在函数内使用;

(function(num){
 var testStr="test"+num;
 console.log(num);
})(100);

console.log(testStr);// testStr is not defined
  • 5、在预解析的时候,如果遇到名字重复了,只声明一次。 不重复的声明,但是赋值还是要重复的进行的

预解析:

var fn; 声明
 fn = xxxfff000; [声明]不用了+定义
 fn = xxxfff111; [声明]不用了+定义
 // ->fn=xxxfff111
 var fn = 12;//window.fn=12
 function fn() {//window.fn=function(){}
 }

JS中作用域只有两种:

  • window全局作用域;
  • 函数执行形成的私有作用域;
  • {name:“”} if(){} for(){} while(){} switch(){} 这些都不会产生作用域;

ES6可以用let形成块级作用域;https://3water.com/article/67732.htm

面试题

// 涉及this的指向和闭包
var num = 20;
var obj = {
	num: 37,
	fn: (function (num) {
		this.num *= 3; // window.num * 3 = 60
		// num += 15; 
		var num = 45;
		return function () {
			this.num *= 4; 
			num += 20; // 调用父作用域的num (45+20)
			console.log(num);
		};
	})(num), //->把全局变量num的值20赋值给了自执行函数的形参,而不是obj下的30,如果想是obj下的30,我们需要写obj.num
};
var fn = obj.fn; 
fn(); //->65 , 执行了第1次=> window.num = 240
obj.fn(); //->85 闭包(65+20) // 执行了第2次=> obj.num = 37*4 = 148
console.log(window.num, obj.num); // 240,148

以上就是详解JS预解析原理的详细内容,更多关于JS预解析原理的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
jquery json 实例代码
Dec 02 Javascript
jQuery UI AutoComplete 使用说明
Jun 20 Javascript
jQuery ajax serialize()方法的使用以及常见问题解决
Jan 27 Javascript
使用jQuery实现更改默认alert框体
Apr 13 Javascript
举例讲解jQuery中可见性过滤选择器的使用
Apr 18 Javascript
JS、jQuery中select的用法详解
Apr 21 Javascript
Agularjs妙用双向数据绑定实现手风琴效果
May 26 Javascript
Javascript实现的StopWatch功能示例
Jun 13 Javascript
解决vue点击控制单个样式的问题
Sep 05 Javascript
vue移动端弹框组件的实例
Sep 25 Javascript
js实现轮播图效果 z-index实现轮播图
Jan 17 Javascript
JS实现电脑虚拟键盘的操作
Jun 24 Javascript
深入了解JS之作用域和闭包
Jun 16 #Javascript
JS数组及对象遍历方法代码汇总
Jun 16 #Javascript
浅谈Vue 函数式组件的使用技巧
Jun 16 #Javascript
原生JS实现天气预报
Jun 16 #Javascript
vue实现公告栏文字上下滚动效果的示例代码
Jun 16 #Javascript
原生JS实现无缝轮播图片
Jun 24 #Javascript
如何利用Node.js与JSON搭建简单的动态服务器
Jun 16 #Javascript
You might like
PHP操作MongoDB时的整数问题及对策说明
2011/05/02 PHP
php遍历目录方法小结
2015/03/10 PHP
php接口数据加密、解密、验证签名
2015/03/12 PHP
基于JQuery+PHP编写砸金蛋中奖程序
2015/09/08 PHP
JavaScript 闭包深入理解(closure)
2009/05/27 Javascript
利用jQuery的$.event.fix函数统一浏览器event事件处理
2009/12/21 Javascript
基于jquery的关于动态创建DOM元素的问题
2010/12/24 Javascript
jquery iframe操作详细解析
2013/11/20 Javascript
给事件响应函数传参数的四种方式小结
2013/12/05 Javascript
结合JQ1.9通过js正则判断各种浏览器版本的方法
2013/12/30 Javascript
javascript实现在指定元素中垂直水平居中
2015/09/13 Javascript
JS运动相关知识点小结(附弹性运动示例)
2016/01/08 Javascript
Node.js插件安装图文教程
2016/05/06 Javascript
解决Node.js使用MySQL出现connect ECONNREFUSED 127.0.0.1:3306的问题
2017/03/09 Javascript
node.js 核心http模块,起一个服务器,返回一个页面的实例
2017/09/11 Javascript
30分钟快速入门掌握ES6/ES2015的核心内容(下)
2018/04/18 Javascript
基于vue中对鼠标划过事件的处理方式详解
2018/08/22 Javascript
基于vue框架手写一个notify插件实现通知功能的方法
2019/03/31 Javascript
vue路由中前进后退的一些事儿
2019/05/18 Javascript
详解vue中的父子传值双向绑定及数据更新问题
2019/06/13 Javascript
layui radio点击事件实现input显示和隐藏的例子
2019/09/02 Javascript
Python使用ntplib库同步校准当地时间的方法
2016/07/02 Python
Python简单操作sqlite3的方法示例
2017/03/22 Python
Python内置函数reversed()用法分析
2018/03/20 Python
Python实现查询某个目录下修改时间最新的文件示例
2018/08/29 Python
python 反编译exe文件为py文件的实例代码
2019/06/27 Python
python time.sleep()是睡眠线程还是进程
2019/07/09 Python
删除pycharm鼠标右键快捷键打开项目的操作
2021/01/16 Python
解决CSS3 transition-delay 属性默认值0不带单位失效的问题
2020/10/29 HTML / CSS
药剂专业自荐信范文
2014/04/16 职场文书
新学期开学演讲稿
2014/05/24 职场文书
2014旅游局党组书记党建工作汇报材料
2014/11/02 职场文书
八年级上册语文教学计划
2015/01/22 职场文书
暑期工社会实践报告
2015/07/13 职场文书
学校2016年全国助残日活动总结
2016/04/01 职场文书
css3手动实现pc端横向滚动
2022/06/21 HTML / CSS