原型模式
只要是个函数对象, 就会有原型类型的对象(prototype), 原型对象类似于 java 的静态属性, 所有对象共享
// function Person() {}
let Person = function () {
}
Person.prototype.name = "haha";
Person.prototype.age = 22;
Person.prototype.sayName = function () {
console.log(this.name)
}
let person1 = new Person();
let person2 = new Person();
console.log(person1.age === person2.age); // true
console.log(person1.name === person2.name); // true
person1.sayName() // haha
person2.sayName() // haha
原型对象有个属性叫 Person.prototype.constructtor 指向我们的构造函数
Person.prototype.constructor === Person // true
上面的原型对象的获取方式都是针对的构造函数, 而不是对象
每次构造函数构造一个对象(实例)时, 实例内部存在一个指针[[Prototype]], 都会指向构造函数的原型对象
console.log(person1.__proto__ === Person.prototype) // true
但这个仅部分浏览器支持: firefox, chrome, Safari等
我们还可以套娃 __proto__
console.log(person1.__proto__ === Person.prototype) // true
console.log(person1.__proto__.__proto__) // Object构造函数的原型对象, [Object: null prototype] {}
console.log(person1.__proto__.__proto__.__proto__) // null
console.log(Object.prototype) // [Object: null prototype] {}
console.log(Object.prototype.prototype) // null
let object = {};
console.log(object.__proto__) //[Object: null prototype] {}
console.log(object.__proto__.__proto__) // null
Object 构造函数的原型
__proto__的原型__proto__是null
我们还可以通过别的方式获得对象的原型对象
Object.getPrototypeOf(person1) === Person.prototype // true
原型对象实现继承
/**
* 父类构造函数, 添加字段
* @param name
* @constructor
*/
function SuperType(name) {
this.name = name;
this.colors = ["red", "green", 'blue'];
}
/**
* 利用原型对象给父类添加方法
*
* 至此给父类添加属性和方法完毕
*/
SuperType.prototype.sayName = function () {
console.log(this.name)
}
/**
* 子类构造函数
* @param name
* @param age
* @constructor
*/
function SubType(name, age) {
// this 是 子类的 this
// 这里使用子类对象调用父类的构造函数, 传递 name 参数 类似于 super(name)
// 这里子类拿到了父类的属性
SuperType.call(this, name);
// 给子类添加 age 属性
this.age = age
}
/**
* 要让子类拿到父类的方法, 则需要让子类的原型对象是父类的原型对象
* @type {SuperType}
*/
SubType.prototype = new SuperType();
/**
* 但是父类的原型对象的构造函数是父类的, 不是子类的, 所以需要修改成子类的构造函数
* @type {SubType}
*/
SubType.prototype.constructor = SubType;
/**
* 给父类原型添加方法
*/
SubType.prototype.sayAge = function () {
console.log(this.age)
};
let subType1 = new SubType("haha", 12);
subType1.sayName()
subType1.sayAge()
/*
true
true
SubType { name: 'haha', colors: [ 'red', 'green', 'blue' ], age: 12 }
SuperType {
name: undefined,
colors: [ 'red', 'green', 'blue' ],
constructor: [Function: SubType],
sayAge: [Function (anonymous)]
}
*/
console.log(subType1 instanceof SubType);
console.log(subType1 instanceof SuperType);
console.log(subType1)
console.log(subType1.__proto__)
但是这样有一个缺点, 父类也有这个字段
寄生式组合原型继承
/**
* 父类构造函数, 添加字段
* @param name
* @constructor
*/
function SuperType(name) {
this.name = name;
this.colors = ["red", "green", 'blue'];
}
/**
* 利用原型对象给父类添加方法
*
* 至此给父类添加属性和方法完毕
*/
SuperType.prototype.sayName = function () {
console.log(this.name)
}
/**
* 子类构造函数
* @param name
* @param age
* @constructor
*/
function SubType(name, age) {
// this 是 子类的 this
// 这里使用子类对象调用父类的构造函数, 传递 name 参数 类似于 super(name)
// 这里子类拿到了父类的属性
SuperType.call(this, name);
// 给子类添加 age 属性
this.age = age
}
function object(o) {
function F(){}
F.prototype = o
return new F()
}
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
inheritPrototype(SubType, SuperType)
/**
* 给父类原型添加方法
*/
SubType.prototype.sayAge = function () {
console.log(this.age)
};
let subType1 = new SubType("haha", 12);
subType1.sayName()
subType1.sayAge()
/*
true
true
SubType { name: 'haha', colors: [ 'red', 'green', 'blue' ], age: 12 }
SuperType {
constructor: [Function: SubType],
sayAge: [Function (anonymous)]
}
*/
console.log(subType1 instanceof SubType);
console.log(subType1 instanceof SuperType);
console.log(subType1)
console.log(subType1.__proto__)
迭代器和生成器
class Person {
constructor() {
this.name = "Jack";
this.sayName = () => console.log(this.name);
this.nicknames = ['Jake', 'J-Dog'];
}
*[Symbol.iterator]() {
yield *this.nicknames.entries()
}
}
let person = new Person();
for (let [idx, value] of person) {
console.log(`index = ${idx}, value = ${value}`)
}
for (let p1 of person) {
console.log(`index = ${p1[0]}, value = ${p1[1]}`)
}
ES6引入了新的关键字
class
ES6继承
ES6 类支持单继承。使用 extends 关键字,就可以继承任何拥有[[Construct]]和原型的对象
class Vehicle {
}
class Bus extends Vehicle {
}
let bus = new Bus();
console.log(bus instanceof Vehicle)
console.log(bus instanceof Bus)
构造函数、HomeObject 和 super()
任何一个解析为类或者构造函数的表达式都可以被extends继承
class Vehicle {}
function getParentClass() {
console.log('evaluated expression')
}
// class Bus extends Vehicle {}
class Bus extends getParentClass() {
}
派生类的方法可以通过 super 关键字引用它们的原型。这个关键字只能在派生类中使用,而且仅限于类构造函数、实例方法和静态方法内部。在类构造函数中使用 super 可以调用父类构造函数。
class Vehicle {
constructor() {
console.log("constructor Vehicle")
}
static identify() {
console.log("Vehicle")
}
}
class Bus extends Vehicle {
constructor() {
super();
console.log("constructor Bus")
}
static identify() {
super.identify();
console.log("Bus")
}
}
// Vehicle.identify();
// Bus.identify();
let bus = new Bus();
Bus.identify();
constructor Vehicle
constructor Bus
Vehicle
Bus
ES6 给类构造函数和静态方法添加了内部特性
[[HomeObject]],这个特性是一个指针,指向定义该方法的对象。这个指针是自动赋值的,而且只能在 JavaScript 引擎内部访问。super始终会定义为[[HomeObject]]的原型。
类添加的方法放在prototype
class Vehicle {
constructor() {
this.prop01 = true
}
func01() {
}
}
class Bus extends Vehicle {
constructor() {
super();
this.prop02 = true
}
func02() {
}
}
let bus = new Bus();
console.log(bus instanceof Vehicle); // true
console.log(bus instanceof Bus); // true
console.log(Vehicle.prototype.func01); // [Function: func01]
console.log(Bus.prototype.func01); // [Function: func01]
console.log(Bus.prototype.func02); // [Function: func02]
let vehicle = new Vehicle();
console.log(vehicle.prop01); // true
console.log(bus.prop01); // true
console.log(bus.prop02); // true
class 的方式类的方法其实放在原型对象上了
抽象基类
new.target 可以做到抽象类类似的方法
class Vehicle {
constructor() {
console.log(new.target)
if (new.target === Vehicle) {
throw new Error("Vehicle cannot be directly instantiated")
}
}
}
class Bus extends Vehicle {
}
new Vehicle(); // Error: Vehicle cannot be directly instantiated
new Bus();
虽然实现了抽象类不能直接定义成对象的功能, 但是继承功能还是没有全部实现出来, 抽象类的部分方法派生类必须实现, 否则无法继承该抽象类
不过通过前面的章节, 我们知道类的方法一般放在类原型对象中, 所以我们可以这样实现抽象类方法必须重写的功能
class Vehicle {
constructor() {
console.log(new.target)
if (new.target === Vehicle) {
throw new Error("Vehicle cannot be directly instantiated")
}
if (!this.foo) {
throw new Error('Inheriting class must be define foo()')
}
}
}
class Bus extends Vehicle {
foo() {}
}
// new Vehicle(); // Error: Vehicle cannot be directly instantiated
new Bus();
代理和反射
代理
代理是什么?
类似于c++中的指针, 对变量做&取地址操作, 这样出来的指针就类似于代理对象, 它是目标变量的替身, 但却独立于目标变量
实际上, 代理和c++的指针不同的
代理怎么用?
最简单的方式是借助Proxy的构造函数, 传递相应的参数就能创建代理对象
const target = {
id : 'target'
}
const handler = {}
let proxy = new Proxy(target, handler);
console.log(proxy.id);
console.log(target.id);
proxy.id = 'proxy';
console.log(proxy.id);
console.log(target.id);
console.log(handler);
proxy.age = 22;
target.name = 'haha';
console.log(proxy)
console.log(target)
console.log(target === proxy); // false
捕获器
捕获器是什么?
使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在处理程序对象中定义的“基本操作的拦截器”。每个处理程序对象可以包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用, 每次在代理对象上调用目标对象的基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为
捕获器(trap)是从操作系统中借用的概念。在操作系统中,捕获器是程序流中的一个同步中断,可以暂停程序流,转而执行一段子例程,之后再返回原始程序流
捕获器怎么用?
const target = {
id: 1
}
const handler = {
get() {
// 捕获器在处理程序对象中以方法名为键
return 'handler override'
}
}
let proxy = new Proxy(target, handler);
console.log(proxy.id) // handler override
console.log(target.id) // 1
console.log(proxy['id']) // handler override
console.log(target['id']); // 1
console.log(Object.create(proxy)['id']); // handler override
console.log(Object.create(target)['id']); // 1
捕获器函数的参数
const target = {
foo: 'bar'
}
const handler = {
get(trapTarget, property, receiver) {
console.log(`trapTarget === target :::> ${trapTarget === target}`); // true
console.log(`property :::> ${property}`); // foo
console.log(`receiver === proxy :::> ${receiver === proxy}`); // true
}
}
let proxy = new Proxy(target, handler);
console.log(`proxy = ${proxy.foo}`); // undefined
console.log(`target = ${target.foo}`); // bar
捕获器参数的具体值
const target = {
foo: 'bar'
}
const handler = {
get(trapTarget, property, receiver) {
console.log(trapTarget) // { foo: 'bar' }
console.log(property) // foo
console.log(receiver) // { foo: 'bar' }
return trapTarget[property]
}
}
let proxy = new Proxy(target, handler);
console.log(`proxy = ${proxy.foo}`); // bar
console.log(`target = ${target.foo}`); // bar
借助Reflect API方式
const target = {
foo: 'bar'
};
const handler = {
get() {
// [Arguments] { '0': { foo: 'bar' }, '1': 'foo', '2': { foo: 'bar' } }
console.log(arguments)
return Reflect.get(...arguments);
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.foo); // bar
console.log(target.foo); // bar
简洁版本:
const target = {
foo: 'bar'
};
const handler = {
get : Reflect.get
};
const proxy = new Proxy(target, handler);
console.log(proxy.foo); // bar
console.log(target.foo); // bar
还可以更简单
const target = {
foo: 'bar'
};
const proxy = new Proxy(target, Reflect);
console.log(proxy.foo); // bar
console.log(target.foo); // bar
反射 API 为开发者准备好了样板代码,在此基础上开发者可以用最少的代码修改捕获的方法。
const target = {
foo: 'bar',
baz: 'qux'
}
const handler = {
get(trapTarget, property, recevier) {
let decoration = ''
if (property === 'foo') {
decoration = '!!!'
}
return Reflect.get(...arguments) + decoration
}
}
let proxy = new Proxy(target, handler);
/*
bar!!!
bar
qux
qux
*/
console.log(proxy.foo)
console.log(target.foo)
console.log(proxy.baz)
console.log(target.baz)
捕获器不变性
如果对象的属性是不可变的, 则捕获器如果返回的值不相同时, 则会报错
const target = {}
Object.defineProperty(target, 'foo', {
writable: false,
configurable: false,
value: 'bar'
});
const handler = {
get() {
return 'qux'
}
}
let proxy = new Proxy(target, handler);
console.log(proxy.foo) // TypeError: 'get' on proxy: property 'foo' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'bar' but got 'qux')
定义可撤销的代理
const target = {
foo: 'bar'
};
const handler = {
get() {
return 'intercepted'
}
}
// 定义可撤销的代理对象, 一个是代理对象, 另一个是可撤销代理关系的函数
const { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.foo) // intercepted
console.log(target.foo) // bar
console.log(revoke) // [Function (anonymous)]
revoke()
console.log(proxy.foo) // TypeError: Cannot perform 'get' on a proxy that has been revoked
反射API
反射 API 与对象 API
Object 上的方法适用于通用程序,而反射方法适用于细粒度的对象控制与操作
状态标记
状态标记: Object 的方法很多时候返回的是对象或者直接抛出异常, 但如果使用的是 Reflect 对象的话, 返回的僵尸状态标记, 要么是 false 要么是 true
const o1 = {}
try {
Object.defineProperty(o1, 'foo', 'bar');
console.log('success');
} catch (e) {
console.log(e); // TypeError: Property description must be an object: bar
console.log('failure');
}
const o2 = {}
if (Reflect.defineProperty(o2, 'foo', {value: 'bar'})) {
console.log('success');
} else {
console.log('failure')
}
以下反射方法都会提供状态标记:
Reflect.defineProperty()
Reflect.preventExtensions()
Reflect.setPrototypeOf()
Reflect.set()
Reflect.deleteProperty()
用一等函数替代操作符
以下反射方法提供只有通过操作符才能完成的操作。
Reflect.get():可以替代对象属性访问操作符。
Reflect.set():可以替代=赋值操作符。
Reflect.has():可以替代 in 操作符或 with()。
Reflect.deleteProperty():可以替代 delete 操作符。
Reflect.construct():可以替代 new 操作符。
安全地应用函数
Function.prototype.apply.call(myFunc, thisVal, argumentList);
这种可怕的代码完全可以使用 Reflect.apply 来避免:
Reflect.apply(myFunc, thisVal, argumentsList);
代理另一个代理
const target = {
foo: 'bar'
}
const firstProxy = new Proxy(target, {
get() {
console.log('first proxy');
return Reflect.get(...arguments);
}
});
const secondProxy = new Proxy(firstProxy, {
get() {
console.log('second proxy');
return Reflect.get(...arguments);
}
});
/*
second proxy
first proxy
bar
*/
console.log(secondProxy.foo);
代理的缺点
1. 代理中的 this
const target = {
thisValEqualsProxy() {
return this === proxy
}
}
let proxy = new Proxy(target, {});
console.log(target.thisValEqualsProxy()) // false
console.log(proxy.thisValEqualsProxy()) // true
目标对象的
this和 代理对象的this不是相同的
这样会有问题
let weakMap = new WeakMap();
class User {
constructor(userId) {
weakMap.set(this, userId);
}
set id(userId) {
weakMap.set(this, userId);
}
get id() {
return weakMap.get(this);
}
}
let user = new User("userId");
let proxyUser = new Proxy(user, {});
console.log(user.id) // userId
console.log(proxyUser.id) // undefined
解决方法很简单
对目标对象的构造函数进行代理
let UserConstructorProxy = new Proxy(User, {});
let constructorProxy = new UserConstructorProxy("userIdProxy");
console.log(constructorProxy.id);
这里代理的是构造函数, 不是目标对象
2. 代理与内部槽位
有些 ECMAScript 内置类型可能会依赖代理无法控制的机制,结果导致在代理上调用某些方法会出错
Date 类型方法的执行依赖 this 值上的内部槽位[[NumberDate]], 但是代理对象上没有这个内部槽位, 所以会报错
// let date = new Date();
// let proxy = new Proxy(date, {});
// console.log(proxy instanceof Date)
// console.log(proxy.getDate()) // this is not a Date object.
// 但是我们使用下面这种方式则不会报错
// 但是这样就不是代理目标对象了, 而是代理目标对象的构造函数
// 不是我们想要的, 它仅仅是对构造函数的代理
let ProxyDateConstructor = new Proxy(Date, {});
let dateConstructor = new ProxyDateConstructor();
console.log(dateConstructor.getDate());
代理捕捉器和反射方法(当字典查就行)
对于在代理对象上执行的任何一种操作,只会有一个捕获处理程序被调用。不会存在重复捕获的情况。只要在代理上调用,所有捕获器都会拦截它们对应的反射 API 操作
每次代理对象的操作, 都会有相对应的捕获器捕获目标对象相对应的反射API
get()
get()捕获器会在获取属性值的操作中被调用。对应的反射 API 方法为 Reflect.get()
const myTarget = {}
const proxy = new Proxy(myTarget, {
get() {
console.log('get()');
return Reflect.get(...arguments);
}
})
proxy.foo
只要属性被读取到, 就会调用叫
get的捕捉器
-
该捕获器的返回值不做限制
-
拦截的操作
proxy.property
proxy[property]
Object.create(proxy)[property]
Reflect.get(proxy, property, receiver)
- 捕获器处理程序参数
target: 目标对象property: 引用的目标对象上的字符串键属性。receiver:代理对象或继承代理对象的对象。
- 捕获器不变式
如果
target.property不可写且不可配置,则处理程序返回的值必须与target.property匹配。 如果target.property不可配置且[[Get]]特性为undefined,处理程序的返回值也必须是undefined。
set()
set()捕获器会在设置属性值的操作中被调用。对应的反射 API 方法为 Reflect.set()
const myTarget = {}
let proxy = new Proxy(myTarget, {
set(target, p, value, receiver) {
console.log('捕获器 set()');
// return Reflect.set(target, p, value, receiver);
return Reflect.set(...arguments);
}
});
proxy.foo = "zhazha";
- 返回值
如果设置成功返回true, 如果设置失败, 则返回false, 在严格模式下会报错
- 拦截的操作
proxy.property = valueproxy[property] = valueObject.create(proxy)[property] = valueReflect.set(proxy, property, value, receiver)
- 捕获器处理程序参数
- target:目标对象。
- property:引用的目标对象上的字符串键属性。
- value:要赋给属性的值。
- receiver:接收最初赋值的对象
- 捕获器不变式
如果 target.property 不可写且不可配置,则不能修改目标属性的值。
如果 target.property 不可配置且[[Set]]特性为 undefined,则不能修改目标属性的值。在严格模式下,处理程序中返回 false 会抛出 TypeError。
has()
has()捕获器会在 in 操作符中被调用。对应的反射 API 方法为 Reflect.has()。
- 返回值
has()必须返回布尔值,表示属性是否存在。返回非布尔值会被转型为布尔值。
- 拦截的操作
property in proxy
property in Object.create(proxy)
with(proxy) {(property);}
Reflect.has(proxy, property)
- 捕获器处理程序参数
- target:目标对象。
- property:引用的目标对象上的字符串键属性。
- 捕获器不变式
如果
target.property存在且不可配置,则处理程序必须返回true。 如果target.property存在且目标对象不可扩展,则处理程序必须返回true。
defineProperty()
defineProperty()捕获器会在 Object.defineProperty()中被调用。对应的反射 API 方法为 Reflect.defineProperty()。
const myTarget = {}
let proxy = new Proxy(myTarget, {
defineProperty(target, p, attributes) {
console.log('defineProperty()');
return Reflect.defineProperty(...arguments)
}
});
Object.defineProperty(proxy, 'foo', {value: 'bar'})
getOwnPropertyDescriptor()
如果不知道捕捉器怎么写, 我们还有个技巧
比如: getOwnPropertyDescriptor 这个函数也是一个捕捉器, 会在 Object.getOwnPropertyDescriptor()中被调用。对应的反射 API 方法为 Reflect.getOwnPropertyDescriptor()。
如果我们不知道捕捉器的参数怎么写? 可以这么做
我们点击进去
const myTarget = {
foo: "bar"
}
let proxy = new Proxy(myTarget, {
// getOwnPropertyDescriptor?(target: T, p: string | symbol): PropertyDescriptor | undefined;
getOwnPropertyDescriptor(target, property) {
console.log('getOwnPropertyDescriptor()')
return Reflect.getOwnPropertyDescriptor(...arguments)
}
});
let descriptor = Object.getOwnPropertyDescriptor(proxy, 'foo');
// { value: 'bar', writable: true, enumerable: true, configurable: true }
console.log(descriptor)
-
返回值 getOwnPropertyDescriptor()必须返回对象,或者在属性不存在时返回 undefined。
-
拦截的操作
Object.getOwnPropertyDescriptor(proxy, property)Reflect.getOwnPropertyDescriptor(proxy, property)
- 捕获器处理程序参数
target:目标对象。property:引用的目标对象上的字符串键属性。
- 捕获器不变式
如果自有的 target.property 存在且不可配置,则处理程序必须返回一个表示该属性存在的对象。
如果自有的 target.property 存在且可配置,则处理程序必须返回表示该属性可配置的对象。
如果自有的 target.property 存在且 target 不可扩展,则处理程序必须返回一个表示该属性存在的对象。
如果 target.property 不存在且 target 不可扩展,则处理程序必须返回 undefined 表示该属性不存在。
如果 target.property 不存在,则处理程序不能返回表示该属性可配置的对象。
deleteProperty()
deleteProperty()捕获器会在 delete 操作符中被调用。对应的反射 API 方法为 Reflect.deleteProperty()。
const myTarget = {};
const proxy = new Proxy(myTarget, {
deleteProperty(target, property) {
console.log('deleteProperty()');
return Reflect.deleteProperty(...arguments)
}
});
delete proxy.foo
- 返回值
deleteProperty()必须返回布尔值,表示删除属性是否成功。返回非布尔值会被转型为布尔值。
- 拦截的操作
delete proxy.propertydelete proxy[property]Reflect.deleteProperty(proxy, property)
- 捕获器处理程序参数
target:目标对象。property:引用的目标对象上的字符串键属性。
- 捕获器不变式
如果自有的 target.property 存在且不可配置,则处理程序不能删除这个属性。
ownKeys()
ownKeys()捕获器会在 Object.keys()及类似方法中被调用。对应的反射 API 方法为 Reflect.ownKeys()。
const myTarget = {};
const proxy = new Proxy(myTarget, {
ownKeys(target) {
console.log('ownKeys()');
return Reflect.ownKeys(...arguments)
}
});
Object.keys(proxy);
- 返回值
ownKeys()必须返回包含字符串或符号的可枚举对象。
- 拦截的操作
Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)Reflect.ownKeys(proxy)
- 捕获器处理程序参数
- target:目标对象。
- 捕获器不变式
返回的可枚举对象必须包含 target 的所有不可配置的自有属性。
如果 target 不可扩展,则返回可枚举对象必须准确地包含自有属性键。
getPrototypeOf()
getPrototypeOf()捕获器会在 Object.getPrototypeOf()中被调用。对应的反射 API 方法为Reflect.getPrototypeOf()。
const myTarget = {};
const proxy = new Proxy(myTarget, {
getPrototypeOf(target) {
console.log('getPrototypeOf()');
return Reflect.getPrototypeOf(...arguments)
}
});
Object.getPrototypeOf(proxy);
// getPrototypeOf()
- 返回值
getPrototypeOf()必须返回对象或 null。
- 拦截的操作
Object.getPrototypeOf(proxy)Reflect.getPrototypeOf(proxy)proxy.__proto__Object.prototype.isPrototypeOf(proxy)proxy instanceof Object
- 捕获器处理程序参数
- target:目标对象。
- 捕获器不变式
如果 target 不可扩展,则 Object.getPrototypeOf(proxy)唯一有效的返回值就是 Object.getPrototypeOf(target)的返回值。
setPrototypeOf()
setPrototypeOf()捕获器会在 Object.setPrototypeOf()中被调用。对应的反射 API 方法为Reflect.setPrototypeOf()。
const myTarget = {};
const proxy = new Proxy(myTarget, {
setPrototypeOf(target, prototype) {
console.log('setPrototypeOf()');
return Reflect.setPrototypeOf(...arguments)
}
});
Object.setPrototypeOf(proxy, Object);
// setPrototypeOf()
- 返回值
setPrototypeOf()必须返回布尔值,表示原型赋值是否成功。返回非布尔值会被转型为布尔值。
- 拦截的操作
Object.setPrototypeOf(proxy)Reflect.setPrototypeOf(proxy)
捕获器处理程序参数
target:目标对象。prototype:target的替代原型,如果是顶级原型则为null。
- 捕获器不变式
如果 target 不可扩展,则唯一有效的 prototype 参数就是Object.getPrototypeOf(target)的返回值。
isExtensible()
isExtensible()捕获器会在 Object.isExtensible()中被调用。对应的反射 API 方法为Reflect.isExtensible()。
const myTarget = {};
const proxy = new Proxy(myTarget, {
isExtensible(target) {
console.log('isExtensible()');
return Reflect.isExtensible(...arguments)
}
});
Object.isExtensible(proxy);
// isExtensible()
- 返回值
isExtensible()必须返回布尔值,表示 target 是否可扩展。返回非布尔值会被转型为布尔值。
- 拦截的操作
Object.isExtensible(proxy)Reflect.isExtensible(proxy)
- 捕获器处理程序参数
target:目标对象。
- 捕获器不变式
如果 target 可扩展,则处理程序必须返回 true。
如果 target 不可扩展,则处理程序必须返回 false。
preventExtensions()
preventExtensions()捕获器会在 Object.preventExtensions()中被调用。对应的反射 API方法为 Reflect.preventExtensions()。
const myTarget = {};
const proxy = new Proxy(myTarget, {
preventExtensions(target) {
console.log('preventExtensions()');
return Reflect.preventExtensions(...arguments)
}
});
Object.preventExtensions(proxy);
// preventExtensions()
- 返回值
preventExtensions()必须返回布尔值,表示 target 是否已经不可扩展。返回非布尔值会被转
型为布尔值。
- 拦截的操作
Object.preventExtensions(proxy)Reflect.preventExtensions(proxy)
- 捕获器处理程序参数
target:目标对象。
- 捕获器不变式
如果 Object.isExtensible(proxy)是 false,则处理程序必须返回 true。
apply()
apply()捕获器会在调用函数时中被调用。对应的反射 API 方法为Reflect.apply()。
const myTarget = () => {};
const proxy = new Proxy(myTarget, {
apply(target, thisArg, ...argumentsList) {
console.log('apply()');
return Reflect.apply(...arguments)
}
});
proxy();
// apply()
- 返回值
返回值无限制。
- 拦截的操作
proxy(...argumentsList)Function.prototype.apply(thisArg, argumentsList)Function.prototype.call(thisArg, ...argumentsList)Reflect.apply(target, thisArgument, argumentsList)
- 捕获器处理程序参数
target:目标对象。thisArg:调用函数时的this参数。argumentsList:调用函数时的参数列表
- 捕获器不变式
target 必须是一个函数对象。
construct()
construct()捕获器会在 new 操作符中被调用。对应的反射 API 方法为 Reflect.construct()。
const myTarget = function() {};
const proxy = new Proxy(myTarget, {
construct(target, argumentsList, newTarget) {
console.log('construct()');
return Reflect.construct(...arguments)
}
});
new proxy;
// construct()
- 返回值
construct()必须返回一个对象。
- 拦截的操作
new proxy(...argumentsList)Reflect.construct(target, argumentsList, newTarget)
- 捕获器处理程序参数
target:目标构造函数。argumentsList:传给目标构造函数的参数列表。newTarget:最初被调用的构造函数。
- 捕获器不变式
target 必须可以用作构造函数。