1. Default/ “Zero Values”
给对象(包括数组)提供zero values. zero values分别是Boolean(false), Number(0), String(''), Object({}), Array([])等等.
const withZeroValue = (target, zeroValue) => new Proxy(target, {
get: (obj, prop) => (prop in obj) ? obj[prop] : zeroValue
})
let pos = {
x: 4,
y: 19
}
console.log(pos.x, pos.y, pos.z) // 4, 19, undefined
pos = withZeroValue(pos, 0)
console.log(pos.x, pos.y, pos.z) // 4, 19, 0
2. Negative Array Indices
获取数组的最后一个元素在JS里一般使用arr[arr.length - 1]
, 又重复又有可能出现差一位的错误.
const negativeArray = (els) => new Proxy(els, {
get: (target, propKey, receiver) => Reflect.get(target,
(+propKey < 0) ? String(target.length + +propKey) : propKey, receiver)
});
const unicorn = negativeArray(['?', '?', '?']);
unicorn[-1] // '?'
相关引用:
TC39Array.lastItem
的提案: proposal-array-last.
Ruby的negative array indices: Ruby Quicktips.
npm包: negative-array
3. Hiding Properties
我们经常将私有属性前面带上_
,hide
函数可以让前缀带有_
的属性不可迭代.
const hide = (target, prefix = '_') => new Proxy(target, {
has: (obj, prop) => (!prop.startsWith(prefix) && prop in obj),
ownKeys: (obj) => Reflect.ownKeys(obj)
.filter(prop => (typeof prop !== "string" || !prop.startsWith(prefix))),
get: (obj, prop, rec) => (prop in rec) ? obj[prop] : undefined
})
let userData = hide({
firstName: 'Tom',
mediumHandle: '@tbarrasso',
_favoriteRapper: 'Drake'
})
userData._favoriteRapper // undefined
('_favoriteRapper' in userData) // false
Object.keys(userData) // ['firstName', 'mediumHandle']
可以使用Object.defineProperty设置属性或Symbol值设为属性使其不可迭代.
4. Caching
ephemeral
函数可以设置属性多久超时. 超时时获取不到该属性值(缓存失效).
const ephemeral = (target, ttl = 60) => {
const CREATED_AT = Date.now()
const isExpired = () => (Date.now() - CREATED_AT) > (ttl * 1000)
return new Proxy(target, {
get: (obj, prop) => isExpired() ? undefined : Reflect.get(obj, prop)
})
}
let bankAccount = ephemeral({
balance: 14.93
}, 10)
console.log(bankAccount.balance) // 14.93
setTimeout(() => {
console.log(bankAccount.balance) // undefined
}, 10 * 1000)
5. Enums & Read-Only Views
枚举(enum)获取不存在的属性会报错, 他与第一个属性默认值类似, 返回的一个是默认值一个是报错, 但他的行为与枚举类型很相似所以单独列出来.
const createEnum = (target) => readOnlyView(new Proxy(target, {
get: (obj, prop) => {
if (prop in obj) {
return Reflect.get(obj, prop)
}
throw new ReferenceError(`Unknown prop "${prop}"`)
}
}))
只读对象
const NOPE = () => {
throw new Error("Can't modify read-only view");
}
const NOPE_HANDLER = {
set: NOPE,
defineProperty: NOPE,
deleteProperty: NOPE,
preventExtensions: NOPE,
setPrototypeOf: NOPE
}
const readOnlyView = target =>
new Proxy(target, NOPE_HANDLER)
let SHIRT_SIZES = createEnum({
S: 10,
M: 15,
L: 20
})
SHIRT_SIZES.S // 10
SHIRT_SIZES.S = 15
// Uncaught Error: Can't modify read-only view
SHIRT_SIZES.XL
// Uncaught ReferenceError: Unknown prop "XL"
相关链接
TS enum type
6. Operator Overload
重载in运算符. 虽然有点感觉增加了心智负担, 但或许增加了代码可读性.
const range = (min, max) => new Proxy(Object.create(null), {
has: (_, prop) => (+prop >= min && +prop <= max)
})
const X = 10.5
const nums = [1, 5, X, 50, 100]
if (X in range(1, 100)) { // true
// ...
}
nums.filter(n => n in range(1, 10)) // [1, 5]
不止可以计算数字范围,还可以排除范围数, 自然数, 有理数, 虚数, 无限数等等.
还可以重载delete
, new
.
7. Cookies Object
document.cookie
获取的是一个字符串, 可以简单实现一个cookies的object方便操作.
_octo=GH1.2.2591.47507; _ga=GA1.1.62208.4087; has_recent_activity=1
const getCookieObject = () => {
const cookies = document.cookie.split(';').reduce((cks, ck) =>
({[ck.substr(0, ck.indexOf('=')).trim()]: ck.substr(ck.indexOf('=') + 1), ...cks}), {});
const setCookie = (name, val) => document.cookie = `${name}=${val}`;
const deleteCookie = (name) => document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
return new Proxy(cookies, {
set: (obj, prop, val) => (setCookie(prop, val), Reflect.set(obj, prop, val)),
deleteProperty: (obj, prop) => (deleteCookie(prop), Reflect.deleteProperty(obj, prop))
})
}
let docCookies = getCookieObject()
docCookies.has_recent_activity // "1"
docCookies.has_recent_activity = "2" // "2"
delete docCookies["has_recent_activity"] // true
所以可以想到, 类似键值对的字符串都可以想是否需要用Proxy进行包装,来方便后续操作.
总结
为什么要用Proxy?
个人的理解为:
-
增强代码的语义化, 改变原来对象对属性的各方面的操作, 而不去用额外的函数等去包裹, 包裹后的对象天生就对设置好的属性有特殊的操作.
-
可以用简单的配置将对象增强, 比如我们可以让cookie只读,隐藏特殊属性, 带有ZeroValue的时候可以这样写:
// document.cookie = "_octo=GH1.2.2591.47507; _ga=GA1.1.62208.4087; has_recent_activity=1" let docCookies = withZeroValue(hide(readOnlyView(getCookieObject())), "Cookie not found") docCookies.has_recent_activity // "1" docCookies.nonExistentCookie // "Cookie not found" docCookies._ga // "Cookie not found" docCookies.newCookie = "1" // Uncaught Error: Can't modify read-only view
一句话总结Proxy的作用?
可能会总结的并不完整,但最重要的作用就是增强对象.
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!