详解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 Select(单选) 模拟插件 V1.3.62 改进版
Jul 17 Javascript
div层的移动及性能优化
Nov 16 Javascript
jquery中ajax学习笔记一
Oct 16 Javascript
jQuery实现首页图片淡入淡出效果的方法
Jun 10 Javascript
jquery插件tytabs.jquery.min.js实现渐变TAB选项卡效果
Aug 25 Javascript
Bootstrap进度条组件知识详解
May 01 Javascript
RequireJS多页面应用实例分析
Jun 29 Javascript
详解vue数据渲染出现闪烁问题
Jun 29 Javascript
基于iScroll实现下拉刷新和上滑加载效果
Jul 18 Javascript
vue实现商城上货组件简易版
Nov 27 Javascript
jquery 回调操作实例分析【回调成功与回调失败的情况】
Sep 27 jQuery
详解ES6实现类的私有变量的几种写法
Feb 10 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乱码问题
2012/03/25 PHP
php+mysqli使用预处理技术进行数据库查询的方法
2015/01/28 PHP
PHP经典面试题集锦
2015/03/19 PHP
php轻量级的性能分析工具xhprof的安装使用
2015/08/12 PHP
PHP创建文件,并向文件中写入数据,覆盖,追加的实现代码
2016/03/25 PHP
PHPUnit测试私有属性和方法功能示例
2018/06/12 PHP
ThinkPhP+Apache+PHPstorm整合框架流程图解
2020/11/23 PHP
JavaScript可否多线程? 深入理解JavaScript定时机制
2012/05/23 Javascript
JS获取当前网页大小以及屏幕分辨率等
2014/09/05 Javascript
js实现同一页面多个不同运动效果的方法
2015/04/10 Javascript
概述VUE2.0不可忽视的很多变化
2016/09/25 Javascript
jQueryUI 拖放排序遇到滚动条时有可能无法执行排序的小bug及解决方案
2016/12/19 Javascript
基于MVC方式实现三级联动(JavaScript)
2017/01/23 Javascript
Angularjs的$http异步删除数据详解及实例
2017/07/27 Javascript
vue-resource拦截器设置头信息的实例
2017/10/27 Javascript
nodejs express配置自签名https服务器的方法
2018/05/22 NodeJs
微信小程序使用map组件实现解析经纬度功能示例
2019/01/22 Javascript
vue + any-touch实现一个iscroll 实现拖拽和滑动动画效果
2019/04/08 Javascript
前端如何实现动画过渡效果
2021/02/05 Javascript
[00:06]Yes,it worked!小卡尔成功穿越时空加入战场!
2019/07/20 DOTA
Python解析最简单的验证码
2016/01/07 Python
Python增量循环删除MySQL表数据的方法
2016/09/23 Python
python2使用bs4爬取腾讯社招过程解析
2019/08/14 Python
Python3.8对可迭代解包的改进及用法详解
2019/10/15 Python
如何使用python进行pdf文件分割
2019/11/11 Python
python中selenium库的基本使用详解
2020/07/31 Python
Python 实现PS滤镜中的径向模糊特效
2020/12/03 Python
线程问题:wait()方法是定义在哪个类里面
2015/07/07 面试题
高级人员简历的自我评价分享
2013/11/03 职场文书
文明演讲稿范文
2014/05/12 职场文书
爱的奉献演讲稿
2014/09/10 职场文书
2015年司法所工作总结
2015/04/27 职场文书
仙境之桥观后感
2015/06/16 职场文书
反邪教学习心得体会
2016/01/15 职场文书
如何用Node.js编写内存效率高的应用程序
2021/04/30 Javascript
Centos环境下Postgresql 安装配置及环境变量配置技巧
2021/05/18 PostgreSQL