浅析如何使用JavaScript轻松实现数据转换
作者:Hamm
之前写了一篇 《TypeScript装饰器之我们是这么处理项目数据转换的》,有很多朋友私信和评论说,如果没有 TypeScript 和 装饰器,纯 JavaScript 有没有什么好的数据转换的方案呢?
很遗憾,还真有,虽然没有 TypeScript 那么优雅,但是也足够好用。
这里用到了 Getter/Setter,以及 Object 原型链相关的知识。
一、假设需求
1. 后端返回的数据
这里我们先假设从后端来了个 JSON 长这样:
{
id: 1,
nickname: 'Hamm',
age: 18,
sex: 1,
createTime: 1707021296000,
bio: '一些废话,前端不需要的字段'
}
其中,id createTime 是固定返回的公共属性。
2. 前端类型声明
基类
class BaseEntity {
id;
createTime;
}
用户类
class User extends BaseEntity {
nickname;
age;
sex;
}
3. 转换要求
转换到类时
createTime转为 Date类型;- 前端使用
gender作为性别字段,且需要根据1/0显示男女; - 前端没有
bio字段,需要过滤掉。
转换到 JSON 时
createTime转回后端需要的时间戳gender还原回后段需要的sex,并且转换为1/0
二、实现思路
正如我们之前有篇关于 几个有关Getter/Setter的小故事 文章中提到,我们可以用 getter/setter 来拦截数据达到转换数据的目的:
1. 基于基类实现 fromJson 静态方法
class BaseEntity {
id;
createTime;
static fromJson(json) {
const user = new this();
const filteredJson = Object.keys(json).reduce((item, key) => {
if (user.hasOwnProperty(key)) {
item[key] = json[key];
}
if (user.__proto__.hasOwnProperty(key)) {
item[key] = json[key];
}
return item;
}, {});
const entity = Object.assign(user, filteredJson);
if (entity.createTime) {
entity.createTime = new Date(entity.createTime);
}
return entity;
}
}
class UserEntity extends BaseEntity {
nickname;
age;
gender;
get sex() {
return this.gender;
}
set sex(value) {
if (value === undefined || value === null) {
this.gender = undefined
}else{
this.gender = value === 1 ? '男' : '女';
}
}
}
const json = {
id: 1,
nickname: 'Hamm',
age: 18,
sex: 1,
createTime: 1707021296000,
bio: '一些废话,前端不需要的字段'
}
console.log("json", json)
const user = UserEntity.fromJson(json)
console.log("entity", user)
其中,我们通过读取 getter/setter 以及本身的属性,来确定哪些是直接赋值,哪些是走set方法,哪些不存在的需要忽略。
2.调试输出,美滋滋
json {
id: 1,
nickname: 'Hamm',
age: 18,
sex: 1,
createTime: 1707021296000,
bio: '一些废话,前端不需要的字段'
}
entity UserEntity {
id: 1,
createTime: 2024-02-04T04:34:56.000Z,
nickname: 'Hamm',
age: 18,
gender: '男'
}
3. 实现动态方法的 toJson
接下来,我们需要将 BaseEntity 添加一个 toJson 方法,用于将实体转换为 JSON 格式,且给 UserEntity 的 sex getter 做一下数据转换:
class BaseEntity {
id;
createTime;
static fromJson(json) {
const user = new this();
const filteredJson = Object.keys(json).reduce((item, key) => {
if (user.hasOwnProperty(key)) {
item[key] = json[key];
}
if (user.__proto__.hasOwnProperty(key)) {
item[key] = json[key];
}
return item;
}, {});
const entity = Object.assign(user, filteredJson);
if (entity.createTime) {
entity.createTime = new Date(entity.createTime);
}
return entity;
}
toJson() {
const proto = Object.getPrototypeOf(this);
const ownProperties = Object.getOwnPropertyNames(this);
const getters = Object.getOwnPropertyNames(proto).filter(key => {
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
return descriptor && typeof descriptor.get === 'function';
});
const json = {}
getters.forEach(key => {
if (this.__proto__.hasOwnProperty(key)) {
json[key] = this[key]
this[key] = undefined
}
})
ownProperties.forEach(key => {
if (this.hasOwnProperty(key)) {
json[key] = this[key]
this[key] = undefined
}
})
if (json.createTime && typeof json.createTime === 'object') {
json.createTime = json.createTime.valueOf()
} else {
json.createTime = undefined
}
return JSON.parse(JSON.stringify(json))
}
}
class UserEntity extends BaseEntity {
nickname;
age;
gender;
get sex() {
if (this.gender === undefined || this.gender === null) {
return undefined
}
return this.gender === '男' ? 1 : 0;
}
set sex(value) {
if (value === undefined || value === null) {
this.gender = undefined
}else{
this.gender = value === 1 ? '男' : '女';
}
}
}
const user = new UserEntity()
user.id = 1
user.nickname = "Hamm"
user.age = 18
user.gender = "男"
user.createTime = new Date()
console.log("entity", user)
console.log("json", user.toJson())
4. 继续调试输出,继续美滋滋
entity UserEntity {
id: 1,
createTime: 2024-10-23T19:37:07.521Z,
nickname: 'Hamm',
age: 18,
gender: '男'
}
json { sex: 1, id: 1, createTime: 1729712227521, nickname: 'Hamm', age: 18 }
三、完整代码如下
class BaseEntity {
id;
createTime;
static fromJson(json) {
const user = new this();
const filteredJson = Object.keys(json).reduce((item, key) => {
if (user.hasOwnProperty(key)) {
item[key] = json[key];
}
if (user.__proto__.hasOwnProperty(key)) {
item[key] = json[key];
}
return item;
}, {});
const entity = Object.assign(user, filteredJson);
if (entity.createTime) {
entity.createTime = new Date(entity.createTime);
}
return entity;
}
toJson() {
const proto = Object.getPrototypeOf(this);
const ownProperties = Object.getOwnPropertyNames(this);
const getters = Object.getOwnPropertyNames(proto).filter(key => {
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
return descriptor && typeof descriptor.get === 'function';
});
const json = {}
getters.forEach(key => {
if (this.__proto__.hasOwnProperty(key)) {
json[key] = this[key]
this[key] = undefined
}
})
ownProperties.forEach(key => {
if (this.hasOwnProperty(key)) {
json[key] = this[key]
this[key] = undefined
}
})
if (json.createTime && typeof json.createTime === 'object') {
json.createTime = json.createTime.valueOf()
} else {
json.createTime = undefined
}
return JSON.parse(JSON.stringify(json))
}
}
class UserEntity extends BaseEntity {
nickname;
age;
gender;
get sex() {
if (this.gender === undefined || this.gender === null) {
return undefined
}
return this.gender === '男' ? 1 : 0;
}
set sex(value) {
if (value === undefined || value === null) {
this.gender = undefined
}else{
this.gender = value === 1 ? '男' : '女';
}
}
}
const json = {
id: 1,
nickname: 'Hamm',
age: 18,
sex: 1,
createTime: 1707021296000,
bio: '一些废话,前端不需要的字段'
}
console.log("json",json)
const user = UserEntity.fromJson(json)
console.log("entity", user)
console.log("json", user.toJson())四、总结
如上代码,我们利用了 getter/setter 以及 Object 原型链上的一些方法来完成了转换。
虽然稍显麻烦,但也算解决了不在外部写一些方法来转换。
各有所长,实现的方式有很多种,但思路才是很好玩的东西。
当然,JavaScript的装饰器提案已经快发布了,后续我们将继续摸索用装饰器在JavaScript中实现类似功能。(TypeScript的装饰器我们敢用,是因为编译后的JS代码没有包含装饰器的部分,在浏览器上不会有问题。)
到此这篇关于浅析如何使用JavaScript轻松实现数据转换的文章就介绍到这了,更多相关JavaScript数据转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
