编程语言分为:编译型语言和解释型语言。
编译型和解释型语言的区别:编译型语言在程序执行之前,需要经过编译器的编译过程,并且编译之后会直接保留机器能读懂的二进制文件,这样每次运行程序时,都可以直接运行该二进制文件,而不需要再次重新编译了。而由解释型语言编写的程序,在每次运行时都需要通过解释器对程序进行动态解释和执行
编译型语言:例如java,c 解释型语言:例如JavaScript, python
如图所示:
如图所示,JavaScript代码并不需要编译成二进制文件,所以JavaScript是一种解释型编程语言,它由事件与对象驱动,支持面向过程,面向对象,命令式以及函数式编程。(可以自己去了解)
运行js代码有两个阶段: 编译阶段、执行阶段 本文主讲第一个阶段,执行阶段在下篇
内存模型:在js引擎完成语法分析进行代码生成时会为变量分配空间(内存)。分为4个区
栈区:存放的是真正的值,比如函数的参数值,局部变量。堆区:存放的是对象的引用(逻辑地址),比如function,array静态区:存放的是全局变量和静态变量常量区:存放的是常量变量查找规则:左查询(LHS)和右查询(RHS),左查询是对变量进行查找,找不到便创建一个变量默认值为undefined。右查询是查找变量的值,如果在全局作用域中未能获取变量的值便会报错为ReferenceError。 举例0:
var a; console.log(a); // undefined上例子中,第一句代码我们进行了对变量a的定义,首先执行的是左查询存不存在a这个变量,很明显不存在,便会创建变量a,并赋值为undefined。第二局代码中打印的a便是右查询,它会去查找变量a的值,如果我们把第一句代码去掉,再执行时便会出现ReferenceError的错误。同样这里的console对象也是进行的右查询,并检查是否具有log方法。
函数作用域:函数拥有自己的作用域,它可以访问外部作用域,但是函数内部作用域无法在外部进行访问 举例一:
var a = 'a'; function b() { var b = 'b'; function c() { console.log(b, a); // b a } c(); } b();在这个例子中,函数b拥有自己的作用域和全局作用域,函数c除了拥有自己的作用域还有函数b和全局的作用域,当我们在函数c中查找变量b和a时,js引擎会先在函数c的作用域中查询,如果未找到便往外部作用域(函数b)中查找,一层一层的往外部查询直到找到为止。如果在全局作用域中也未能查找到变量,便会报ReferenceError的错误。在变量的查找过程就形成了作用域链。 作用域规则如图所示:
举例二:
var a = 'a'; var b = 'b'; function c() { var b = 'b_c()'; d(); } c(); function d() { console.log(b); // 'b' }在例子二中,调用函数b却是打印的是b,按照正常的想法应该是b_c(),这是因为什么呢?因为JavaScript采用的是词法作用域,它是静态的,取决于你写在哪个位置。在上例中,函数d是写在全局作用域中,所以在函数d中查找的变量b,首先会在自身的作用域中查找,而后在全局作用域中查找,而不是在函数c的作用域中查找。 作用域如图所示:
可以看出函数d的作用域其实并不是在函数c的作用域中。
块作用域:javaScript的块级就是{…}大括号内的代码块,我们称之为一个块级。es6新增变量let,常量const,他们都具有块级作用域,也不存在变量提升。 我们先看一个变量提升的例子:
for (var i=0; i<10; i++) { } console.log( i ); // 10为什么i是10?当var i=0;时,会进行左查询,然后变量提升在全局作用域创建一个变量i赋值为0,而后进行了10次加1,这很容易造成全局变量污染。所以要怎么解决呢? es6推出了let 和 const, 它们都具有块级作用域,也就是说定义的变量i不会出现在全局作用域,而是在一个块里面。
for (let i=0; i<10; i++) { } console.log( i ); // ReferenceError活动对象用于保存this,参数变量(默认arguments),调用位置,调用方法 变量对象用于保存定义的变量以及函数
全局执行环境 Web 浏览器中,全局执行环境是最外围的一个执行环境。被认为是 window 对象,并且全局环境没有活动对象,关闭网页和浏览器时销毁环境。全局执行环境的变量对象始终都是作用域链中的最后一个对象 函数执行环境 当一个函数被调用时,在执行函数内部代码前生成执行环境,然后该函数的环境会被推入执行环境栈 (调用栈)中执行,执行完毕之后会销毁环境,保存在其中的所有变量和函数定义也随之销毁。
在执行时会创建变量对象的一个作用域链,在作用域链的最前面是当前执行环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。而环境栈中的下一个环境的变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境,一直延续到全局执行环境。
作用域链是保证对执行环境有权访问的所有变量和函数的有序访问。 当我们进行变量查找时便是遵循的作用域链的规则,从当前环境的变量对象开始查找一直到全局环境
对于例子一,一步一步执行代码时用执行环境来分析一下:首先是一个全局环境,然后调用函数b生成一个执行环境,再调用函数c又生成一个执行环境。如图所示:
执行环境和作用域的区别 执行环境和作用域是两个不一样的东西,很多人都会搞混它。执行环境是动态的,在执行代码时产生的,代码执行完毕环境也就被销毁了,当我们调用函数时,会生成一个函数执行环境,函数内的代码执行完毕,那么该环境也就被销毁了。而作用域是静态的,它取决于我们代码写在哪里,可以查看举例一和二。当进行变量查找时,会先在自身执行环境的变量对象中查找,如果查找不到则根据变量对象作用域链向外部环境的变量对象中查找,一直查找到全局执行环境的变量对象为止。
以上就是本人所理解的js在编译时经历了什么,如有错误可指正。🤭🤭🤭
本文参考于:你不知道的js上卷,JavaScript高3 https://www.jianshu.com/p/672d512fdbae https://www.cnblogs.com/lucy-xyy/p/11820303.html