Proxy 的基本定义

Proxy 这个词本身就是代理的意思,在这里可以理解它为一个代理器。在 JavaScript 中,Proxy 对象用于在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

Proxy 的参数解析

new Proxy(target, handler)

- target:参数表示所要拦截的目标对象。

- handler:参数也是一个配置对象,用来定制拦截行为。如果 handler 没有设置任何拦截,那就等同于直接通向原对象。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

Proxy 的常见拦截操作

1. get 方法

get(target, propKey, receiver)

- target:参数表示目标对象。

- propKey:参数表示属性名。

- receiver:参数表示 proxy 实例本身(严格地说,是操作行为所针对的对象,也就是所谓的接收器),其中最后一个参数可选。

2. set 方法

set(target, property, value, receiver)

- target:参数表示目标对象。

- property:参数表示要设置的属性名称。

- value:参数表示要赋予属性的值。

- receiver:参数表示最初被调用的对象,通常是代理对象。

3. has 方法

has() 方法用来拦截 HasProperty 操作,即判断对象是否具有某个属性时,这个方法会生效典型操作就是 in 运算符,一般用于隐藏某些属性,不被 in 运算符发现。

根据提供的内容,我为您完成了重构并保持了段落结构。请查看以下内容:

```javascript

// 4. deleteProperty方法

deleteProperty 方法用于拦截 delete 操作。如果这个方法抛出错误或者返回 false,当前属性就无法被 delete 命令删除。

const handler = {

deleteProperty(target, key) {

invariant(key, "delete");

delete target[key];

return true;

},

};

const invariant = (key, action) => {

if (key[0] === "_") {

throw new Error(`can't ${action} to private ` + `'${key}' property`);

}

};

let target = { _prop: "hah", name: "zs" };

let proxy = new Proxy(target, handler);

delete proxy._prop; // can't delete to private '_prop' property

// 5. apply方法

apply(target, object, args) 可以接受三个参数,依次为:this、object、args。

```

以下是重构后的代码:

```javascript

// 目标函数

function target() {

console.log("目标函数被调用");

return "res";

}

// 使用 Proxy 实现私有变量的 getset

const handler = {

get(target, key) {

if (key.charAt(0) !== "_") {

console.log("参数列表:", arguments);

}

return Reflect.get(target, key);

},

set(target, key, value) {

if (key.charAt(0) !== "_") {

console.log("设置值:", value);

}

return Reflect.set(target, key, value);

},

};

const targetProxy = new Proxy(target, handler);

console.log(targetProxy()); // res

targetProxy.name = "saucxs"; // 设置值:saucxs

console.log(targetProxy.name); // "saucxs"

```

在上述代码中,我们声明了两个私有变量:`_prop` 和 `_userListId`,以便在 `userList` 对象的内部方法中进行调用,但不希望从外部访问或修改 `userList._prop` 和 `userList._userListId`。为了实现这个目标,我们使用了代理(Proxy)对象来拦截属性操作。以下是重构后的代码:

```javascript

let userList = {

_prop: {},

_userListId: "123abc",

getUsers: function () {},

getUser: function (userId) {},

setUser: function (userId, config) {},

};

const RESTRICTED = ["_prop", "_userListId"];

userList = new Proxy(userList, {

get(target, key, proxy) {

if (RESTRICTED.indexOf(key) > -1) {

throw Error(`${key} is restricted. Can't get to private '$${key}' property.`);

}

return Reflect.get(target, key, proxy);

},

set(target, key, value, proxy) {

if (RESTRICTED.indexOf(key) > -1) {

throw Error(`${key} is restricted. Can't set to private '$${key}' property.`);

}

return Reflect.get(target, key, value, proxy);

},

});

// 下面的操作都会抛出错误

console.log(userList._userListId); // 这里会抛出错误,因为 _userListId 是私有的

userList._userListId = "123abcad"; // 这里也会抛出错误,因为 _userListId 是私有的

```

通过这种方式,我们确保了 `userList._prop` 和 `userList._userListId` 只能够在对象的内部方法中访问和修改。如果尝试从外部获取或设置这些私有属性,将会触发错误。

链式调用

在 JavaScript 中,我们可以使用 `Proxy` 对象来实现链式调用。以下是一个简单的示例:

```javascript

const Chainable = function(obj) {

return new Proxy(obj, {

get(target, prop) {

if (prop in target) {

if (typeof target[prop] === 'function') {

return (...args) => {

target[prop](...args);

return Chainable(target);

};

}

return Chainable(target[prop]);

}

throw new Error(`Property ${prop} not found`);

}

});

};

const api = {

add(x) {

this.value += x;

},

subtract(x) {

this.value -= x;

},

getValue() {

console.log(this.value);

}

};

const chain = Chainable(api);

chain.add(5).subtract(3).getValue(); // Output: 2

```

在这个示例中,我们创建了一个名为 `Chainable` 的函数,它接受一个对象作为参数,并返回一个代理对象。当我们访问代理对象的属性时,它会拦截属性访问操作,并在调用属性方法后返回代理对象,以便可以继续在代理对象上进行属性访问或方法调用。这样我们就可以实现链式调用。

```javascriptlet userInfo = {

id: 0,

username: "abc",

password: 14,

};

userInfo = new Proxy(userInfo, {

set(target, key, value, proxy) {

if (key === "username" && typeof value !== "string") {

throw Error(`${key} in userInfo can only be string`);

}

return Reflect.set(target, key, value, proxy);

},

});

// 抛出错误:username in userInfo can only be string

userInfo.username = 123; // 赋值成功

userInfo.username = 333; // 抛出错误:username in userInfo can only be string

```

以下是内容重构的代码:

```javascript

const cacheFun = (n) => {

const cacheData = new Map();

return (n) => {

if (cacheData.has(n)) {

console.log("data from cache");

return cacheData.get(n);

}

const result = n * n;

cacheData.set(n, result);

return result;

};

};

const proxyFunction = new Proxy(cacheFun(), {

apply(target, thisArg, args) {

console.log("Using cache...");

return target(...args);

},

});

console.log(proxyFunction(5)); // Using cache...

console.log(proxyFunction(5)); // data from cache...

```