尝鲜
下面两段代码的执行结果
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
};
确定 this 指向
- 在全局环境中,全局执行上下文中变量对象的 this 是指向 window 的
确认评论邮箱
邀请制评论