关于前端:JavaScript-的函数底层运行机制

45次阅读

共计 2654 个字符,预计需要花费 7 分钟才能阅读完成。

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 代码,会开拓一块栈内存来作为执行环境:ECStack(Execution Context Stack)

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

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


△ 图 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)第二题,简图

△ 图 2.2_函数执行

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

(2)创立函数

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

① 独自开拓一个堆内存:16 进制地址,函数堆内存中存储的是函数体中的 <u> 代码字符串 </u>

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

③ 把 16 进制地址(16 进制以 0x 结尾)寄存到栈中,供函数名变量名关联援用即可

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

函数执行 的目标:把创立函数的时候在堆内存中存储的 <u> 代码字符串 </u> 变为代码执行

代码执行肯定会有一个执行的环境,它的下级执行上下文,是函数创立的中央

函数执行会造成一个全新的、公有的执行上下文,在公有上下文中,也有寄存本人变量的对象: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++ 后加

△ 图 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、实参、形参、作用域链


△ 图 2.5_练习题,第一题

正文完
 0