likes
comments
collection
share

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

作者站长头像
站长
· 阅读数 65

背景

事情是这样的,客户端首页窗口是一个合约信息查询模块,有两个列对称的 Table 展示合约信息。大概长这样:

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

开发时我想着,既然列都一样,只是 正序倒序 的区别,那本着能用一组数据就绝不用两组数据的原则,我将列名等信息 抽离 出来。

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

最终结构如下:

// columns.ts
import { ColumnProps } from 'ant-design-vue/lib/table/interface';

type CustomColumnType = (ColumnProps & { resizable?: boolean, sortIndex?: number });
const contract: CustomColumnType[] = {
    {
        title: '合约名称',
        key: 'stockName',
        dataIndex: 'stockName',
        resizable: true,
        width: 180,
        align: 'center',
        sortIndex: contractSortIndexMap['stockName']
    },
    {
        title: '合约代码',
        key: 'stockCode',
        dataIndex: 'stockCode',
        resizable: true,
        width: 120,
        align: 'center',
        sortIndex: contractSortIndexMap['stockCode'],
        customRender: ({ text }: any) => {
            return h('span', { style: { color: '#4A7EFF' } }, text);
        }
    },
    ...
};
export {
    contract
};

首页通过 import 进行引入:

import { contract } from './config/columns';

const leftContractColumns = ref(JSON.parse(JSON.stringify(contract.sort((a: any, b: any) => a.sortIndex - b.sortIndex))));
const rightContractColumns = ref(JSON.parse(JSON.stringify(contract.sort((a: any, b: any) => b.sortIndex - a.sortIndex))));

然后用 JSON.parse + JSON.stringify 实现简单的拷贝,分别赋值给对应变量,大功告成,洒洒水嘛。

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

随后测试了一番,发现列的渲染没问题(正序和倒序),但是在列的配置数组里,所有含有 customRender 属性定义的函数 全部 没有生效。

{
    title: '合约代码',
    key: 'stockCode',
    ...,
    customRender: ({ text }: any) => { // customRender 失效
        return h('span', { style: { color: '#4A7EFF' } }, text);
    }
}

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

这是为什么呢?

我猜有可能是哪个数据一直在刷新,导致函数配置项没有反应过来(说出来我自己都不信)。

于是开启了摸瞎胡猜的排错之路,结果显而易见的,又是和兰陵王做斗争。

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

那没办法了,摇人是不可能摇人的,边摸鱼边想吧。

也许是摸鱼带给了我几分清明,也许是我的脑袋宕机恢复了,我突然灵光一闪:会不会是 JSON.stringfy + JSON.parse 实现拷贝有什么弊端?来做个实验试试!

锁定问题

说干就干,我们准备一个数组,往里塞个拥有两个属性和一个方法的对象:

let arr = [{
    name: 'CatWatermelon',
    age: 18,
    talk: function(){
        console.log(`${this.name} is handsome`);
    }
}]
arr[0].talk() // CatWatermelon is handsome

测试一下正常输出,毛闷台。

通过 JSON 两兄弟拷贝一下看看:

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

嗯。嗯?talk 方法怎么不见了?

难道!莫非?对,还真是它俩搞的鬼!通过 JSON 两兄弟拷贝对象数组,对象里的函数直接 被删掉了

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

那我不用 JSON 坑比两兄弟不就完事了。

import { contract } from './config/columns';

const leftContractColumns = ref([...contract.sort((a: any, b: any) => a.sortIndex - b.sortIndex)]);
const rightContractColumns = ref([...contract.sort((a: any, b: any) => b.sortIndex - a.sortIndex)]);

渲染效果没问题,喜滋滋。

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

你以为这就完了吗?点到为止可不是俺们打工人的作风,接下来我们一探究竟。

拨开迷雾

首先,通过上面的分析我们已经知道是 JSON 坑比二兄弟导致我们拷贝后对象的方法丢失,那么到底是在 JSON.stringify 序列化后丢失的,还是在 JSON.parse 解析完才丢失的?

话不多说,直接动手测试一下:

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

一目了然,talk 方法在 JSON.stringify 序列化后就已经丢失了。这说明 JSON.stringify 应该是有自己的一套转换规则的,什么能搞里头,什么不能搞里头,搞了会怎么样,都是规定好了的。

因为不了解 JSON 拷贝的缺陷,导致我查错查了一下午😅🤡

JSON.stringify 的转换规则

那么 JSON.stringify 的转换规则到底是啥呢?话不多说,我们直接往下看:

  • 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
  • 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
  • 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined).
  • 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  • 所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
  • Date 日期调用了 toJSON() 将其转换为了 string 字符串(同 Date.toISOString()),因此会被当做字符串处理。
  • NaNInfinity 格式的数值及 null 都会被当做 null
  • 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。

小小方法,门道还不少,一不小心就掉进坑里了,不过这也说明了对 api 的掌握程度还不够,以至于发生问题没有第一时间怀疑到这个点。不过踩坑不要紧,填平了那就是经验了,下次出了问题多了一个思考方向,美滋滋。

结束语

本文通过踩坑 JSON 实现拷贝讲解了该方法需要注意的点,大家应该根据自己的实际情况,来选择合适的拷贝方式,而不是一招鲜,希望大家阅读本文都能有所收获。

转载自:https://juejin.cn/post/7149210196689043469
评论
请登录