详解JS预解析原理

目录
  • 预解析的的不同机制
  • var的预解析机制
  • function 关键字的预解析步骤
  • 预解析机制
  • 面试题

预解析的的不同机制

预解析也叫预声明,是提前解析声明的意思;预解析是针对变量和函数来说的;但是变量和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://www.jb51.net/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预解析原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • 跟我学习javascript的var预解析与函数声明提升

    1.var 变量预编译 JavaScript 的语法和 C .Java.C# 类似,统称为 C 类语法.有过 C 或 Java 编程经验的同学应该对"先声明.后使用"的规则很熟悉,如果使用未经声明的变量或函数,在编译阶段就会报错.然而,JavaScript 却能够在变量和函数被声明之前使用它们.下面我们就深入了解一下其中的玄机. 先来看一段代码: (function() { console.log(noSuchVariable);//ReferenceError: noSuchVari

  • 深入理解JavaScript中的预解析

    前言 JavaScript是解释型语言是毋庸置疑的,但它是不是仅在运行时自上往下一句一句地解析的呢? 事实上或某种现象证明并不是这样的,通过<JavaScript权威指南>及网上相关资料了解到,JavaScript有"预解析"行为.理解这一特性是很重要的,不然在实际开发中你可能会遇到很多无从解析的问题,甚至导致程序bug的存在.为了解析这一现象,也作为自己的一次学习总结,本文逐步引导你来认识JavaScript"预解析",如果我的见解有误,还望指正. 在

  • JS变量提升及函数提升实例解析

    1在js中只有两种作用域 a:全局作用域 b:函数作用域 在ES6之前,js是没有块级作用域. 首先来解释一下什么是没有块级作用域? 所以此时 是可以打印输出变量a的值. 2:什么是变量提升? 在我们的js中,代码的执行时分两步走的,1.预解析 2.一步一步执行 在预解析阶段:首先会在全局作用域内,js解析器会找所有的 var .function .参数,并提前到当前作用域的最顶上去(变量的赋值操作不会提前,还在原来的地方),此时并没有执行代码. 然后再开始一行一行执行代码.遇到了函数调用,于是

  • 详解javascript 变量提升(Hoisting)

    简介 "变量提升"意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确. 实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中. 声明变量的方法 var.let.const 不用以上关键字直接赋值的变量会挂载与windows环境下; let a=9 const a=1 var a=6 c=5 声明函数的方法 javascript中声明函数的方法有两种:函数声明式和函数表达式. //函数声明 function say(){ console.log('

  • web面试之JS预解析与变量提升区别

    目录 什么是预解析? 变量和函数预解析的区别 重复声明var变量 变量提升和函数提升优先级 函数优先级大于变量优先级的深入探究 预解析流程 搜寻预解析关键字 执行预解析 几个需要注意的细节 什么是预解析? 概念: JS代码在在代码从上往下执行前,浏览器会先把所有变量声明解析一遍, 这个阶段叫预解析. 详讲 寻找作用域中的var 和function声明(匿名函数没有function声明,所以不会提升),然后对其进行事先声明, 并把赋值操作留在原地,再从上到下执行代码.这就是一个预解析的过程. 变量

  • 详解JS预解析原理

    目录 预解析的的不同机制 var的预解析机制 function 关键字的预解析步骤 预解析机制 面试题 预解析的的不同机制 预解析也叫预声明,是提前解析声明的意思:预解析是针对变量和函数来说的:但是变量和function的的预解析是两套不同的机制: 当浏览器加载我们的HTML页面的时候,首先会提供一个供JS代码执行的环境->全局作用域global(浏览器中的全局作用域,也叫顶级作用域是window) JS中的内存空间分为两种:栈内存.堆内存 栈内存:提供JS代码执行的环境,存储基本数据类型的值:

  • 详解js跨域原理以及2种解决方案

    1.什么是跨域 我们经常会在页面上使用ajax请求访问其他服务器的数据,此时,客户端会出现跨域问题. 跨域问题是由于javascript语言安全限制中的同源策略造成的. 简单来说,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名.协议和端口号的组合. 例如: 2.实现原理 在HTML DOM中,Script标签是可以跨域访问服务器上的数据的.因此,可以指定script的src属性为跨域的url,从而实现跨域访问. 例如: 这种访问方式是不行的.但是如下方式,

  • 详解JS事件循环及宏任务微任务的原理

    目录 宏任务 微任务 事件循环 宏任务与微任务 微任务中创建宏任务 宏任务中创建微任务 宏任务中创建宏任务 微任务中创建微任务 总结 本质上来说,JavaScript是同步的.阻塞的.单线程语言,不管是在浏览器中还是nodejs环境下.浏览器在执行js代码和渲染DOM节点都是在同一个线程中,执行js代码就无法渲染DOM,渲染DOM的时候就无法执行js代码.如果按照这种同步方式执行,页面的渲染将会出现白屏甚至是报错,特别是遇到一些耗时比较长的网络请求或者js代码,因此在实际开发中一般是通过异步的方

  • 详解MD5算法的原理以及C#和JS的实现

    目录 一.简介 二.C# 代码实现 三.js 代码实现 一.简介 MD5 是哈希算法(散列算法)的一种应用.Hash 算法虽然被称为算法,但实际上它更像是一种思想.Hash 算法没有一个固定的公式,只要符合散列思想的算法都可以被称为是 Hash 算法. 算法目的就是,把任意长度的输入(又叫做预映射 pre-image),通过散列算法变换成固定长度的输出,该输出就是散列值. 注意,不同的输入可能会散列成相同的输出,所以不能从散列值来确定唯一的输入值. 散列函数简单的说就是:一种将任意长度的消息压缩

  • 详解http访问解析流程原理

    详解http访问解析流程原理 http访问网址域名解析流程: 1.在浏览器中输入www.qq.com域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析. 2.如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析. 3.如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/ip参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器

  • Qiankun原理详解JS沙箱是如何做隔离

    目录 前言 复习一下沙箱 SanpshotSandbox LegacySandbox ProxySandbox 隔离原理 XXX is undefined 总结 前言 相信大家也知道 qiankun 有 SnapshotSandbox, LegacySandbox 和 ProxySandbox 这些沙箱,而它们又可以分为单例和多例两种模式,网上也有很多文章对其进行介绍. 但这些文章的关注点都是沙箱的环境恢复做的事,那 JS 的隔离到底是怎么做到的呢? 换个问法,当我写 window.a = 1

  • 详解jquery选择器的原理

    详解jquery选择器的原理 html部分 <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <script src="js/minijquery.js"></script> </head> <body>

  • 详解JS异步加载的三种方式

    一:同步加载 我们平时使用的最多的一种方式. <script src="http://yourdomain.com/script.js"></script> <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作.所以默认同步执行才是安全的.但这样如果js中有输

  • 详解JS深拷贝与浅拷贝

    一.预备知识 1.1.JS数据类型 基本数据类型:Boolean.String.Number.null.undefined 引用数据类型:Object.Array.Function.RegExp.Date等 1.2.数据类型的复制 基本数据类型的复制,是按值传递的 var a = 1; var b = a; b = 2; console.log(a); // 1 console.lob(b); // 2 引用数据类型的复制,是按引用传值 var obj1 = { a: 1; b: 2; }; v

随机推荐