【JS深渊】系列是我用来对前端原生javascript语言相关技术进行的深度探究或者笔记记录,我我的是一个很爱去挖层的这么一我的,但不幸的是觉悟的太晚,但我不会放弃。决定开始写博客目的有两个,一个是有仪式性的去持续提升本身的技术能力,另外一个就是但愿向上再向上。不过本身能力也是有限的,若是写的很差或者出现什么纰漏又或者错误的地方,脱裤式欢迎你们的建议、指正。没错看着我,对,盯着你的屏幕,别眨眼,请记住个人这句话:
您的反馈是就是我持续进步的动力。
好了,收!biu~javascript
javascrip是一个弱类型解释性语言,依赖于浏览器JS引擎进行解析执行。里面的解析过程也十分复杂。可是稍微了解一下,对javascript语言的掌握有着很大的帮助,可以使咱们在平常的开发过程当中更加灵活运用js语言自己的特性。废话很少说了,开始:🤣🤣html
lotoze我先来段风骚的代码前端
console.log("123")
console.log("456");
复制代码
某某侠士:“喂,糟老头!你在逗我玩?“,哪里风骚了,不就是两句打印吗? java
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<script type="text/javascript">
//js代码块...
</script>
</body>
</html>
复制代码
这一过程,专业一点叫作语法分析,每一个计算机语言都会有这一步,并且计算机语言的区分的奥妙就在语法分析、词法分析、语义分析的差别。不过这里只说语法分析,能够理解为对加载好的代码的一次的初次检验。
首先,系统,不,确切的说是js引擎线程并不会当即执行文件中的js代码,而是会先通读一遍js代码,看看是否有低级的语法错误,若是有低级语法错误,那么就当即中止并抛出错误到控制台;若是没有低级的语法错误,就会为接下来js代码的真正执行作一些准备工做。可是这样说真的对吗?必定要注意理解! 咱们来看一下代码:数组
console.log("123");
console.log"456");
console.log("789");
复制代码
咱们先来假设一下:按照刚才咱们所说的js代码并不会当即执行,而是会先进行通读一遍代码,若是有低级错误就抛出,并当即中止后续的代码。那么按照这种理解,最后的结果就是:直接抛出低级错误,"123"并不会输出。可是你会惊奇的发现😱😱,"123"竟然正常输出了,第二行报错,后边的不执行。最终结果为:浏览器
console.log("123"); //正常打印"123"
console.log"456"); //抛出低级错误,当即中止解析执行
console.log("789");
复制代码
这说明什么?bash
说明: js引擎在通读代码作分析时,并非读所有的js代码,而是一行一行的去读,若是这一行代码没有低级语法错误,那么是这一行代码进入js执行的下一准备阶段,若是这一行代码有低级语法错误则会在这一行抛出错误并当即中止。注意是 这一行。数据结构
这又是为何?为何要一行一行的读?闭包
由于javascript语言是解释性的语言,是解释一行执行一行。那么这里请跟lotoze去发散思考一下,解释性语言是否是都是解释一行执行一行呢?答案是确定的。若是是先等到所有的代码解释完成再同一执行,这性能得有多慢啊,黄花菜都凉了。值得一提的是,一有错误就会当即中止,这是单线程计算机语言的特性。异步
预编译是js代码执行前一刻的准备过程。为js的执行提供一些必要所需,在加载好的js代码经历过js引擎的语法分析检验以后会当即进入此阶段。
首先,想要了解这一阶段咱们须要先明确几个重要的概念。
顾名思义,隐式属性即为系统内部调用,不能被咱们看到或使用的属性。
做用域分为全局做用域和函数做用域,能够说这两类出现缘由是由于函数的出现,函数有着本身专属的做用域。
做用域有个比较专业一点的名字叫作执行期上下文。其实看着高大上的名字,本质就是一个对象,里面存储着js代码执行时所须要的必要“粮食”。
//全局做用域
...
function test() {
//局部/函数做用域
...
}
复制代码
做用域链专业一点的说法叫作,执行期上下文的集合按照必定的规律造成的链式结构叫作做用域链。 通俗一点讲,执行期上下文穿串了,而后串太长了,成了个链子la😂😂
咱们说,在js中的数据结构受Java的影响很大,在js中也是一切皆对象,函数也能够看作一个对象,对象能够拥有属性和方法,属性分为显式属性和隐式属性。在函数对象上都有着一个叫作[[scopes]]的隐式属性。这个属性的做用就是存储着执行期上下文的集合。换句话说,[[scopes]]就是做用域链。它是一个栈结构,能够理解为是一个数组,具备栈结构First in, last out的特性。
var b = 20;
function test(argument) {
var a = 10;
}
复制代码
下面是在控制台打印出来的test函数体,能够看到[[scopes]]:
基本的概念已经了解了,可是咱们进一步思考一下,咱们说预编译是为js代码真正执行阶段准备过程,那么预编译到底准备了什么?准备的过程是怎样的?
*下面我来总结一下预编译的过程,预编译的执行过程分为两步:
预编译是变量声明提高和函数声明提高的根本缘由。通过预编译过程以后,代码将当即进入真正的执行阶段。
这里咱们来一个的栗子,来进行理解一下预编译执行过程。
console.log(a);
console.log(b);
var a = 10;
var b = a;
function a(a) {
var a = "aaa";
console.log(a);
}
function foo(b) {
var a = 100;
var b = a;
console.log(a);
}
foo();
a("lalala");
//请问上述代码打印什么?
复制代码
这个栗子若是你只知道变量声明提高和函数声明提高,想要得出正确结果然的很麻烦。可是这个栗子实质上就是js代码的预编译过程。 咱们来分析一下:
当js代码加载以后,通过语法分析后,进入程序运行的前一刻的预编译阶段。这个阶段分为全局预编译阶段和函数预编译阶段。 那么咱们依据这个栗子去具体分析一下预编译过程:
//Go
{}
复制代码
//GO
{
a: undefined,
b: undefined
}
复制代码
//GO
{
a: a(){}, //赋值为函数体
b: undefined
}
复制代码
// aAO --> 表示a函数的AO对象
{}
复制代码
//aAO --> 表示a函数的AO对象
{
a: undefined
}
复制代码
//aAO --> 表示a函数的AO对象
{
a: "lalala"
}
复制代码
//aAO --> 表示a函数的AO对象
{
a: a(){}
}
复制代码
//aAO --> 表示foo函数的AO对象
{
a: undefined,
b: undefined
}
复制代码
这时预编译过程就已经完成了,生成了这个一个GO和两个AO对象,每一个预编译对象(虚构的,为了理解,包括GO和AO)生成完毕的同时都会当即压入[[scopes]]栈中,一般GO先入栈,而后是当前的AO对象入栈。下一步就是真正的执行了,调用栈则会按照先进后出的方式出栈执行。 最后的结果为:
console.log(a); //a(){}
console.log(b); //undefined
var a = 10;
var b = a;
function a(a) {
var a = "aaa";
console.log(a);
}
function foo(b) {
var a = 100;
var b = a;
console.log(a);
}
foo(); //100
a("lalala"); //报错a is not a function
复制代码
js的内部执行阶段,是一个很是庞杂的过程。老头我会去使用专门的篇幅来写。这里只作一个简单的介绍。
当加载的js代码通过语法分析以及预编译后,会进入js引擎线程会把js代码划分为的宏任务(包括同步任务、异步任务)、微任务。而后按照宏任务(同步任务)-->微任务-->宏任务(异步任务)的轮询中。
lotoze | 【原创】
着重说明:里面一些表情图片并不是原创,只是为了读者读起来不是那么枯燥乏味。但若是原做者以为有侵犯版权的意思,请使用下方联系方式与我联系,为了尊重原创做者的辛苦创做,我将及时处理!
固然,没事也能够联系啦😘😘欢迎交流!
写做不易,
若是您还以为凑合,就给个赞!
若是以为确实以为: “老家伙,有你的啊!”就加个关注!
若是文章有任何的错误,脱裤式欢迎你们来进行批评指正!
每个鼓励都是lotoze我持续抛头颅,撒鸡血的创做动力!
每个批评反馈也都是lotoze我持续成长的台阶!