尝鲜
下面两段代码的执行结果
console.log(person); console.log(fun);var person = 'koa'; console.log(person); function fun() { console.log(person);var person = 'express'; console.log(person);}fun(); console.log(person);// 结果// undefined// 函数// koa// undefined// express// koa
console.log('step1'); function first() { console.log('step2');second(); console.log('step3');} function second() { console.log('step4');}first(); console.log('step5');// 结果// step1// step2// step4// step3// step5
运行机制
js 执行是单线程的,是基于事件循环的,事件循环分为下面几个步骤
所有同步任务都在主线程上执行,形成一个执行栈
主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件
一旦执行栈中所有的同步任务执行完毕,系统就会读取任务队列,看看有哪些事件,那么对应的异步任务结束等待状态,进入执行栈,开始执行
主线程不断重复第三步
主线程的执行过程就是一个 tick,所有的异步结果都是通过任务队列来调度,消息队列中存放着一个个 task,task 分为 macro task 和 micro task,常见的 macro task 分为 setTimeout,MessageChannel,postMessage,setImmediate,常见的 micro task 有 MutationObserver 和 Promise.then
执行过程
语法分析
分析 js 代码块语法是否正确,不正确抛出语法错误,停止该 js 代码块的执行,然后加载下一个代码块,语法正确,则进入预编译阶段
预编译阶段
js 运行环境
全局环境
js 代码块加载完毕,并且没有语法错误,则进入全局环境
函数环境
函数调用执行时,进入函数环境,不同函数则函数环境不同
函数调用栈:先进后出,后进先出
每次进入不同环境,会创建一个对应的上下文,在一段 js 程序中,会创建多个执行上下文,js 引擎会创建一个函数调用栈对这些执行上下文进行处理,栈底永远是全局执行上下文,栈顶永远是当前执行上下文
function bar() {var name = 'koa';
function foo() {
var age = '22';}foo();}bar();进入全局环境,创建全局执行上下文
调用 bar 函数,进入 bar 函数运行环境,创建 bar 函数上下文
在 bar 函数内部调用 foo 函数,进入 foo 函数运行环境,创建 foo 函数执行上下文
栈底是全局执行上下文,栈顶是 foo 函数执行上下文,foo 函数内部没有再调用其他函数,进行出栈操作
foo 函数执行完毕后,栈顶 foo 执行上下文,首先出栈
bar 函数执行完毕后,bar 函数执行上下文出栈
globar 在浏览器或者标签页关闭时候出栈
创建执行上下文
在全局环境中,window 对象就是全局执行的上下文变量对象,所有变量和函数都是 window 对象的属性或方法,函数声明提前和变量声明提升时古在创建变量对象中进行的,函数声明优先级高于变量声明
// 创建上下文function fun(a, b) {var num = 1; function test() { console.log(num);}}fun(2, 3); <!--创建的结果-- > funEC = {//变量对象VO: { //arguments对象 arguments: { a: undefined, b: undefined, length: 2 }, //test函数 test: < test reference > , //num变量 num: undefined},//作用域链scopeChain: [],//this指向this: window };
创建变量对象
创建 arguments 对象,建立该对象的属性与属性值,仅在函数环境中执行,箭头函数除外,全局环境没有此过程
检查当前上下文的函数声明,按照代码顺序查找,将找到函数提前声明,如果当前上下文的变量对象没有该函数名属性,则需要在该变量对象以函数名创建一个属性,属性值为指向该函数所在堆内存地址的引用,如果引用存在,会被覆盖
查当前上下文的变量声明,将找到的变量提前声明,如果没有变量属性,则为该变量创建一个属性,属性值为 undefined,如果存在,忽略变量声明
创建作用域链
作用域链的第一项永远是当前作用域,就是 innerTest,最后一项永远是全局作用域,查找方式是从左到右,直到找到全局作用域,如果再找不到,就报错
作用域链由当前执行环境的变量对象与上层环境的活动对象组成,保证变量的权限访问和函数有序访问
// 创建作用域链var num = 30;
function test() {var a = 10;
function innerTest() {
var b = 20;
return a + b;}innerTest();}test();// 创建结果
innerTestEC = {//变量对象VO: {
b: undefined},//作用域链scopeChain: [VO(innerTest), AO(test), AO(global)],//this指向this: window
};
确认评论邮箱
邀请制评论