前端基石:构造函数和普通函数

简介: 在上面的代码中,一个 Fn 函数有两种命运,普通函数执行,new 构造函数执行。那这两种执行方法有什么区别了?简单来理解,普通函数执行,是把 Fn 函数执行的结果返回给 f1。构造函数执行,把 Fn 当做一个类,是把创造的一个实例返回,f2 是创造出来的一个实例。本文将从执行流程中为大家分享这两类函数的区分。

网络异常,图片无法展示
|

前言


function Fn() {
  // ...
}
let f1 = Fn();
let f2 = new Fn();


在上面的代码中,一个 Fn 函数有两种命运,普通函数执行,new 构造函数执行。那这两种执行方法有什么区别了?


简单来理解,普通函数执行,是把 Fn 函数执行的结果返回给 f1。构造函数执行,把 Fn 当做一个类,是把创造的一个实例返回,f2 是创造出来的一个实例。

本文将从执行流程中为大家分享这两类函数的区分。


代码执行前奏


全局执行上下文进栈执行


  1. 初始化变量对象(VO)
  2. 初始化作用域链<<window, ->>
  3. 初始化 this
  4. 初始化 arguments(没有)
  5. 形参赋值(没有)
  6. 变量提升,Fn 变量提升,声明 + 定义(let 没有变量提升,所以 f1、f2 不会声明也不会在这个阶段被定义)
  7. 代码执行


全局上下文代码执行


  1. Fn 不做任何操作,在上一步已经被声明+ 定义
  2. f1 = Fn(),普通函数执行,函数执行创建一个新的执行上下文进栈执行
  3. f2 = new Fn(),构造函数执行,大多数构造函数执行和普通函数执行是一样的,但是有几点不太一样。

为了方便区分,下文中 Fn(f1) 指代的是普通函数 Fn 执行。Fn(f2) 指代的是 new Fn()执行。


普通函数执行


Fn(f1)进栈执行


  1. Fn(f1) 私有上下文进栈执行
  2. 初始化变量对象 AO(f1)
  3. 初始化作用域链<<EC(f1), EC(G)>>
  4. 初始化 this
  5. 初始化 arguments
  6. 形参赋值
  7. 变量提升
  8. 代码执行 ...
  9. 函数由于没有返回,所以默认返回 undefined,f1 被赋值为 undefined ,在全局的 VO(G) 中 f1 = undefined
  10. 函数执行完成出栈


存储示意图


网络异常,图片无法展示
|


Fn(f2) 进栈执行


  1. Fn(f2) 私有上下午进栈执行
  2. 初始化变量对象 AO(f2)
  3. 在构造函数执行,初始化作用域链之前,浏览器会默认先创建一个对象(空对象,Fn 的实例对象)
  4. 初始化作用域链<<EC(f2), EC(G)>>
  5. 初始化 this,这里的初始化 this,会将 this 指向第三步创建的对象,所以在后期代码中执行 this.xx = xx 的时候,实际上就是在往这个对象(实例对象)上添加属性或者方法,这里还需要注意下,构造函数执行,函数内部的私有变量和实例是没有关系的。
  6. 初始化 arguments
  7. 形参赋值
  8. 变量提升
  9. 代码执行
  10. 函数执行完,出栈之前,会查看构造函数本身的返回结果:
  1. 如果有 「return 对象」,则以返回值为主,f2 就会等于这个返回值对象。
  2. 如果没有返回值或者返回的是一个原始值,则浏览器默认会将创建的实例返回,f2 就会等于这个实例对
  1. 函数出栈


存储示意图


网络异常,图片无法展示
|


总结


JavaScript 这一门语言本身是基于类和实例构建和组成的。当我们将一个函数当做构造函数执行时和普通函数有大致三点不同:


  • 在构造函数执行,初始化作用域链之前,浏览器会默认先创建一个对象(空对象,Fn 的实例对象)。
  • 初始化 this,这里的初始化 this,会将 this 指向第三步创建的对象,所以在后期代码中执行 this.xx = xx 的时候,实际上就是在往这个对象(实例对象)上添加属性或者方法,这里还需要注意下,构造函数执行,函数内部的私有变量和实例是没有关系的。
  • 函数执行完,出栈之前,会查看构造函数本身的返回结果:
  1. 如果有 「return 对象」,则以返回值为主,f2 就会等于这个返回值对象。
  2. 如果没有返回值或者返回的是一个原始值,则浏览器默认会将创建的实例返回,f2 就会等于这个实例对
目录
相关文章
|
4月前
|
前端开发 JavaScript 开发者
揭秘JavaScript魔法三剑客:call、apply、bind,解锁函数新世界,你的前端之路因它们而精彩!
【8月更文挑战第23天】在 JavaScript 的世界里,`call`、`apply` 和 `bind` 这三个方法常常让新手感到困惑。它们都能改变函数执行时的上下文(即 `this` 的指向),但各有特点:`call` 接受一系列参数并直接调用函数;`apply` 则接收一个参数数组,在处理不确定数量的参数时特别有用;而 `bind` 不会立即执行函数,而是创建一个新版本的函数,其 `this` 上下文已被永久绑定。理解这三个方法能帮助开发者更好地运用函数式编程技巧,提升代码灵活性和可维护性。
42 0
|
2月前
|
存储 监控 前端开发
掌握微前端架构:构建未来前端应用的基石
【10月更文挑战第12天】随着前端技术的发展,传统的单体应用架构已无法满足现代应用的需求。微前端架构通过将大型应用拆分为独立的小模块,提供了更高的灵活性、可维护性和快速迭代能力。本文介绍了微前端架构的概念、核心优势及实施步骤,并探讨了其在复杂应用中的应用及实战技巧。
|
2月前
|
前端开发 JavaScript
前端:实现一个 sleep 函数
在前端开发中,实现一个 `sleep` 函数可以用来暂停代码执行,模拟延迟效果,常用于测试或控制异步操作的节奏。该函数通常基于 `Promise` 和 `setTimeout` 实现,简单易用。
|
3月前
|
存储 前端开发 JavaScript
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
本文介绍了JavaScript中作用域的概念,包括全局变量和局部变量的区别,预解析机制(变量提升),以及函数返回值的使用和类型。通过具体示例讲解了变量的作用域、函数的返回值、以及如何通过return关键字从函数中返回数据。
25 1
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
|
2月前
|
JSON 前端开发 JavaScript
构建现代前端应用的基石
【10月更文挑战第13天】构建现代前端应用的基石
|
3月前
|
存储 前端开发 JavaScript
前端基础(十一)_函数声明及调用、函数的形参与实参、arguments参数、函数的参数类型、函数中的问题
本文介绍了JavaScript中函数的声明及调用、形参与实参的概念、arguments对象的使用、函数参数的类型以及函数中this的作用。通过示例代码详细解释了函数如何接收参数、如何处理参数个数不匹配的情况,以及函数在不同上下文中this的指向。
29 1
|
4月前
|
JSON 前端开发 API
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
|
3月前
|
JavaScript 前端开发
前端JS函数
【9月更文挑战第4天】前端JS函数
29 6
|
5月前
|
开发框架 前端开发 JavaScript
循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中
循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中
|
5月前
|
开发框架 JSON 前端开发
循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数
循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数