JavaScript 的函数底层运行机制

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

0 / 题
(1)第一题
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x);
console.log(b);

△ 引用数据类型:object

(2)第二题
var x = [12, 23];
function fn(y) {
    y[0] = 100;
    y = [100];
    y[1] = 200;
    console.log(y);
}
fn(x);
console.log(x);

△ 引用数据类型:function

这些题是不是很简单?我们主要看逻辑:

1 / 引用数据类型:object

在Web浏览器中执行JS代码,会开辟一块栈内存来作为执行环境:ECStackExecution Context Stack

会开辟一块栈内存供全局代码执行:全局执行上下文 EC(G)Execution Context Global),还有其他的上下文:函数私有执行上下文、块级私有上下文…… 自己管好自己那一摊的代码执行内容

形成的执行上下文都会 进栈 到执行环境栈中运行.私有上下文会在不被占用时出栈释放,浏览器的回收机制GC.当浏览器关闭时,全局执行上下文就会出栈释放了

公众号:朝霞的光影笔记 ID:zhaoxiajingjing
△ 图2.1_第一题,简图

GO:全局对象 Global Object ,并不是VO(G)全局变量对象 Variable Object Global

全局对象,它是个对象,它就是个堆内存,浏览器打开一加载页面就默认开辟的堆内存。

浏览器提供的一些供JS调用的API,在Web浏览器中,全局对象可以通过window来访问的

注意:运算符优先级,要多看看多比划比划

注意基本数据类型值直接存储在栈内存中,引用数据类型值存在堆内存

2 / 引用数据类型:function
var x = [12, 23];
function fn(y) {
    y[0] = 100;
    y = [100];
    y[1] = 200;
    console.log(y);
}
fn(x);
console.log(x);

△ 函数执行

(1)第二题,简图

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

△ 图2.2_函数执行

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

△ 图2.3_数组的格式:键值对

(2)创建函数

创建函数的步骤:【和创建变量区别不是很大,函数名就是变量名】

① 单独开辟一个堆内存:16进制地址,函数堆内存中存储的是函数体中的代码字符串

② 创建函数的时候,就声明了它的作用域[[scope]],也就是所在的上下文环境

③ 把16进制地址(16进制以0x开头)存放到栈中,供函数名变量名关联引用即可

只创建函数,不执行函数,没啥意义,那就是一堆字符串。

函数执行的目的:把创建函数的时候在堆内存中存储的 代码字符串 变为代码执行

代码执行一定会有一个执行的环境,它的上级执行上下文,是函数创建的地方

函数执行会形成一个全新的、私有的执行上下文,在私有上下文中,也有存放自己变量的对象:AO(Active Object 活动对象),它是VO的一种。

变量对象: ① 在全局上下文中:VO ② 在私有上下文中:AO

实参都是值。形参是变量。

fn(x):执行函数fn,把全局上下文中存储的x变量关联的值(0x000001),作为实参传递给函数的形参变量

(3)执行函数

执行函数做了哪些事情:

1、形成了一个全新的、私有的执行上下文EC(xxx)

2、当前私有的上下文中,有一个存放此上下文内声明的变量的地方 AO(xxx) 私有变量对象

① 形参变量

② 当前上下文中声明的变量

3、进栈执行

4、代码执行之前还要处理很多事情:

① 初始化作用域链

[[scope-chain]]:<当前自己的上下文, 上级上下文(创建函数时形成的作用域)>

作用域链有两头,一头是自己执行的上下文,另一头是自己创建时所在的上下文

即:当前函数的上级上下文是创建函数所在的上下文,就是作用域

以后再遇到函数内的代码执行,遇到一个变量,首先看是否为自己上下文中的私有变量(看AO中有没有,有,是自己私有的;没有,不是自己私有的)。如果是私有的变量,则当前变量的操作和外界环境中的变量互不干扰(没有直接关系);如果不是自己的私有变量,则按照作用域链,查找是否为其上级上下文中的私有变量…一直找到EC(G)全局上下文为止:作用域链查找机制

② 初始化this…

③ 初始化arguments…

④ 形参赋值:形参都是私有变量,放在AO中的。如果不传递实参,默认值是undefined

⑤ 变量提升…

5、代码自上而下执行

6、

7、一般情况下,函数执行所形成的私有上下文,进栈执行完后,会默认出栈释放掉

【私有上下文中存储的私有变量和一些值都会被释放掉,目的:为了优化内存空间,减少栈内存的消耗,提高页面或者计算机的处理速度…】

不能出栈释放:当前上下文中某些内容(一般是堆内存地址)被当前上下文的外部的事物占用了,则无法出栈释放。一旦被释放,后期外部事物就无法找到对应的内容了

注意: 多次函数执行,会形成多个全新的、私有执行上下文,这些上下文之间没有直接的关系

(4)闭包

一般,很多人认为:大函数返回小函数是闭包。

这只是闭包机制中的一种情况。

闭包:函数执行形成一个私有的执行上下文,此上下文中的私有变量,与此上下文以外的变量互不干扰;也就是当前上下文把这些变量保护起来了,我们把函数的这种保护机制称为闭包。

闭包不是具体的代码,而是一种机制。

一般情况下,形成的私有上下文很容易被释放掉,这种保护机制存在时间太短了,不是严谨意义上的闭包。有人认为,形成的上下文不被释放,才是闭包。此时,不仅保护了私有变量,而且这些变量和存储的值也不会被释放掉,保存起来了。

闭包的作用:① 保护 ② 保存

利用闭包的两个作用,可以实现高阶编程技巧,以后再说~

3 / 练习题
(1)第一题
var x = 100;
function fn() {
    var x = 200;
    return function(y) {
        console.log(y + x++);
    }
}
var f = fn();
f(10);
f(20);

△ 第一题

i++ 后加

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

△ 图2.4_后加

(2)第二题
let a=0,
    b=0;
function A(a){
    A=function(b){
        alert(a+b++);
    };
    alert(a++);
}
A(1);
A(2);

△ 第二题

(3)第三题
let x = 5;
function fn(x) {
    return function(y) {
        console.log(y + (++x));
    }
}
let f = fn(6);
f(7);
fn(8)(9);
f(10);
console.log(x);

△ 第三题

- end -

好啦,好啦,碎碎念了很多:

全局执行上下文、创建函数、作用域、执行函数、私有执行上下文、AO和VO、实参、形参、作用域链

公众号:朝霞的光影笔记 ID:zhaoxiajingjing
△ 图2.5_练习题,第一题

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值