跳到主要内容

11 篇博文 含有标签「Javascript」

查看所有标签

· 阅读需 19 分钟
Hanasaki
信息

本文翻译、修改自 Fast properties in V8,其中会删除、修改、批注部分内容,但不改变原本的意思,使阅读更加通顺。

在本篇内容中,我们将介绍 V8 内部是如何处理 JavaScript 属性的。从 JS 的角度来看,对象(Object)和 字典(Dictionary)差不多:以字符串为键,任意对象为值。不过,在进行迭代时,会对整数索引的属性和其他属性进行不同的处理。其他情况下,不同属性的行为基本相同,与是否整数索引无关。

不过,出于性能和内存方面的考虑,V8 确实依赖于几种不同的属性表示方式。在本篇文章中,我们将解释 V8 是如何在处理动态添加的属性时提供快速的属性访问。同时,了解属性的工作原理对于解释 V8 是如何做优化的,例如内联缓存,也是至关重要的。

本文将先阐述处理整数索引属性和命名属性的区别,然后,我们将展示 V8 在添加命名属性时,如何维护 HiddenClasses 以便提供一种快速识别对象形状的方法。然后,我们将继续深入介绍命名属性是如何根据使用情况进行优化的,以实现快速访问或修改。最后,我们将详细介绍 V8 如何处理整数索引属性或数组索引。

· 阅读需 4 分钟
Hanasaki

思路

从最基本的拷贝开始,一步一步处理更复杂的对象、类型。

拷贝对象字面量

先考虑基本类型和层层对象的情况,直接 for-in 遍历 + 递归拷贝

function deepClone(obj) {
// 基本类型直接返回
if (typeof obj !== 'object') return obj;
const newObj = {};
for (let key in obj) {
// 只复制自身的对象
if (Object.hasOwn(obj, key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}

· 阅读需 2 分钟
Hanasaki

为什么防抖这样写不行?

function debounce(fn, delay) {
let timeout;
return function () {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(fn, delay, ...arguments);
};
}

· 阅读需 5 分钟
Hanasaki

负责人布置了个任务,写一个过滤 JSON 数据的方法,处理一下模型数据。递归绕的我真晕,脑子内存还是不太够。

样例数据

规则:bosclass 值为 boqs 的是树节点,bosclass 值为 wps 的是纯数据。如果树节点的 children 是空数组,那么把树节点移除掉。树的层级是无法预估的,可能有很多层。以下是过滤后的数据:

[
{
"bosclass": "boqs",
"children": [
{
"bosclass": "boqs",
"children": [
{
"bosclass": "wps",
"guid": "fa01a4a1f1d448a3bfb315dac41e1914"
},
{
"bosclass": "wps",
"guid": "cbf47dc52b2243c1a9d46bb046d8a13d"
}
],
"guid": "f785b4a061714dfe8fea367d12ddd29c"
}
],
"guid": "8bc6c9026bb9472f8a3e23b382f87ac1"
}
]

如何过滤呢?首先想到的方法是递归!

· 阅读需 3 分钟
Hanasaki

这两天吃饱撑着,在探究为什么我的 Node 程序 Heap Usage 一直居高不下,而且会越来越多。

如何调试 NodeJS 程序

在VSCode中可以直接调试,选择Debug,显示所有自动调试配置,选择要运行的命令,就进入了调试模式,侧栏会显示出调试的信息。可以捕获 Heap 快照,拿到 ChromeDevTool 里面导入分析。

用 Node 的 --inspect 参数搭配 Chrome 实时分析

运行以下命令,进入了调试模式

node --inspect serve.js

在 Chrome 中打开 chrome://inspect,就可以看到 Open dedicated DevTool for Node,点击进入 Node 调试工具,就可以开始分析了。

放大问题

很多时候,人工是难以准确排查出问题的,因为会有很多不可预料的误差,导致每次运行的结果数据不准。这时候,要放大问题,大量测试多次触发漏洞,就能很清楚的发现问题。

k6 对接口进行测试,运行下列测试程序 k6 run test.js

// test.js
import http from 'k6/http';

export let options = {
vus: 100,
duration: '20s',
};

export default function () {
let res = http.post("http://localhost:5000/monitor/start", JSON.stringify({ phone: Math.random()*100000 }), { headers: { 'Content-Type': 'application/json' } })

console.log(res.body);
}

通过对比前后的 Heap Size,确实管用,明显增加了将近 8MB。

接着调用了一下其他的接口,调用 processMap.clear() 把保存子进程的 processMap 清空。内存果然又降回去了,但并没有和最开始一样,而是稍稍高出了一点点。

得出结论,引用计数法回收垃圾是实实在在的,这个例子验证了它。但是仍然困扰我的是,Heap 里的 [compiled code] 这部分会一直增加,无法被回收,增加的量很小...

未解之谜:每次访问接口,即使是访问 / 路径,返回一段文本, [compiled code] 也会不断增加?