Skip to content

perf: optimize getType and isType - #250

Open
wumo1016 wants to merge 1 commit into
VisActor:mainfrom
wumo1016:perf/vutils
Open

wumo1016 wants to merge 1 commit into
VisActor:mainfrom
wumo1016:perf/vutils

Conversation

@wumo1016

Copy link
Copy Markdown

[中文版模板 / Chinese template]

🤔 This is a ...

  • New feature
  • Bug fix
  • TypeScript definition update
  • Bundle size optimization
  • Performance optimization
  • Enhancement feature
  • Refactoring
  • Update dependency
  • Code style optimization
  • Test Case
  • Branch merge
  • Site / documentation update
  • Demo update
  • Workflow
  • Chore
  • Other (about what?)

🔗 Related issue link

💡 Background and solution

📝 Changelog

Language Changelog
🇺🇸 English
🇨🇳 Chinese

☑️ Self-Check before Merge

⚠️ Please check all items below before requesting a reviewing. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • TypeScript definition is updated/provided or not needed
  • Changelog is provided or not needed

🚀 Summary

copilot:summary

🔍 Walkthrough

copilot:walkthrough

@imsunhao

Copy link
Copy Markdown
Contributor

这两段写法的目的相同——都是想得到 value 的类型名(如 "Array", "Object", "Number" 等),但实现方式和细节略有不同


🧩 第一种写法

return {}.toString
  .call(value)
  .replace(/^\[object /, '')
  .replace(/]$/, '');

✅ 原理

  • {}.toString 实际上等价于 Object.prototype.toString

  • .call(value) 调用时会返回类似 "[object Array]" 这样的字符串。

  • 之后用正则 replace 去掉前缀 "[object " 和结尾 "]",得到 "Array"

⚙️ 特点

  • 显式地用正则替换字符串

  • 不依赖固定的下标(更语义化一点)。

  • 稍微慢一点(正则匹配 + 两次替换)。


🧩 第二种写法

return Object.prototype.toString.call(value).slice(8, -1);

✅ 原理

  • 同样用 Object.prototype.toString.call(value)

  • 它返回 "[object XXX]",其中 "XXX" 的起始索引是 8("[object " 长度为 8)。

  • slice(8, -1) 截取中间部分得到 "XXX"

⚙️ 特点

  • 更简洁且性能更好(只做字符串切片,没有正则)。

  • 可读性稍弱:8-1 是“魔法数字”,需要知道格式细节。


✅ 总结对比

对比项 第一种写法 第二种写法
实现方式 正则替换 字符串截取
可读性 较语义化 较紧凑但依赖 magic number
性能 稍慢 更快
推荐 ✅ 不常用或教学场景 ✅ 实际项目中更推荐

👉 推荐写法:

Object.prototype.toString.call(value).slice(8, -1);

因为它更直接、性能更好,也被许多框架(如 Lodash、Vue 内部)采用。

@xile611 xile611 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议保留 getType 的优化,撤回本次 isType 的修改后再合并。

getTypeslice(8, -1) 替换两次正则替换有明确收益;但 isType 改为提取子串后再比较,对动态类型名有收益,对仓库中常见的固定类型名调用则出现可复现的性能回退。isDateisRegExpisNumberisStringisBooleanisPlainObject 都属于后一类。

实测环境:Apple M5 Pro / arm64,Chrome 153.0.8010.50。使用实际源码经 TypeScript 4.9.5 转译为 ES modules,混合输入包含基础类型、对象、数组、Date、RegExp、Map、Set。每组先预热两次,再测五次,每次 100 万调用,消费返回值;三轮新页面交替执行版本,取中位数。下表单位为 ns/次,百分比表示耗时变化:

场景 修改前 完整 PR 耗时变化
getType 混合输入 43.1 8.4 -81%
isEmpty 混合输入 36.8 14.7 -60%
isType 动态类型名、匹配成功 29.2 11.0 -62%
isType(value, 'Object') 6.8 9.7 +43%
isDate 混合输入 6.7 9.2 +37%
isRegExp 混合输入 7.2 9.5 +32%
isNumber 混合输入 7.3 9.2 +26%

Node 24.19.0 / V8 13.6 的五轮独立进程测试也复现了相同趋势。另测“只修改 getType”时,保留了 getType / isEmpty 的收益,且 isDateisNumber 的耗时回到基线附近。这些是函数微基准,不能直接推导为整体图表渲染的性能变化。

正确性验证:将两处改动应用到当前本地基线后,common 目录的 23 个测试套件、86 个测试全部通过;已核实这两个函数的原始实现与 PR 父提交一致。另外对 62 个值、26 种目标类型及 9 个相关函数做了 2,108 次行为对比,包含跨 realm、自定义 Symbol.toStringTag、异常 getter 和 revoked proxy,均与原实现一致。在有效参数范围内未发现功能回归。

建议此次只合入 getType 改动,并补充 getType / isType 的直接测试;后续若继续优化 isType,请同时覆盖动态类型名和现有固定类型名调用的性能。

const isType = (value: any, type: string): boolean => Object.prototype.toString.call(value) === `[object ${type}]`;
import getType from './getType';

const isType = (value: any, type: string): boolean => getType(value) === type;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 建议保留原 isType 实现,避免固定类型名调用的性能回退。

这里新增了类型标签的切片操作。仓库中的 isDate(value)isRegExp(value)isNumber(value) 等均传入固定类型名,无法用动态类型名场景的收益代表这些路径。实际源码在 Chrome 153 中的混合输入测试显示:isDate 从 6.7 增至 9.2 ns/次(+37%),isRegExp 从 7.2 增至 9.5 ns/次(+32%),固定 'Object'isType 从 6.8 增至 9.7 ns/次(+43%);Node 24 同样复现。只保留 getType 改动时,这些回退可避免,同时仍保留 getType / isEmpty 的收益。建议从本 PR 撤回这处修改。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants