上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

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

[复制链接]

182

主题

1388

帖子

757

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
757
发表于 2021-7-24 17:15:01 | 显示全部楼层 | 阅读模式

本文参考

本文参考的是MDN文档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[0] === '_') {
      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 [123, 12.5, true, false, undefined, null, {}, []];
  }
});

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

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

handler.apply()

函数调用爱作的捕捉器。

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

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

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

handler.construct()

new爱作符的捕捉器。

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

注意,必须返回一个对象

var p = new Proxy(function() {}, {
  construct: function(target, argumentsList, newTarget) {
    console.log('called: ' + argumentsList.join(', '));
    return { value: argumentsList[0] * 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函数以及爱作方法,如果有不明白的可以先放一放,了解如何使用就可以了,以后在使用的过程中可以慢慢查阅。

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

混的人。

发表回复

本版积分规则

快速回复 返回顶部 返回列表