前端小技巧: TS实现一个获取传入参数类型的函数

文章介绍了在JavaScript前端中,如何使用`Object.prototype.toString.call`方法进行精确的值类型判断,包括基本类型如number、string、null、undefined等,以及复杂类型如function、object、array、weakset等。作者提供了编码示例和测试用例来展示这一方法的实用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析

  • 在前端中,能判断类型的无非几种
    • 1 )typeof 只能判断值类型,其他就是 function 和 object
    • 2 ) instanceof 需要两个参数判断,不是获取
    • 3 )手动枚举各种类型
      • 可能会忽略某些类型
      • 如果新增了类型,则需要修改代码
      • 这种方案不可取
    • 4 )原生API
      • Object.prototype.toString.call(input) input 是用户输入
      • 而非 input.toString() 这种不可行

测试 Object.prototype.toString.call

  • Object.prototype.toString.call(12) // ‘[object Number]’
  • Object.prototype.toString.call(NaN) // ‘[object Number]’
  • Object.prototype.toString.call(-Infinity) // ‘[object Number]’
  • Object.prototype.toString.call('abc') // ‘[object String]’
  • Object.prototype.toString.call([]) // ‘[object Array]’
  • Object.prototype.toString.call({}) // ‘[object Object]’
  • Object.prototype.toString.call(() => {}) // ‘[object Function]’
  • Object.prototype.toString.call(new Set()) // ‘[object Set]’
  • Object.prototype.toString.call(new WeakSet()) // ‘[object WeakSet]’
  • Object.prototype.toString.call(new Map()) // ‘[object Map]’
  • Object.prototype.toString.call(new WeakMap()) // ‘[object WeekMap]’
  • Object.prototype.toString.call() // ‘[object Undefined]’
  • Object.prototype.toString.call(undefined) // ‘[object Undefined]’
  • Object.prototype.toString.call(null) // ‘[object Null]’

基于以上测试,可看到可以获取具体的类型

代码实现

1 )编码

export function getType(input: any): string {
  const orgType = Object.prototype.toString.call(input) // '[object String]'
  const i = orgType.indexOf(' ')
  const type = orgType.slice(i + 1, -1)
  return type.toLowerCase() // 'string'
}

2 )测试

console.log(getType(1)) // number
console.log(getType(NaN)) // number
console.log(getType('ass')) // string
console.log(getType(null)) // null
console.log(getType(undefined)) // undefined
console.log(getType({})) // object
console.log(getType(() => {})) // function
console.log(getType(new WeakSet())) // weakset
<think>嗯,用户想要了解如何在Vue3和TypeScript中封装复杂的Echarts图表。首先,我需要回忆一下Vue3的组合式API和TypeScript的类型系统,以及Echarts的使用方法。 首先,用户可能需要知道为什么需要封装Echarts组件。可能的原因包括代码复用、统一管理配置、维护性等。接下来,我需要考虑封装的基本步骤。通常,封装Echarts组件需要创建一个Vue组件,在组件内部处理Echarts实例的初始化、更新和销毁。 然后,考虑如何在Vue3中使用组合式API,比如使用setup函数和ref来引用DOM元素。可能需要使用到onMounted和onUnmounted生命周期钩子来初始化和销毁Echarts实例。同时,TypeScript的类型定义也很重要,比如为组件的props定义类型,确保传入的配置项符合Echarts的要求。 接下来,我需要想到如何处理动态数据。可能需要使用watch来监听数据变化,并更新图表。另外,可能需要将Echarts的配置项进行拆分,比如将主题、数据、样式等部分分离,使得组件更加灵活。 还有,可能需要考虑响应式布局的问题,当窗口大小变化时,图表需要自适应调整。这时候需要在组件中添加resize事件监听器,并在组件销毁时移除监听器,避免内存泄漏。 另外,封装的时候可能需要提供默认配置,同时允许用户通过props传入自定义配置,这样可以兼顾灵活性和便利性。需要将默认配置和用户传入的配置进行合并,可能需要用到深合并,比如使用lodash的merge函数或者自己实现一个深合并的方法。 还有可能要考虑加载状态和错误处理,比如数据加载时显示loading效果,或者数据错误时展示友好的提示信息。这也需要组件内部有相应的状态管理和UI反馈。 在TypeScript方面,需要为组件的props定义接口,包括必填和可选的配置项,以及数据项的类型。可能需要引入Echarts类型定义,比如从echarts中导入ECOption类型,确保传入的配置符合Echarts的要求。 可能还需要考虑组件的性能优化,比如避免不必要的重新渲染,使用watch的deep选项来深度监听配置项的变化,但要注意性能影响。或者使用debounce来限制resize事件的触发频率,提升性能。 最后,可能需要提供一个使用示例,展示如何引入封装好的组件,并传递必要的props,比如配置项和数据,这样用户能够快速上手使用封装后的组件。 总结一下,封装Echarts组件需要处理初始化、配置合并、响应式更新、事件监听、类型定义和性能优化等方面的问题。通过组合式API和TypeScript,可以有效地将这些功能模块化,提高代码的可维护性和复用性。</think>以下是在 Vue3 + TypeScript 中封装复杂 ECharts 组件的实现思路和最佳实践: # 一、基础封装结构 ```vue <template> <div ref="chartRef" class="echart-container"></div> </template> <script setup lang="ts"> import { ref, onMounted, onBeforeUnmount, watch } from 'vue' import * as echarts from 'echarts/core' import { TooltipComponent, GridComponent } from 'echarts/components' import { BarChart } from 'echarts/charts' import { CanvasRenderer } from 'echarts/renderers' import type { EChartsOption } from 'echarts' // 注册必要组件 echarts.use([TooltipComponent, GridComponent, BarChart, CanvasRenderer]) interface Props { options: EChartsOption theme?: string | object autoResize?: boolean } const props = withDefaults(defineProps<Props>(), { theme: 'default', autoResize: true }) const chartRef = ref<HTMLElement>() let chartInstance: echarts.ECharts | null = null // 初始化图表 const initChart = () => { if (!chartRef.value) return chartInstance = echarts.init(chartRef.value, props.theme) chartInstance.setOption(props.options) if (props.autoResize) { window.addEventListener('resize', handleResize) } } // 响应式更新 watch( () => props.options, (newOptions) => { chartInstance?.setOption(newOptions) }, { deep: true } ) // 自适应逻辑 const handleResize = () => { chartInstance?.resize() } // 生命周期管理 onMounted(initChart) onBeforeUnmount(() => { window.removeEventListener('resize', handleResize) chartInstance?.dispose() }) </script> <style scoped> .echart-container { width: 100%; height: 400px; } </style> ``` # 二、高级封装技巧 1. 类型增强(types/chart.d.ts): ```typescript declare module 'echarts' { interface SeriesOption { customProp?: string } } ``` 2. 配置继承与扩展: ```typescript // 使用 TypeScript 泛型实现配置继承 function extendBaseConfig<T extends EChartsOption>(customConfig: T): EChartsOption { return { // 基础配置 tooltip: { trigger: 'axis' }, grid: { containLabel: true }, // 合并自定义配置 ...customConfig } } ``` 3. 数据更新策略: ```typescript // 使用数据差异对比优化更新 watch( () => props.data, (newVal, oldVal) => { if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) { chartInstance?.setOption({ dataset: { source: newVal } }, { notMerge: true }) } }, { deep: true } ) ``` # 三、最佳实践建议 1. 组件分层设计: ``` components/ ├─ charts/ │ ├─ base/ │ │ └─ BaseChart.vue // 基础封装 │ ├─ mixins/ // 通用逻辑复用 │ ├─ types/ // 类型定义 │ └─ business/ // 业务图表组件 │ └─ SalesTrend.vue // 具体业务图表 ``` 2. 按需加载优化: ```typescript // 动态加载图表组件 const loadChartComponents = async (type: string) => { const { BarChart, LineChart } = await import('echarts/charts') return { BarChart, LineChart } } ``` 3. 性能优化方案: ```typescript // 使用 debounce 优化 resize import { debounce } from 'lodash-es' const handleResize = debounce(() => { chartInstance?.resize() }, 300) ``` # 四、完整使用示例 ```vue <template> <BaseChart :options="chartOptions" theme="dark" class="sales-chart" /> </template> <script setup lang="ts"> import BaseChart from '@/components/charts/BaseChart.vue' import type { EChartsOption } from 'echarts' const chartOptions: EChartsOption = { xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] }, yAxis: { type: 'value' }, series: [{ type: 'bar', data: [120, 200, 150, 80], customProp: '自定义属性' // 类型安全的扩展属性 }] } </script> <style> .sales-chart { height: 500px; background: var(--bg-color); } </style> ``` # 五、扩展功能实现 1. 全局主题管理: ```typescript // 在 Vue 应用层面提供主题管理 import { provide } from 'vue' const theme = reactive({ colorPalette: ['#5470c6', '#91cc75', '#fac858'], textStyle: { fontSize: 14 } }) provide('chartTheme', theme) ``` 2. 错误边界处理: ```vue <template> <div v-if="error" class="error-message"> {{ errorMessage }} </div> <div v-else> <slot /> </div> </template> <script setup lang="ts"> import { ref, onErrorCaptured } from 'vue' const error = ref(false) const errorMessage = ref('') onErrorCaptured((err) => { error.value = true errorMessage.value = `图表加载失败: ${err.message}` return false }) </script> ``` 3. 交互事件转发: ```typescript // 在基础组件中添加事件转发 const emit = defineEmits<{ (e: 'click', params: any): void (e: 'hover', params: any): void }>() onMounted(() => { chartInstance?.on('click', (params) => { emit('click', params) }) chartInstance?.on('mouseover', (params) => { emit('hover', params) }) }) ``` 这种封装方式具有以下优势: 1. 完整的 TypeScript 类型支持 2. 响应式数据更新机制 3. 自动化的生命周期管理 4. 灵活的主题配置能力 5. 优化的性能表现 6. 完善的错误处理机制 7. 良好的可扩展性设计 建议根据实际业务需求,结合项目的状态管理方案(如 Pinia)进行数据管理,并配合 VueUse 等工具库实现更复杂的交互逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值