JavaScript函数执行过程解析

函数执行过程解析

以下图的代码为例,画了一个流程图
在这里插入图片描述

代码解析

编译阶段:

  1. 先创建GO(Global Object)全局作用域,里面包含 变量name函数foo
// GO(Global Object)
{
	name: undefined   // 初始值为undefined
	foo: 0xa00    // 指向函数内存地址
}
  1. 碰到函数foo时,会开辟出一块函数存储空间(如:0xa00)用来存储函数作用域以及函数执行体(代码块);

执行阶段:

  1. 从第一行代码开始执行var name = "why",从GO里面找到 name 属性,给它赋值 “why”
// GO(Global Object)
{
	name: "why"   // 赋值为why
	foo: 0xa00    // 指向函数内存地址
}
  1. 走到第3行代码foo(123)准备执行之前,创建AO(foo)对象用来存储函数foo内部的变量;遇见函数bar,再开辟出一块函数存储空间(如:0xb00)用来存储函数作用域以及函数执行体(代码块);
// AO (foo)
{
	num: undefined   // 初始值为undefined
	m: undefined   // 初始值为undefined
	n: undefined   // 初始值为undefined
	bar: 0xb00    // 指向函数内存地址
}
  1. 执行第3行代码,调用函数foo,传入参数 123;
    从AO(foo)里面找到num属性,赋值 123;
  // AO (foo)
  {
  	num: 123   // 赋值123
  	m: undefined   // 初始值为undefined
  	n: undefined   // 初始值为undefined
  	bar: 0xb00    // 指向函数内存地址
  }

走到第5行代码console.log(m)从AO(foo)取到m属性的值undefined,所以此处打印:undefined
第6,7行代码给m,n赋值

 // AO (foo)
 {
 	num: 123   // 赋值123
 	m: 10	// 赋值10
 	n: 20	//赋值20
 	bar: 0xb00    // 指向函数内存地址
 }
  1. 走到第13行代码bar()准备执行之前,创建AO(bar)对象用来存储函数bar内部的变量;由于函数bar内部没有变量,所以AO(bar)是空的
// AO (bar)
{
	
}
  1. 然后执行第13行代码bar(),调用函数bar,执行console.log(name)
    从AO(bar)寻找name属性,但是里面并没有name,所以继续向上从父作用域里查找,
    从AO(foo)里面寻找,也没有,继续向上从GO查找,找到了name属性,值是 why ,因此打印结果为:why
    原理:沿着作用域链一层一层查找(若是GO中也不存在该属性,则报错 name is not defined)
    bar函数执行完之后,函数执行上下文FEC(bar) 就会从ECStack调用栈中移除,接下来函数执行上下文FEC(foo)也也会移除

作用域提升练习题

var n = 100
function foo() {
  n = 200
}
foo()
console.log(n)  

打印结果:200;
解析:执行n=200时,先从函数作用域AO中寻找n,没找到,则从父作用域GO寻找,找到n,赋值为200;打印的n作用域是GO,从GO中寻找n,故结果是200

function foo() {
  console.log(n)
  var n = 200
  console.log(n)
}
var n = 100
foo()

打印结果:(1)undefined;(2)200;
解析:因为函数foo声明了n,所以在编译阶段AO:{ n:undefined };
第一次打印从AO中取值,此时是undefined;然后执行 var n = 200,给AO中的n赋值,AO:{ n:200 };
第二次打印从AO中取值,此时是200;

var n = 100
function f001() {
  console.log(n)
}
function foo2() {
  var n = 200
  console.log(n)
  foo1()
}
foo2()
console.log(n)

打印结果:从上到下 100;200;100;

var a = 100
function foo() {
  console.log(a)
  return
  var a = 100 
}
foo()

打印结果:undefined;
解析:代码编译时,只关心声明的变量,不管return,所以AO:{ a:undefined };
执行时,打印的 a 从AO取值,故结果是undefined;而return后面的代码不会执行

function foo() {
  var m = 100
}
foo()
console.log(m)  

打印结果:抛异常 m is not defined;
解析:因为var m = 100是定义在函数作用域AO中的,而打印的m作用域是GO,所以找不到m

function foo() {
  m = 100
}
foo()
console.log(m) 

打印结果:100;
解析js引擎对这种语法(m = 100)进行特殊处理,默认将 m 定义在全局对象GO中

function foo() {
  var a = b = 100
}
foo()
console.log(a)
console.log(b)

打印结果:抛异常 a is not defined;因为前面抛异常,所以console.log(b)不会打印
解析var a = b = 100会转成以下两行代码:var a = 10 b = 10;赋值顺序是从右到左,先给b赋值,后给a赋值;
所以 a 属于函数作用域AO,b属于全局作用域GO;
而打印的 a 从GO里找不到a,所以抛异常;正常情况下,b会打印100

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值