Function执行原理 & 闭包

简介: Execution Context 执行期上下文 在java或c语言中,都有块级作用域这个概念,而js中则没有。 在js中,作用域只有一种,即函数级作用域。 而执行期上下文,可以理解为函数的作用域或执行环境。

Execution Context 执行期上下文

在java或c语言中,都有块级作用域这个概念,而js中则没有。

在js中,作用域只有一种,即函数级作用域。

而执行期上下文,可以理解为函数的作用域或执行环境。

在代码层面,执行期上下文是嵌套存在的

15314682222249

在js引擎内,执行期上下文是以栈的形式进行存放

js引擎中存放

栈的最底部存放的global上下文,每次执行一个函数,则会创建一个上下文放入栈中,执行结束后再pop移除。

(function foo(i) {
   if (i === 3) {
       return;
   }
   else {
       foo(++i);
   }
}(0));

当前的执行环境则永远使用存放在栈顶的上下文对象。

1111111

参考博文:

Function 执行原理

js中有在function上面有很多的用法和概念,就比如作用域链,闭包这些。

其实问题归结到了根本,都在于Function在执行时做了什么。

这个是以下内容的一个思维导图:

15314687305329

以Function的生命周期划分,分为:创建阶段执行阶段

这里从执行阶段开始,以 执行函数内部代码 为时间点分成3个阶段:

  • 执行前
  • 执行时
  • 执行后

执行前

当执行一个函数时,会创建一个函数的执行环境,即执行期上下文对象(execution context)。

ExecutionContext = {
    VO:{
        // Variable Object(全局上下文独有)

        // 变量 (var, 变量声明);
        // 函数声明 (FunctionDeclaration, 缩写为FD);
    }, 
    AO:{
        // Activation Object (函数上下文独有)

        // 变量 (var, 变量声明);
        // 函数声明 (FunctionDeclaration, 缩写为FD);
        // arguments
    },
    scopeChain:{..},
    this:{..}
}

该对象主要做了三件事情:

  1. 确定函数内所有的变量 (AO | VO)

    • AO:通过上下文栈中的当前上下文,获取参数,创建arguments对象
    • AO & VO:扫描代码,创建所有函数声明(hoist作用域提升的原因,这些函数将进入创建阶段)和变量声明(值为undefined)
  2. 创建作用域链 (scopeChain = (AO | VO) + [[Scope]])
  3. 确定this指向 (由当前所处的执行期上下文提供)

VO & AO

VO 和 AO 的作用是存储函数中执行时需要用到的所有函数和变量。

执行期上下文分为两种:

  • 全局上下文 (global,在上下文栈最底部,这个对象只存在一份,它的属性在程序中任何地方都可以访问)
  • 函数上下文 (每次在函数调用时进行创建)

这两种上下文的区别就在于其创建方式和VO的访问性。

全局上下文的VO可以直接访问,VO对象指向的是global自身

var name = {};

name === this.name // true

name === window.name // true

函数上下文的VO不能直接访问,创建活动对象AO来代替VO。

因此函数上下文只有AO,VO没有。

创建作用域

函数生命周期分为两个阶段:

  • 创建阶段 (创建 [[scope]]
  • 执行阶段 (创建 scopeChain

在函数创建阶段,[[scope]]就已经被创建了。

function say(){
  var words = "hello";

  hello(); // 输出:hello

  function hello(){
    console.log(words);
  }
}

say.[[scope]] = [
  GlobalExecutionContext.VO
]

遵循 [[scope]] = superExecutionContext.scopeChain + superFn.[[scope]] 这个规则。

该变量[[scope]]是一种静态变量,一直存在,直至函数被delete或垃圾回收。

进入执行阶段,执行期上下文初始化作用域链scopeChain。

say.ExecutionContext = {
  AO : {
    words : undefined,
    
    arguments : [...]
  },
  scopeChain : say.[[scope]].concat(this.AO)
}

say.ExecutionContext = {
  AO : {
    words : undefined,
    
    arguments : [...]
  },
  scopeChain : [
    
    say.ExecutionContext.AO, // 创建自身活动对象

    GlobalExecutionContext.VO

  ]
}

遵循 scopeChain = (AO | VO) + [[Scope]] 这个规则。

闭包也遵循这个规则,在say函数执行时,内部函数hello进入创建阶段。

hello.[[scope]] = [

    say.scopeChain, 

    GlobalExecutionContext.VO
]

在执行hello()时,进入执行阶段

hello.ExecutionContext = {
  
  AO : {
    arguments : [...]
  },

  scopeChain : [

    hello.ExecutionContext.AO, 

    say.scopeChain, 

    GlobalExecutionContext.VO

  ]
}

参考博文:

确定this执向

this由该函数的执行环境所确定

function person = {
  say : function(){
    console.log(this);
  }
}

person.say() // this指向person对象

var say = person.say;

say() // this指向window

参考博文:

执行时

函数的内部代码执行时,由于在执行前将函数声明(function关键字)和变量声明(var关键字)全部创建到了AO中。

因此会存在一种hoist,即作用域提升的问题。

执行后

在函数的内部代码执行后,会销毁函数的执行期上下文。

与此同时AO也将被销毁,除非有引用的情况。

function say(){
  var words = "hello";

  hello(); // 输出:hello

  function hello(){
    console.log(words);
  }

  return hello; // 将hello抛出
}

var hello = hello.say();

此时的作用域情况如下

window.hello.[[scope]] = [
  say.scopeChain : [

    say.ExecutionContext.AO,

    GlobalExecutionContext.VO

  ]
]

由于 hello.[[scope]] 中留有 say.scopeChainsay.ExecutionContext.AO 的引用,所有不会被删除。

相关文章
|
机器学习/深度学习 存储 人工智能
大语言模型的预训练[5]:语境学习、上下文学习In-Context Learning:精调LLM、Prompt设计和打分函数(Scoring Function)设计以及ICL底层机制等原理详解
大语言模型的预训练[5]:语境学习、上下文学习In-Context Learning:精调LLM、Prompt设计和打分函数(Scoring Function)设计以及ICL底层机制等原理详解
大语言模型的预训练[5]:语境学习、上下文学习In-Context Learning:精调LLM、Prompt设计和打分函数(Scoring Function)设计以及ICL底层机制等原理详解
|
JavaScript API 定位技术
百度地图API详解之事件机制,function“闭包”解决for循环和监听器冲突的问题:
原文: 百度地图API详解之事件机制,function“闭包”解决for循环和监听器冲突的问题:   百度地图API详解之事件机制 2011年07月26日 星期二 下午 04:06 和DOM编程里的事件模型一样,百度地图API也提供了类似的事件机制。
1626 0
|
6月前
|
人工智能 Python
083_类_对象_成员方法_method_函数_function_isinstance
本内容主要讲解Python中的数据类型与面向对象基础。回顾了变量类型(如字符串`str`和整型`int`)及其相互转换,探讨了加法在不同类型中的表现。通过超市商品分类比喻,引出“类型”概念,并深入解析类(class)与对象(object)的关系,例如具体橘子是橘子类的实例。还介绍了`isinstance`函数判断类型、`type`与`help`探索类型属性,以及`str`和`int`的不同方法。最终总结类是抽象类型,对象是其实例,不同类型的对象有独特运算和方法,为后续学习埋下伏笔。
114 7
083_类_对象_成员方法_method_函数_function_isinstance
|
6月前
|
Python
[oeasy]python086方法_method_函数_function_区别
本文详细解析了Python中方法(method)与函数(function)的区别。通过回顾列表操作如`append`,以及随机模块的使用,介绍了方法作为类的成员需要通过实例调用的特点。对比内建函数如`print`和`input`,它们无需对象即可直接调用。总结指出方法需基于对象调用且包含`self`参数,而函数独立存在无需`self`。最后提供了学习资源链接,方便进一步探索。
122 17
|
6月前
|
人工智能 Python
[oeasy]python083_类_对象_成员方法_method_函数_function_isinstance
本文介绍了Python中类、对象、成员方法及函数的概念。通过超市商品分类的例子,形象地解释了“类型”的概念,如整型(int)和字符串(str)是两种不同的数据类型。整型对象支持数字求和,字符串对象支持拼接。使用`isinstance`函数可以判断对象是否属于特定类型,例如判断变量是否为整型。此外,还探讨了面向对象编程(OOP)与面向过程编程的区别,并简要介绍了`type`和`help`函数的用法。最后总结指出,不同类型的对象有不同的运算和方法,如字符串有`find`和`index`方法,而整型没有。更多内容可参考文末提供的蓝桥、GitHub和Gitee链接。
127 11
|
12月前
|
JavaScript
箭头函数与普通函数(function)的区别
箭头函数是ES6引入的新特性,与传统函数相比,它有更简洁的语法,且没有自己的this、arguments、super或new.target绑定,而是继承自外层作用域。箭头函数不适用于构造函数,不能使用new关键字调用。
|
11月前
|
中间件 Docker Python
【Azure Function】FTP上传了Python Function文件后,无法在门户页面加载函数的问题
通过FTP上传Python Function至Azure云后,出现函数列表无法加载的问题。经排查,发现是由于`requirements.txt`中的依赖包未被正确安装。解决方法为:在本地安装依赖包到`.python_packages/lib/site-packages`目录,再将该目录内容上传至云上的`wwwroot`目录,并重启应用。最终成功加载函数列表。
115 0

热门文章

最新文章