李恒道 发表于 2021-7-24 17:15:01

[油猴脚本开发指南]Proxy的使用

# **本文参考**

**本文参考的是MDN文档**[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy)

**对其进行了一定的整理以及编排,以便符合讲解的顺序。**

# **Proxy是什么?**

****Proxy**** 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

**如果说数据劫持是对对象中某个属性进行了劫持,那Proxy可以理解为对一个对象进行劫持。**

# **语法以及参数**

```
const p = new Proxy(target, handler)
```

`target`**要使用 **`Proxy`** 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。``**

`handler`**一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。**

# **handler的方法**

handler.getPrototypeOf()

**是Object.getPrototypeOf方法的捕捉器。**

**当读取代理对象的原型时,使用该函数进行劫持。**

```
const handler = {
getPrototypeOf(target) {
 return {name:'8888'};
}
};

const proxy1 = new Proxy(obj, handler);
```

handler.setPrototypeOf()

**是Object.setPrototypeOf 方法的捕捉器。**

**当设置代理对象的原型时,使用该函数进行劫持,如果不想设置一个新的原型,可以返回false**

```
var handlerReturnsFalse = {
 setPrototypeOf(target, newProto) {
     return false;
 }
};

var newProto = {}, target = {};

var p1 = new Proxy(target, handlerReturnsFalse);
Object.setPrototypeOf(p1, newProto); // throws a TypeError
Reflect.setPrototypeOf(p1, newProto); // returns false
```

**handler.preventExtensions()**

**Object.preventExtensions 方法的捕捉器。**

**Object.preventExtensions 是让对象变得不可拓展的函数,当设置不可拓展时,会触发handler.preventExtensions**

**注意,如果对象是可拓展的,那么只能返回false**

```
var p = new Proxy({}, {
preventExtensions: function(target) {
 console.log('called');
 Object.preventExtensions(target);
 return true;
}
});

console.log(Object.preventExtensions(p)); // "called"
                                       // false
```

**handler.isExtensible()**

**Object.isExtensible 方法的捕捉器。**

**该方法用于拦截Object.isExtensible()。,而Object.isExtensible用于判断对象是否是一个可拓展的对象**

**注意,该函数返回必须为true**

```
var p = new Proxy({}, {
isExtensible: function(target) {
 console.log('called');
 return true;//也可以return 1;等表示为true的值
}
});

console.log(Object.isExtensible(p)); // "called"
                                    // true
```

**handler.getOwnPropertyDescriptor()**

**handler.getOwnPropertyDescriptor()方法是 Object.getOwnPropertyDescriptor()的钩子。**

**该方法用于拦截Object.getOwnPropertyDescriptor(),Object.getOwnPropertyDescriptor获取对象自有属性的描述符,自有属性指直接赋予对象的属性,而且原型链中的属性**

```
getOwnPropertyDescriptor 必须返回一个 object 或 undefined。
如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在。
如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在。
如果属性不存在作为目标对象的属性,并且目标对象不可扩展,则不能将其报告为存在。
属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在。
Object.getOwnPropertyDescriptor(target)的结果可以使用 Object.defineProperty 应用于目标对象,也不会抛出异常。
```

```
var p = new Proxy({ a: 20}, {
getOwnPropertyDescriptor: function(target, prop) {
 console.log('called: ' + prop);
 return { configurable: true, enumerable: true, value: 10 };
}
});

console.log(Object.getOwnPropertyDescriptor(p, 'a').value); // "called: a"
                                                         // 10
```

**configurable**

**当且仅当该属性的 **`configurable`** 键值为 **`true`** 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。****
******默认为****** ****`false`****。**

**enumerable**

**当且仅当该属性的 **`enumerable`** 键值为 **`true`** 时,该属性才会出现在对象的枚举属性中。****
******默认为 **`false`****。**

**以下代码则违反了规则。**

```
var obj = { a: 10 };
Object.preventExtensions(obj);
var p = new Proxy(obj, {
getOwnPropertyDescriptor: function(target, prop) {
 return undefined;
}
});

Object.getOwnPropertyDescriptor(p, 'a'); // TypeError is thrown
```

**handler.defineProperty(**

**Object.defineProperty 方法的捕捉器。**

**Object.defineProperty是啥我就不多说了**

```
var p = new Proxy({}, {
defineProperty: function(target, prop, descriptor) {
 console.log('called: ' + prop);
 return true;
}
});

var desc = { configurable: true, enumerable: true, value: 10 };
Object.defineProperty(p, 'a', desc); // "called: a"
```

**handler.has()**

**in操作符的捕捉器。**

**如果指定的属性在指定的对象或其原型链中,则****`in`** 运算符******返回**`true`**。**

```
const handler1 = {
has(target, key) {
 if (key === '_') {
   return false;
 }
 return key in target;
}
};

const monster1 = {
_secret: 'easily scared',
eyeCount: 4
};

const proxy1 = new Proxy(monster1, handler1);
console.log('eyeCount' in proxy1);
// expected output: true

console.log('_secret' in proxy1);
// expected output: false

console.log('_secret' in monster1);
// expected output: true

```

**handler.get()**

**属性读取操作的捕捉器。get的意思与Object.defineProperty 中的get一致**

**需要注意的是**

```
如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同。
如果要访问的目标属性没有配置访问方法,即get方法是undefined的,则返回值必须为undefined。
```

```
var p = new Proxy({}, {
get: function(target, prop, receiver) {
 console.log("called: " + prop);
 return 10;
}
});

console.log(p.a); // "called: a"
               // 10
```

**handler.set()**

**属性设置操作的捕捉器。set的意思与Object.defineProperty 中的set一致**

```
const monster1 = { eyeCount: 4 };

const handler1 = {
set(obj, prop, value) {
 if ((prop === 'eyeCount') && ((value % 2) !== 0)) {
   console.log('Monsters must have an even number of eyes');
 } else {
   return Reflect.set(...arguments);
 }
}
};

const proxy1 = new Proxy(monster1, handler1);

proxy1.eyeCount = 1;
// expected output: "Monsters must have an even number of eyes"

console.log(proxy1.eyeCount);
// expected output: 4

proxy1.eyeCount = 2;
console.log(proxy1.eyeCount);
// expected output: 2

```

**handler.deleteProperty()**

**delete操作符的捕捉器。**

** ****`delete`** 操作符******用于删除对象的某个属性;如果没有指向这个属性的引用,那它最终会被释放。**

**如果目标对象的属性是不可配置的,那么该属性不能被删除。**

```
var p = new Proxy({}, {
deleteProperty: function(target, prop) {
 console.log('called: ' + prop);
 return true;
}
});

delete p.a; // "called: a"
```

**handler.ownKeys()**

**Object.getOwnPropertyNames方法和 Object.getOwnPropertySymbols方法的捕捉器。**

**`Object.getOwnPropertyNames()`****方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。**

```
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

// 类数组对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]
```

`Object.getOwnPropertySymbols()`** 方法返回一个给定对象自身的所有 Symbol 属性的数组。(常规开发涉及较少,这里就不做讲解了)**

**约束**

```
ownKeys 的结果必须是一个数组.
数组的元素类型要么是一个 String ,要么是一个 Symbol.
结果列表必须包含目标对象的所有不可配置(non-configurable )、自有(own)属性的key.
如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有(own)属性的key,不能有其它值.
```

```
var p = new Proxy({}, {
ownKeys: function(target) {
 console.log('called');
 return ['a', 'b', 'c'];
}
});

console.log(Object.getOwnPropertyNames(p)); // "called"
                                         // [ 'a', 'b', 'c' ]
```

```
var obj = {};
Object.defineProperty(obj, 'a', {
configurable: false,
enumerable: true,
value: 10 }
);

var p = new Proxy(obj, {
ownKeys: function(target) {
 return ];
}
});

console.log(Object.getOwnPropertyNames(p));

// TypeError: proxy [] 必须返回一个数组
// 数组元素类型只能是String或Symbol
```

**handler.apply()**

**函数调用操作的捕捉器。**

**`handler.apply`**** 方法用于拦截函数的调用。**

```
var p = new Proxy(function() {}, {
apply: function(target, thisArg, argumentsList) {
 console.log('called: ' + argumentsList.join(', '));
 return argumentsList + argumentsList + argumentsList;
}
});

console.log(p(1, 2, 3)); // "called: 1, 2, 3"
                        // 6
```

**handler.construct()**

**new操作符的捕捉器。**

`handler.construct()`** 方法用于拦截new 操作符. 为了使new操作符在生成的Proxy对象上生效,用于初始化代理的目标对象自身必须具有[]内部方法(即 **`new target`** 必须是有效的)。**

**注意,必须返回一个对象**

```
var p = new Proxy(function() {}, {
construct: function(target, argumentsList, newTarget) {
 console.log('called: ' + argumentsList.join(', '));
 return { value: argumentsList * 10 };
}
});

console.log(new p(1).value); // "called: 1"
                            // 10
```

**下面的代码违反了约定.**

```
var p = new Proxy(function() {}, {
construct: function(target, argumentsList, newTarget) {
 return 1;
}
});

new p(); // TypeError is thrown
```

**下面的代码未能正确的初始化Proxy。Proxy初始化时,传给它的**`target`** 必须具有一个有效的constructor供**`new`**操作符调用。**

```
var p = new Proxy({}, {
construct: function(target, argumentsList, newTarget) {
 return {};
}
});

new p(); // TypeError is thrown, "p" is not a constructor
```

# **结语**

**那么到这里你已经了解Proxy的handle函数以及操作方法,如果有不明白的可以先放一放,了解如何使用就可以了,以后在使用的过程中可以慢慢查阅。**

**祝大家有一个愉快的油猴编程之旅**

viewtheard 发表于 2022-11-27 20:28:30

苦涩难懂,不能精简下,不要超过10行。{:4_113:}

李恒道 发表于 2022-11-27 20:31:04

viewtheard 发表于 2022-11-27 20:28
苦涩难懂,不能精简下,不要超过10行。

越到后边越复杂的
proxy相比可能算入门级的了....
页: [1]
查看完整版本: [油猴脚本开发指南]Proxy的使用