Skip to content

Proxy

创建一个代理对象,拦截对目标对象的访问。

使用方法

js
const p = new Proxy(target, handler);

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

handler: 一个对象,其属性是当执行一个操作时定义代理的行为的函数。

1.handler.getPrototypeOf()

Object.getPrototypeOf 方法的捕捉器。

拦截目标对象的原型。当使用下面四种方法时,会调用此方法。

  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof
  • obj.__proto__
ts
const monster1 = {
	eyeCount: 4,
}

const monsterPrototype = {
	eyeCount: 2,
}

const proxy1 = new Proxy(monster1, {
	getPrototypeOf(target) {
		return monsterPrototype
	},
})

console.log(Object.getPrototypeOf(proxy1) === monsterPrototype) // true

console.log(Object.getPrototypeOf(proxy1).eyeCount) // 2

console.log(proxy1.eyeCount) // 4

2. handler.setPrototypeOf()

Object.setPrototypeOf 方法的捕捉器。

下面两种方法会调用此方法。

  • Object.setPrototypeOf()
  • Reflect.setPrototypeOf()
ts
const newProto = {}
const target = {}

const p1 = new Proxy(target, {
	setPrototypeOf(target, newProto) {
		return false // 不允许设置原型 如果允许的话 可以返回 Reflect.setPrototypeOf(target, newProto)
	},
})
Object.setPrototypeOf(p1, newProto) // throws a TypeError
Reflect.setPrototypeOf(p1, newProto) // returns false

3. handler.isExtensible()

Object.isExtensible 方法的捕捉器。 判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。

下面两种方法会调用此方法。

  • Object.isExtensible()
  • Reflect.isExtensible()
ts
const monster1 = {
	canEvolve: true,
}

const handler1 = {
	isExtensible(target) {
		return Reflect.isExtensible(target)
	},
	preventExtensions(target) {
		target.canEvolve = false
		return Reflect.preventExtensions(target)
	},
}

const proxy1 = new Proxy(monster1, handler1)

console.log(Object.isExtensible(proxy1)) // true

console.log(monster1.canEvolve) // true

Object.preventExtensions(proxy1)

console.log(Object.isExtensible(proxy1)) // false

console.log(monster1.canEvolve) // false

4. handler.preventExtensions()

Object.preventExtensions 方法的捕捉器。(使对象变得不可扩展,也就是永远不能再添加新的属性。)

下面两种方法会调用此方法。

  • Object.preventExtensions()
  • Reflect.preventExtensions()

5. handler.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor 方法的捕捉器。(获取单个的属性描述)

下面两种方法会调用此方法。

  • Object.getOwnPropertyDescriptor()
  • Reflect.getOwnPropertyDescriptor()
ts
const 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"; output 10

6. handler.defineProperty()

Object.defineProperty 方法的捕捉器。(定义对象的内部属性)

下面两种方法会调用此方法。

  • Object.defineProperty()
  • Reflect.defineProperty()
ts
const obj = {}
const p = new Proxy(obj, {
	defineProperty: function (target, prop, descriptor) {
		console.log('called: ' + prop)
		return true
	},
})

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

7. handler.has()

in 操作符的捕捉器。

下面两种方法会调用此方法。

  • in 操作符
  • Reflect.has()
ts
const monster1 = {
	_secret: 'easily scared',
	eyeCount: 4,
}

const proxy1 = new Proxy(monster1, {
	has(target, key) {
		if (key[0] === '_') {
			return false
		}
		return key in target
	},
})
console.log('eyeCount' in proxy1) // true

console.log('_secret' in proxy1) // false

console.log('_secret' in monster1) // true

8. handler.get()

属性访问的捕捉器。

下面两种方法会调用此方法。

  • 属性访问
  • Reflect.get()
ts
const p = new Proxy(
	{},
	{
		get: function (target, prop, receiver) {
			console.log('called: ' + prop)
			return 10
		},
	}
)

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

9. handler.set()

属性设置的捕捉器。

下面两种方法会调用此方法。

  • 属性设置
  • Reflect.set()
ts
const monster1 = { eyeCount: 4 }

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

proxy1.eyeCount = 1 // Monsters must have an even number of eyes

console.log(proxy1.eyeCount) // 4

proxy1.eyeCount = 2
console.log(proxy1.eyeCount) // 2

10. handler.deleteProperty()

delete 操作符的捕捉器。

下面两种方法会调用此方法。

  • delete 操作符
  • Reflect.deleteProperty()
ts
const p = new Proxy(
	{},
	{
		deleteProperty: function (target, prop) {
			console.log('called: ' + prop)
			return true
		},
	}
)

delete p.a // "called: a"

11. handler.ownKeys()

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

Object.getOwnPropertyNames: 返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。(不包含Symbol类型)

Object.getOwnPropertySymbols: 返回一个给定对象自身的所有 Symbol 属性的数组。

访问下面四种方法时会调用此方法。

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • Reflect.ownKeys()
ts
const monster1 = {
	_age: 111,
	[Symbol('secret')]: 'I am scared!',
	eyeCount: 4,
}

const proxy1 = new Proxy(monster1, {
	ownKeys(target) {
		console.log('ownKeys', Reflect.ownKeys(target)) // [ '_age', 'eyeCount', Symbol(secret) ]
		return Reflect.ownKeys(target)
	},
})

for (const key of Object.keys(proxy1)) {
	console.log(key)
	// Expected output: "_age"
	// Expected output: "eyeCount"
}

12. handler.apply()

函数调用的捕捉器。

下面三种方法会调用此方法。

  • 函数调用
  • Reflect.apply()
  • Function.prototype.apply() 和 Function.prototype.call()
ts
function sum(a, b) {
	return a + b
}

const proxy1 = new Proxy(sum, {
	apply: function (target, thisArg, argumentsList) {
		console.log(`Calculate sum: ${argumentsList}`)
		// Expected output: "Calculate sum: 1,2"

		return target(argumentsList[0], argumentsList[1]) * 10
	},
})

console.log(sum(1, 2))
// Expected output: 3
console.log(proxy1(1, 2))
// Expected output: 30

13. handler.construct()

new 操作符的捕捉器。

下面两种方法会调用此方法。

  • new 操作符
  • Reflect.construct()
ts
function monster1(disposition) {
	this.disposition = disposition
}

const proxy1 = new Proxy(monster1, {
	construct(target, args) {
		console.log(`Creating a ${target.name}`)
		// Expected output: "Creating a monster1"

		return new target(...args)
	},
})

console.log(new proxy1('fierce').disposition)
// Expected output: "fierce"

总结

总共有13个捕捉器

  1. getPrototypeOf
  2. setPrototypeOf
  3. isExtensible
  4. preventExtensions
  5. getOwnPropertyDescriptor
  6. defineProperty
  7. has
  8. get
  9. set
  10. deleteProperty
  11. ownKeys
  12. apply
  13. construct

参考

MDN Proxy