尝鲜 下面两段代码的执行结果 运行机制 js 执行是单线程的,是基于事件循环的,事件循环分为下面几个步骤 所有同步任务都在主线程上执行,形成一个执行栈 主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件 一旦执行栈中所有的同步任务执行完毕,系统就会读取任务队列,看看有哪些事件,那么对应的异步任务结束等待状态,进入执行栈,开始执行 主线程不断重复第三步 主线程的执行…

尝鲜

下面两段代码的执行结果

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 执行是单线程的,是基于事件循环的,事件循环分为下面几个步骤

  1. 所有同步任务都在主线程上执行,形成一个执行栈
  2. 主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件
  3. 一旦执行栈中所有的同步任务执行完毕,系统就会读取任务队列,看看有哪些事件,那么对应的异步任务结束等待状态,进入执行栈,开始执行
  4. 主线程不断重复第三步

运行机制

主线程的执行过程就是一个 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();
  1. 进入全局环境,创建全局执行上下文
  2. 调用 bar 函数,进入 bar 函数运行环境,创建 bar 函数上下文
  3. 在 bar 函数内部调用 foo 函数,进入 foo 函数运行环境,创建 foo 函数执行上下文
  4. 栈底是全局执行上下文,栈顶是 foo 函数执行上下文,foo 函数内部没有再调用其他函数,进行出栈操作
  5. foo 函数执行完毕后,栈顶 foo 执行上下文,首先出栈
  6. bar 函数执行完毕后,bar 函数执行上下文出栈
  7. 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 的

暂无评论