在出栈函数的形参_JavaScript函数的存储机制(堆)和执行原理(栈)

effa9b619e0cc41d5a03288afcd294eb.png

JavaScript函数

为什么JavaScript中要设计函数的概念?

程序的本质是解决问题,通过逐条语句的编写可以实现目标。但是,当遇到重复的问题时,再实现一遍逻辑就会使得程序变得臃肿,所以我们需要将相同的问题抽离、包装,并在需要时,能够以最简单的方式使用重复逻辑
JavaScript 函数就是被设计为执行特定代码的代码块。
function add(a,b){
    return a + b;
}
add(1,2);
add(2,2);

函数的基本概念

函数的定义

  1. 函数式声明
function add(a,b){  // a 和 b 都是 add 函数的形参
    return a + b;   // return 的值就是函数的执行结果,没有 return 时函数的执行结果为 undefined
}
  1. 函数表达式
var add = function(a,b){
    return a + b;
}
  1. 函数生成器声明
function * add(a,b){
    yield a + b;
}
  1. 箭头函数
add = (a,b) => {
    return a + b;
}
  1. Function构造函数
add = new Function(a,b){
    return a + b;
}

函数的参数

  • arguments对象
在除箭头函数以外的方式来定义函数,函数的参数都会自动挂载到 arguments对象上(不论是否传递形参,都会按顺序将参数挂载上去)
  • 剩余运算符
箭头函数中没有 arguments对象,但是某些情况下我们是不知道箭头函数传递进来的参数的具体数量,这时候就可以利用剩余运算符将参数收集
add = (...args) => {    // ... 剩余运算符将参数收集到 args 中,并以数组的形式存储
    return args.reduce((total,item)=>{  // reduce 遍历数组,并接收一个函数作为累加器
        return total + item;
    })
}

函数的返回值

return 语句不但用于返回函数的计算结果,当函数执行到 return时,就会立即停止函数的执行(不论return的下一的语句是什么)
function add(a,b,c){
    return a + b;   // 函数执行到这一步时,会直接将 a+b 的结果返回,并立即结束当前函数的执行
    return b + c;   // 该语句不会被执行
}
add(1,1,1)

函数体中没有return的时候,会将函数体中的所有代码都执行一遍,并返回undefined

JS函数浏览器中的存储(堆)

浏览器在读取到函数时,并非直接将函数体中代码执行,而是会将函数以字符串的形式存储在浏览器的内存中,这个过程浏览器会分配一块堆内存用于函数的存储,并记录函数存储的位置(16进制的地址)

66af38e6d40e9dd6dd59df9b130b8c9a.png

JS函数的运行机制(栈)

JavaScript的运行环境

  • 浏览器(不同厂家对应不同的引擎)
  • node(V8引擎)
  • webview(V8引擎)

JS代码的执行环境--ECStack

  • ECStack(Execution Context Stack)
ECStack 执行环境栈,它也是浏览器从内存中拿出的一块内存,但是是栈结构内存,作用是执行代码,而非保存代码。
  • EC(Execution Context)
EC 执行上下文, EC不同于 ECStack它虽然同样是栈内存,但是它的作用是为了在函数执行时,区分全局和函数作用域执行所处的不同作用域(保障每个词法作用域下的代码的独立性)
  • GO(Global Object)
浏览器会将供JS调用的属性和方法存放在 GO中(内置对象),同时浏览器会声明一个名为 window的属性指向这个对象
  • AO & VO (Active Object & Variable Object)
代码执行时,会创建变量, VO就是用于存储这些变量的空间。 VO通常用于存放全局变量(全局变量一般情况下是不被释放的),而函数执行或其他情况下形成的私有上下文的变量存储在 AO中, AOVO的一种,区别是 AO通常指的是进出栈较为频繁的对象

6f8570b98b8d7d33f88918b6e7f26675.png

函数的创建和执行

函数创建时,会从内存中新建一块堆内存来存储代码块。在函数执行时,会从堆中取出代码字符串,存放在新建的栈中

创建函数

  1. 开辟堆内存(16进制得到内存地址)
  2. 声明当前函数的作用域(函数创建的上下文才是他的作用域,和在那执行的无关)
  3. 把函数的代码以字符串的形式存储在堆内存中(函数再不执行的情况下,只是存储在堆内存中的字符串)
  4. 将函数堆的地址,放在栈中供变量调用(函数名)

执行函数

  1. 会形成一个全新的执行上下文EC(xx)(目的是供函数体中的代码执行),然后进栈(ECStack执行环境栈)执行
  2. 在私有上下文中有一个存放变量的变量对象AO(xx)
  3. 代码执行之前需要做的事
  • 初始化作用域链<自己的上下文,函数的作用域>
  • 初始化this(箭头函数没有this)
  • 初始化arguments实参集合(箭头函数没有arguments)
  • 形参赋值(形参变量是函数的私有变量,需要存储在AO中)
  • 变量提升(在私有上下文中声明的变量都是私有变量)
  1. 代码执行(把之前在函数堆中存储的字符串拿过来在当前上下文中执行)
  2. 根据实际情况判断当前上下文是否出栈释放
作用域链的查找机制,在代码执行过程中,遇到一个变量,首先查看是否是自己的私有变量,如果是自己的私有变量,就直接操作私有变量,如果不是自己的私有变量,则按照 scope-chain,向上级上下文查找,如果上级有则拿过来操作,没有则一直向上查找,直到EC(全局)
var x = [1,2]
function fn(y){
    y[0] = 100;
    y = [100];
    y[1] = 200;
    console.log(y);
}
fn(x);
console.log(x);

ffbe2edabe32cd1ad36b715c5fa1e271.png
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值