妙啊!Js的对象属性居然还能这么写
静态属性获取的缺陷前段时间在做项目国际化时,遇到一个比较隐蔽的问题:
我们在定义枚举常量时,直接调用了i18n的翻译方法:
exportconstOverdueStatus: any = {
ABOUT_TO_OVERDUE: {
value:'ABOUT_TO_OVERDUE',
name: i18n.global.t('common.about_to_overdue'),
color:'#ad0000',
bgColor:'#ffe1e1'
},
}
结果发现翻译始终不生效。排查后才发现原因很简单 ——OverdueStatus「对象的初始化早于」i18n「实例的生成」,因此取到的翻译结果是空的。
虽然最后我通过封装自定义 Vue 插件的方式彻底解决了问题,但排查过程中其实还有一个可选思路。
当时我想到的最直接办法是:让name在被访问时再去执行i18n.global.t,而不是在对象定义时就执行。比如把OverdueStatus定义为函数:
exportconstOverdueStatus =()=({
ABOUT_TO_OVERDUE: {
value:'ABOUT_TO_OVERDUE',
name: i18n.global.t('common.about_to_overdue'),
color:'#ad0000',
bgColor:'#ffe1e1'
},
})
这样在调用时:
OverdueStatus().ABOUT_TO_OVERDUE.name
就能确保翻译逻辑在i18n实例创建完成之后再执行,从而避免初始化顺序的问题。不过,这种方式也有明显的缺点:所有类似的枚举都要改成函数,调用时也得多加一层执行,整体代码会变得不够简洁。
如何优雅地实现“动态获取属性”?上面提到的“把枚举改成函数返回”虽然能解决问题,但在实际业务中显得有些笨拙。有没有更优雅的方式,让属性本身就支持「动态计算」呢?
其实,JavaScript 本身就为我们提供了解决方案 ——「getter」。
举个例子,我们可以把枚举对象改写成这样:
exportconstOverdueStatus: any = {
ABOUT_TO_OVERDUE: {
value:'ABOUT_TO_OVERDUE',
getname() {
returni18n.global.t('common.about_to_overdue')
},
color:'#ad0000',
bgColor:'#ffe1e1'
},
}
这样一来,在访问name属性时,才会真正执行i18n.global.t,确保翻译逻辑在i18n实例创建完成后才生效,完美解决问题。
访问器属性的原理在 JavaScript 规范里,get定义的属性叫「访问器属性」,区别于普通的「数据属性 (Data Property)」。简单来说getter其实就是对象属性的一种特殊定义方式。
当我们写:
constobj = {
getfoo() {
return"bar"
}
}
等价于用Object.defineProperty:
constobj = {}
Object.defineProperty(obj,"foo", {
get:function(){
return"bar"
}
})
所以访问obj.foo时,其实是触发了这个get函数,而不是读取一个固定的值。
类比Vue的computed在 Vue 里,我们经常写「computed 计算属性」,其实就是 getter 的思想。
import{ computed, ref }from"vue"
constfirstName = ref("Tom")
constlastName = ref("Hanks")
constfullName = computed(()=`${firstName.value}${lastName.value}`)
computed内部其实就是包装了一个 getter 函数。
注意点getter 不能跟属性值同时存在:constobj = {
getname() {return"石小石"},
name:"石小石Orz"// 会报错
}
getter 是只读的,如果你想支持赋值,需要配合setter:constobj = {
_age:18,
getage() {returnthis._age },
setage(val) {this._age = val }
}
obj.age =20
console.log(obj.age)// 20
其他实用场景延迟计算有些值计算比较复杂,但只有在真正使用时才去算,可以提升性能
constuser = {
firstName:"石",
lastName:"小石",
getfullName() {
// 类比一个计算,实现开发中,一个很复杂的计算才使用此方法
console.log("计算了一次 fullName")
return`${this.firstName}${this.lastName}`
}
}
console.log(user.fullName)// "石小石"
这种写法让 API 看起来更自然,不需要调用函数user.getFullName(),而是user.fullName。
数据封装与保护有些属性可能并不是一个固定字段,而是基于内部状态计算出来的:
constcart = {
items: [100,200,300],
gettotal() {
returnthis.items.reduce((sum, price) =sum + price,0)
}
}
console.log(cart.total)// 600
这样cart.total永远是最新的,不用担心手动维护,你也不用写一个函数专门去更新这个值。
AI编程资讯AI Coding专区指南:
https://aicoding.juejin.cn/aicoding
点击"阅读原文"了解详情~
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线