学习ES6原生Proxy构造函数

0xGeekCat · 2020-9-14 · 次阅读


了解Proxy

使用Proxy可以将一只猫伪装成一只老虎。Proxy 提供了强大的Javascript元编程

尽管不像其他ES6功能使用普遍,但Proxy有许多用途

Proxy用于修改某些操作的默认行为,可理解为在目标对象之前架设一层拦截,外部的所有访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改

screenshot

ES6原生提供Proxy构造函数,用来生成Proxy实例;其中new Proxy用来生成Proxy实例,target表示所要拦截的对象,handle是用来定制拦截行为的对象

👇一个有陷阱的代理,一个get陷阱,总是返回42

let target = {
    x: 10,
    y: 20
};

let handler = {
    get: () => 42
};

target = new Proxy(target, handler);

console.log(target.x) → 42
console.log(target.y) → 42
console.log(target['y']) → 42
console.log(Reflect.get(target, 'x')) → 42

结果返回对象target任何属性访问操作的返回结果都是42

👇Proxy的各种陷阱

screenshot 1

Proxy功能实现

默认值/零值

在Go语言中有零值的概念,零值是特定于类型的隐式默认结构值。其思想是提供类型安全的默认基元值

Javascript无法用隐式初始值包装对象,未设置属性的默认值是undefined。但Proxy可以改变这种情况

const withZeroValue = (target, zeroValue) =>
    new Proxy(target, {
        get: (obj, prop) => prop in obj ? obj[prop] : zeroValue
    });

withZeroValue 包装目标对象target。 如果target设置属性,则返回属性值。 否则返回一个默认的零值zeroValue

  • obj:对象
  • prop:获取的属性名

‼️👆访问对象属性使用obj[prop],而不是obj.prop

let pos = {
    x: 4,
    y: 19
};
const a = 'x';
console.log(pos[a]) → 4
console.log(pos.a) → undefined

screenshot 2

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

此功能可能有用的一个地方是坐标系

负索引数组

在JavaScript中获取数组中的最后一个元素方式通过写的很冗长且重复也容易出错。 这就是为什么有一个TC39提案定义了一个便利属性Array.lastItem来获取和设置最后一个元素

其他语言如Python和Ruby会使用负索引更容易访问数组最后面的元素。可以简单地使用arr[-1]替代arr[arr.length-1]访问最后一个元素

使用Proxy也可以在Javascript中使用负索引

const negativeArray = els =>
    new Proxy(els, {
        get: (target, propKey) =>
            Reflect.get(
                target,
                +propKey < 0 ? String(target.length + +propKey) : propKey,
            )
    });

const unicorn = negativeArray(["🐴", "🌈", "🦄"])
console.log(unicorn[-1]) → 🦄
  • target:目标对象
  • propKey:被获取的属性名

+propKey

👆propKey数据类型是String,但如果要进行数学运算需要转换为数值型;+Number()等效

let propKey = '-1';
let target = ['a', 'b', 'c'];

let fin = propKey < 0 ? String(target.length + propKey) : propKey
console.log(target[fin]) → undefined

let bin = Number(propKey) < 0 ? String(target.length + Number(propKey)) : propKey
console.log(target[bin]) → c

Reflect.get()

screenshot 3

隐藏属性

众所周知JavaScript没有私有属性。 Symbol最初是为了启用私有属性而引入,但后来使用像Object.getOwnPropertySymbols这样的反射方法进行了淡化,这使得它们可以被公开发现;长期以来的惯例是将私有属性命名为前下划线_,有效地标记其不要访问。Proxy提供了一种稍微更好的方法来屏蔽这些属性

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)
    });

🔔has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符

hide函数包装目标对象,并使得从in运算符和Object.getOwnPropertyNamesObject.getOwnPropertySymbols等方法无法访问带有下划线的属性

let userData = hide({
    name: '0xGeekCat',
    age: 19,
    _gender: 'male'
})

for (let attribute in userData) {
    console.log(attribute) → name age
}

reference

ES6之Proxy 的巧用

语法 - JavaScript属性访问:点符号与括号?

js变量前的+是什么意思