加入收藏 | 设为首页 | 会员中心 | 我要投稿 西双版纳站长网 (https://www.0691zz.com.cn/)- 数据计算、IT业界、服务器、教程、云日志!
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

你对JavaScript了解多少?大神详细解读JavaScript的框架结构

发布时间:2019-08-01 13:23:03 所属栏目:优化 来源:程序员私房菜
导读:对于任何一个程序员来说,最关注的两个问题无非就是:时间复杂度和空间复杂度。第一部分介绍了 V8 为改进 JavaScript 执行时间所做的速度提升和优化,第二部分则将着重介绍内存管理方面的知识。 这篇文章,小编简要概述了编程语言的一般工作机制,并深入探
副标题[/!--empirenews.page--]

对于任何一个程序员来说,最关注的两个问题无非就是:时间复杂度和空间复杂度。第一部分介绍了 V8 为改进 JavaScript 执行时间所做的速度提升和优化,第二部分则将着重介绍内存管理方面的知识。

你对JavaScript了解多少?大神详细解读JavaScript的框架结构

这篇文章,小编简要概述了编程语言的一般工作机制,并深入探讨了 V8 引擎的管道。第二部分将介绍一些更重要的概念,这些概念是每一个 JavaScript 程序员都必须了解的,并且不仅仅和 V8 引擎有关。

内存堆

你对JavaScript了解多少?大神详细解读JavaScript的框架结构

Orinoco 的 logo:V8 的垃圾回收器

每当你在 JavaScript 程序中定义了一个变量、常量或者对象时,你都需要一个地方来存储它。这个地方就是内存堆。

当遇到语句 var a = 10 的时候,内存会分配一个位置用于存储 a 的值

可用内存是有限的,而复杂的程序可能有很多变量和嵌套对象,因此合理地使用可用内存非常重要。

和诸如 C 这种需要显式分配和释放内存的语言不同,JavaScript 提供了自动垃圾回收机制。一旦对象/变量离开了上下文并且不再使用,它的内存就会被回收并返还到可用内存池中。

在 V8 中,垃圾回收器的名字叫做 Orinoco,它的处理过程非常高效。这篇文章有相关解释

标记与清除算法

你对JavaScript了解多少?大神详细解读JavaScript的框架结构

标记和清除算法

我们通常会使用这种简单有效的算法来判定可以从内存堆中安全清除的对象。算法的工作方式正如其名:将对象标记为可获得/不可获得,并将不可获得的对象清除。

垃圾回收器周期性地从根部或者全局对象开始,移向被它们引用的对象,接着再移向被这些对象引用的对象,以此类推。所有不可获得的对象会在之后被清除。

内存泄漏

虽然垃圾回收器很高效,但是开发者不应该就此将内存管理的问题束之高阁。管理内存是一个很复杂的过程,哪一块内存不再需要并不是单凭一个算法就能决定的。

内存泄漏指的是,程序之前需要用到部分内存,而这部分内存在用完之后并没有返回到内存池。

下面是一些会导致你的程序出现内存泄漏的常见错误:

全局变量:如果你不断地创建全局变量,不管有没有用到它们,它们都将滞留在程序的整个执行过程中。如果这些变量是深层嵌套对象,将会浪费大量内存。

  1. var a = { ... }var b = { ... }function hello() { 
  2.  c = a; // 这是一个你没有意识到的全局变量} 

如果你试图访问一个此前没有声明过的变量,那么将在全局作用域中创建一个变量。在上面的例子中,c 是没有使用 var 关键字显式创建的变量/对象。

事件监听器:为了增强网站的交互性或者是制作一些浮华的动画,你可能会创建大量的事件监听器。而用户在你的单页面应用中移向其他页面时,你又忘记移除这些监听器,那么也可能会导致内存泄漏。当用户在这些页面来回移动的时候,这些监听器会不断增加。

  1. var element = document.getElementById('button'); 
  2. element.addEventListener('click', onClick) 

Intervals 和 Timeouts:当在这些闭包中引用对象时,除非闭包本身被清除,否则不会清除相关对象。

  1. setInterval(() => { 
  2.  // 引用对象}// 这时候忘记清除计时器// 那么将导致内存泄漏! 

移除 DOM 元素:这个问题很常见,类似于全局变量导致的内存泄漏。DOM 元素存在于对象图内存和 DOM 树中。用例子来解释可能会更好:

  1. var terminator = document.getElementById('terminate');var badElem = document.getElementById('toDelete'); 
  2. terminator.addEventListener('click', function() {memory 
  3.  badElem.remove();}); 

在你通过 id = ‘terminate’ 点击了按钮之后,toDelete 会从 DOM 中移除。不过,由于它仍然被监听器引用,为这个对象分配的内存并不会被释放。

  1. var terminator = document.getElementById('terminate'); 
  2. terminator.addEventListener('click', function() { 
  3.  var badElem = document.getElementById('toDelete'); 
  4.  badElem.remove();}); 

badElem 是局部变量,在移除操作完成之后,内存将会被垃圾回收器回收。

调用栈

栈是一种遵循 LIFO(先进后出)规则的数据结构,用于存储和获取数据。JavaScript 引擎通过栈来记住一个函数中最后执行的语句所在的位置。

  1. function multiplyByTwo(x) { 
  2.  return x*2;}function calculate() { 
  3.  const sum = 4 + 2; 
  4.  return multiplyByTwo(sum);} 
  5. calculate()var hello = "some more code follows" 

1.引擎了解到我们的程序中有两个函数

2.运行 calculate() 函数

3.将 calculate 压栈并计算两数之和

4.运行 multiplyByTwo() 函数

5.将 multiplyByTwo 函数压栈并执行算术计算 x*2

6.在返回结果的同时,将 multiplyByTwo() 从栈中弹出,之后回到 calculate() 函数

7.在 calculate() 函数返回结果的同时,将 calculate() 从栈中弹出,继续执行后面的代码

栈溢出

你对JavaScript了解多少?大神详细解读JavaScript的框架结构

在不对栈执行弹出的情况下,可连续压栈的数目取决于栈的大小。如果超过了这个界限之后还不断地压栈,最终会导致栈溢出。chrome 浏览器将会抛出一个错误以及被称为栈帧的栈快照。

递归:递归指的是函数调用自身。递归可以大幅度地减少执行算法所花费的时间(时间复杂度),不过它的理解和实施较为复杂。

(编辑:西双版纳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读