网站建设项目汇报,软件外包开发平台,苏州网站制作设计,中国纪检监察报单国平嘿#xff0c;欢迎你来 #xff01;#x1f495; 文章目录 前言一、运行一个 js 文件二、运行环境三、js执行代码的过程#xff08;普通变量#xff09;四、打印 window五、js执行代码的过程#xff08;函数变量#xff09;六、函数调用函数的过程注意#xff01;… 嘿欢迎你来 文章目录 前言一、运行一个 js 文件二、运行环境三、js执行代码的过程普通变量四、打印 window五、js执行代码的过程函数变量六、函数调用函数的过程注意最后、作用域与作用域链面试题 前言 在上一篇文章里我们简单说了浏览器的工作原理和v8引擎的原理 浏览器渲染
首先先下载 index.html 遇到css 或者script 文件就下载通过浏览器内核进行解析 index.html如果解析过程中遇到 js 代码就立即执行 js 代码因为js代码属于高级语言cpu不能直接执行所以借助js引擎对于v8 引擎来说通过parse模块把我们的代码解析成 AST 树型结构再通过ignation转化为字节码再编译成低级语言cpu就可以执行这些指令了
那今天我们研究的就是 js源代码到AST抽象语法树他进行解析是如何解析的。 上篇文章整理了很久多多少少有了点概念今天这篇文章是基于上篇基础上的加深希望我表达的足够清晰如果错处还望指正感谢biubiu ~ 一、运行一个 js 文件 我们以下的讲解都是基于早期的ECMA的版本规范也就es5之前的。也发现了所有的变量定义都是用的 var。 在早期的版本规定里是这样定义的每个执行上下文都会被关联到一个变量环境VO: variable Object它是一个对象源代码中的变量和函数都会 被作为属性 添加到VO中对于函数来说参数也会被添加到VO中 在最新的的ECMA 版本规范中每个执行上下文会关联到一个变量环境中在执行代码中变量和函数的声明会 作为环境纪录 添加到变量环境中对于函数来说参数也会被作为环境记录添加到环境变量中 const name haha;const name2 haha2;console.log(name);
假设这是一个 js 文件 想在浏览器上运行首先得有一个index.html 通过script 引入才能运行。js引擎怎么运行的呢 二、运行环境 我们编写的JS代码都是需要在一个环境中运行的比如
浏览器引擎例如webkitgecko,Trident, blinknode*(基于V8渲染js)webviewv8引擎JS之所以能够在浏览器中运行是因为浏览器给JS提供了执行的环境 栈内存
先说几个概念不太理解也没关系
栈内存 ( ECStackExecution Context Stack):执行环境栈) 它是代码执行环境它是浏览器分配的内存空间用来执行代码 。全局对象GOGloble Object: 该对象所有作用域都可以访问 它用来存放当前环境的内置方法类变量等他还有个window属性指向自己这就是为什么我们可以直接使用Math,String,setTimeout 等方法 这里请注意对于方法类这种复杂类型数据存放的是对应的内存地址堆内存才是真正的存放代码堆内存(Heap) 存放东西属性和方法例:isNaN:function…任何开辟的内存都有一个16进制的内存地址方便后期找到这个内存ox开头执行上下文ECS(Execution Context stack) 在编程语言中代码执行中为了区分全局变量和函数执行所处的不同的作用域目的是为了区分每个词法作用域下代码的独立性会产生执行上下文栈一个是全局执行上下栈 GEC一个是函数执行上下文栈FEC都会在ECS里运行全局执行上下栈 GEC: 包括两部分,第一部分在代码执行前在paese转化为AST的过程中会将全局定义的变量函数等加入到 GO里但是不会赋值也称为变量的提升第二部分是代码的执行变量赋值或者执行函数等等函数执行上下文栈FEC:包括三部分第一部分在解析函数成为AST树结构时会创建一个AOAO中包括形参、arguments、函数定义和指向函数对象、定义的变量第二部分作用域由当前AO对象和父级VO组成查找是会一层层查找第三部分this的绑定值本篇先不说
三、js执行代码的过程普通变量
const name haha;const name2 haha2;console.log(name);var golbalObject{String:类,Date:类,setTimeout:方法,window:golbalObject,name:undefined,name2:undifined}再代码被解析之前运行之前v8 引擎内部会帮助我们创建一个对象全局变量GlobalObject又称为GO,这个GlobalObject 存放着当前环境的全局对象该对象所有的作用域都可以访问里面包括 Math,Date,settimeout…等 类 和 方法 还有一个 window属性 指向自己在代码编译的时候它可以确定我们定义了哪些变量然后将这些变量添加到全局对象里面因为我们还没有真正的执行所以这些变量都是undefined变量存放在GO ,v8 为了执行代码v8引擎内部会有一个全局执行上下文栈全局代码需要被执行的时候才会创建称为EGC有且只有一个,EGC 包括两部分: 第一部分称为VO在代码执行前在paese转化为AST的过程中会将全局定义的变量函数等加入到 GO里但是不会赋值也称为变量的提升所以这个VO对应的是GO第二部分是代码的执行变量赋值或者执行函数等等比如现在要执行 var name30 这个代码它就会去VO里面找VO对应GO再把GEC放到调用栈里执行 四、打印 window
const name haha;const name2 haha2;console.log(name);var golbalObject{String:类,Date:类,setTimeout:方法,window:golbalObject,name:undefined,name2:undifined}console.log(window)当打印window时发现这个对象包括很多东西很多内置方法包括name,name2等属性是因为这个window指向时golbalObject正如上面所说他一个window属性 指向自己
五、js执行代码的过程函数变量
1. foo();2. function foo() {3. console.log(开心快乐向前冲);4. }
提前声明 ps:
我们所有代码在执行前都要加载到内存里代码存放在磁盘再进入内存转化为机器语言最后才能被cpu执行在内存里会划分为栈结构和堆结构简单类型数据放在栈里复杂类型数据地址放在栈值放在堆
运行的不仅仅是普通变量那对于函数来说他是一个怎么样的过程呢
根据上面所说在编译阶段会把变量添加到全局对象里在未运行之前值为undefined但是foo 在定义之前执行但是能执行所有的代码为什么可以打印出来foo呢因为是在编译所以第一行代码没有执行第二行发现声明了一个函数他也会定义 foo在GO里但是不同的是因为函数较特殊他会再开辟一个新的内存空间用来存放函数 函数执行上下文栈FEC先理解这一块下面会具体说 foo这里存放的就是函数对应的内存空间地址理解成指针、引用因为一个地址可以对应多个变量也是为什么复杂类型数据会互相影响以上是编译的过程当执行到第二行时 foo() 【请留意他其实是两部分foo 和】他会再VO (对应的是GO) 里面查找Foo他发现是个内存地址就会根据地址找到这个空间表示函数的执行就会把我们 FEC 放入到 调用栈 里面执行。 为了不饶晕乎我们先把以上理解清除再往下看
1. foo();2. var name1233. function foo(num) {4. num15. console.log(开心快乐向前冲);6. console.log(name)7. }我们把对应的函数空间放到调用栈不是直接运行而是创建一个函数执行上下文 包括三部分第一部分在解析函数成为AST树结构时会创建一个AOAO中包括形参、arguments、函数定义和指向函数对象、定义的变量第二部分作用域由当前AO对象和父级VO组成查找是会一层层查找第三部分this的绑定值本篇先不说在第六行的时候我们发现在Ao 里面并没有name这个值为什么使用了全局的name呢是这样的当我们查找一个变量时真实的查找路径是沿着作用域链来查找的函数执行上下文不仅仅包括 **VO** ,还有一个 **scope chain** 叫做作用域链 看图二一个函数执行上下文的作用域链有两部份组成当前 AO 的作用域 父级作用域父级作用域在函数编译时就确定了他的执行顺序是先在自己的函数执行上下文找再向他的上级作用域找 所以你理解为什么可以使用name了吗 一旦函数执行完毕这个函数执行上下文就会弹出调用栈就会销毁了这个 ao 也因为没有指向也会被销毁掉当再执行时是重新创建了一个函数执行上下文把以上的过程再走一遍 六、函数调用函数的过程
我们在上面看了普通变量的执行过程和函数的执行过程其实发现很简单调用栈帮我们执行代码为了区分变量还是函数我们使用全局执行上下文栈和函数执行上下文栈帮助我们执行编译过程中定义了真正运行时才是会有赋值等操作。只有函数才有作用域当我们在一个函数里使用了当前作用域没有定义的变量而全局变量存在的变量时也可以使用是因为沿着作用域链来查找的。在函数定义的时候他的内存地址里存放两个东西一个是他的父级作用域和他的执行块
那我们现在看看函数调用函数的过程 其实也很简单在函数里调用函数时他发现有函数定义执行时会再次创建一个函数执行上下文栈用来存放新的函数这个函数的VO 同样存储两个东西一个是作用域父级作用域和当前作用域和代码块如上所以在函数里调用函数 也是按照作用域链查找变量所以与定义的位置无关
注意 我们以上的讲解都是基于早期的ECMA的版本规范也就es5之前的。也发现了所有的变量定义都是用的 var。 在早期的版本规定里是这样定义的 每个执行上下文都会被关联到一个变量环境VO: variable Object再源代码中的变量和函数都会被作为属性添加到VO中对于函数来说参数也会被添加到VO中 在最新的ECMA版本规范中进行了新的改动每一个执行上下文会关联到一个变量环境中(variableEnvironment)在执行代码中变量和函数的声明会作为环境记录添加到变量环境中对于函数来说参数也会被作为环境记录添加到变量环境中 通过上面的变化我们可以得知在最新的ECMA标准中我们前面的变量对象VO已经有另外一个称呼环境变量VE 最后、作用域与作用域链面试题
函数表达式遵循变量提升规则 今天我的表达还可以吗