Proxy

2021/12/11

Proxy

  • Proxy:用于创建一个对象的代理,从而实现对目标对象操作的拦截。
  • target:被代理的对象,一般理解为存储后端。
  • handler:处理器对象。
  • traps:handler提供的拦截原生操作方法。

# 声明

# 构造函数

  • 调用:new Proxy(target, handler)
  • 入参:Object, Object

  • get入参的属性名默认时String类型:get(target: object, name: string) {}
  • 数组要将String转化为Number才能计算下标
  • 创造一个有默认属性值的对象




 
 
 
 
 




const obj = {
    name: 'hdy',
    age: 18
}
const proxy = new Proxy(obj, {
    get (target, name) {
        return target[name] ? target[name] : `还未定义${name}属性`;
    }
})
console.log(proxy.name); // hdy
console.log(proxy.age); // 18
console.log(proxy.height); // 还未定义height属性
复制成功
1
2
3
4
5
6
7
8
9
10
11
12

# revocable

  • 作用:创建一个可撤销的代理对象
  • 调用:Proxy.revocable(target, handler)
  • 入参:Object, Object
  • 返回:{ proxy: proxy, revoke: revokeFn }
  • tip:和new Proxy()创建的一样,只是这个可以被删除






 

 







 
 

const ans = Proxy.revocable({}, {
    get (target, name) {
        return `${name}: hdy`;
    }
})

console.log(ans); // { proxy: {}, revoke: [Function (anonymous)] }

const proxy = ans.proxy;
console.log(proxy.ownner); // ownner: hdy
console.log(proxy.name); // name: hdy

/**
 *  撤销以后再用就报错:
 *  Cannot perform 'get' on a proxy that has been revoked
 */
ans.revoke();
// console.log(proxy.name); // error
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Reflect

  • Reflect提供JS原生操作,并且只有静态方法
  • Reflect拥有的静态方法都和handler提供的方法一一对应,可以用来proxy劫持操作后再执行target原生JS操作

# this

  • handler里面的traps函数里的this已经被绑定到handler上了。不是proxy也不是target也不是receiver


 






const handler = {
    set(target, key, value, receiver) {
        console.log(this === handler); // true
        Reflect.set(target, key, value, receiver);
    }
}
const proxy = new Proxy({}, handler);
proxy.name = 'hdy';
复制成功
1
2
3
4
5
6
7
8

# handler方法

# get

  • 作用:拦截读取属性
  • 定义:get(target, prop, receiver)
  • 入参:Object, String, Object
  • 返回:any
  • 调用:proxy.XX | proxy[XX]
  • receiver:如果target里面有getter,那么getter里面的this就是receiver

约束

  1. 如果target属性的writable是false,则proxy不可改动返回值
  2. 如果target属性的get是undefined,那么proxy也要返回undefined
  • 获取属性加上标识


 





const proxy = new Proxy({ age: 19 }, {
    get(target, prop, proxy) {
        return `读取到${prop}的属性值是${target[prop]}`;
    }
})

console.log(proxy.age); // 读取到age的属性值是19
复制成功
1
2
3
4
5
6
7

# set

  • 作用:劫持赋值操作
  • 定义:set(target, key, value, receiver)
  • 入参:Object, String, any, Object
  • 返回:Boolean
  • 调用:proxy.key = value

receiver

  • 直接调用proxy是proxy本身
  • proxy作为原型链,后代对象set属性也会调用此劫持,但receiver是后代对象
const proxy = new Proxy({}, {
    set(target, key, value, receiver) {
        console.log('劫持操作');
        Reflect.set(target, key, value, receiver);
    }
})

console.log(proxy.name = 'hdy');
/**
 *   劫持操作
 *   hdy
 */
复制成功
1
2
3
4
5
6
7
8
9
10
11
12

# has

  • 作用:针对【in】方法的劫持
  • 定义:has(target, key)
  • 入参:Object, String
  • 返回:Boolean
  • 调用:key in target
















 
 
 









const obj = Object.defineProperties(
    {
        ['_name']: 'hdy'
    },
    {
        name: {
            get() {
                return this['_name'];
            },
            set(value) {
                this['_name'] = value;
            }
        }
    }
)
const proxy = new Proxy(obj, {
    has(target, key) {
        return key[0] !== '_';
    }
})

console.log('_name' in obj);   // true
console.log('_name' in proxy); // false

console.log(proxy.name); // hdy
proxy.name = '张三';
console.log(proxy.name); // 张三
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# apply

  • 作用:劫持函数调用操作
  • 定义:apply(target, thisArg, arguments)
  • 入参:Object, Object, Array
  • 返回:any
  • 调用:proxy() | proxy.apply() | proxy.call()
  • tip:被代理的一定要是函数,否则自身都没有apply方法
  • 将所有入参加上代理【(proxy)】标识
function say(word, word2) {
    console.log(`I said ${word} and ${word2}!`);
}
const proxy = new Proxy(say, {
    apply(target, thisArg, args) {
        args = args.map(item => `(proxy)${item}`);
        return target.call(thisArg, ...args);
    }
})

proxy('hdy', 'cool'); // I said (proxy)hdy and (proxy)cool!
复制成功
1
2
3
4
5
6
7
8
9
10
11

# ownKeys

  • 作用:劫持获取对象keys值操作
  • 定义:ownKeys(target)
  • 入参:Object
  • 返回:Iterator
  • 调用:
    1. Reflect.ownKeys(proxy)
    2. Object.keys(proxy)
    3. Object.getOwnPropertyNames(proxy)
    4. Object.getOwnPropertySymbols(proxy)

  • 必须返回一个可枚举对象






 
 
 



const obj = {
    name: 'hdy',
    age: 18,
    ['_height']: 180
}
const proxy = new Proxy(obj, {
    ownKeys(target) {
        return Reflect.ownKeys(target).filter(item => !item.startsWith('_'));
    }
})
console.log(Object.keys(proxy)); // [ 'name', 'age' ]
复制成功
1
2
3
4
5
6
7
8
9
10
11

# construct

  • 作用:劫持原生的constructor函数
  • 定义:construct(target, arguments, newTarget)
  • 入参:Object, Array, Object(本Proxy对象)
  • 返回:Object
  • 调用:new proxy(args);
  • tip:target必须有constructor(是函数\类)
  • 劫持new操作符,做更多的事
class People {
    constructor() {
        this.head = 1;
    }
}
const proxy = new Proxy(People, {
    construct(target, args, newTarget) {
        const obj = new target(...args);
        obj.hand = 2;
        obj.foot = 2;
        return obj;
    }
})

const a = new proxy();
console.log(a); // People { head: 1, hand: 2, foot: 2 }
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# getPropertypeOf

  • 作用:劫持访问原型链对象操作
  • 定义:getPropertypeOf(target)
  • 入参:Object
  • 返回:Object | null
  • 调用:
    1. Object.getPropertyOf(proxy)
    2. Reflect.getPrototypeOf(proxy)
    3. instanceof
    4. proxy._ _ proto _ _
    5. obj.isPrototypeOf(proxy)

限制

  • 必须返回Object或null
  • target不可扩展的情况下禁止撒谎
  • 撒谎





 



 
 

const father = { name: 'hdy' };
const child = Object.create(father);

const proxy = new Proxy(child, {
    getPrototypeOf(target) {
        return { value: '就不告诉你我爹是谁' };
    }
})

console.log(Object.getPrototypeOf(proxy)); // { value: '就不告诉你我爹是谁' }
console.log(Object.getPrototypeOf(child)); // { name: 'hdy' }
复制成功
1
2
3
4
5
6
7
8
9
10
11

# setPrototypeOf

  • 作用:拦截Object.setProtypeOf()
  • 定义:setPrototypeOf(target, prototype)
  • 入参:Object, Object | null
  • 返回:Boolean
  • 调用:Object.setPrototypeOf(proxy, proto)
const father = { name: 'hdy' };
const child = { age: 18 };
const proxy = new Proxy(child, {
    setPrototypeOf(target, proto) {
        console.log('设置原型劫持操作');
        return Reflect.setPrototypeOf(target, proto);
    }
})
Object.setPrototypeOf(proxy, father); // 设置原型劫持操作

console.log(father.isPrototypeOf(proxy)); // true
console.log(father.isPrototypeOf(child)); // true
复制成功
1
2
3
4
5
6
7
8
9
10
11
12

# defineProperty

  • 作用:拦截原生的defineProperty方法
  • 定义:defineProperty(target, property, descriptor)
  • 入参:Object, String, Object
  • 返回:Boolean (属性操作是否成功)
  • 调用:Object.defineProperty(proxy, name, descriptor)




 
 












 
 
 

const proxy = new Proxy({}, {
    defineProperty(target, prop, descriptor) {

        // 每次设置做提醒
        console.log(`${target}设置了${prop}`);
        return Reflect.defineProperty(target, prop, descriptor);
    }
})

Object.defineProperty(proxy, 'age', {
    get() {
        return this._age ? this._age + '岁' : '0岁';
    },
    set(value) {
        this._age = value;
    }
}); // [object Object]设置了age

console.log(proxy.age); // 0岁
proxy.age = 100; // [object Object]设置了_age
console.log(proxy.age); // 100岁
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# deleteProperty

  • 作用:劫持delete操作
  • 定义:deleteProperty(target, name)
  • 入参:Object, String
  • 返回:Boolean (是否删除成功)
  • 调用:delete proxy.prop
  • 劫持了但是没有调用原生,所以删除无效

 
 
 





const proxy = new Proxy({ name: 'hdy' }, {
    deleteProperty(target, name) {
        console.log(`不能删除${name}属性!`);
    }
});
console.log(proxy.name); // hdy
console.log(delete proxy.name); // 不能删除name属性! false
console.log(proxy.name); // hdy
复制成功
1
2
3
4
5
6
7
8
  • 劫持调用原生


 









const proxy = new Proxy({ name: 'hdy' }, {
    deleteProperty(target, name) {
        const res = Reflect.deleteProperty(target, name);
        console.log(res ? `删除了${name}属性!` : `不能删除${name}属性!`);
        return res;
    }
});

console.log(proxy.name); // hdy
console.log(delete proxy.name); // 删除了name属性! true
console.log(proxy.name); // undefined
复制成功
1
2
3
4
5
6
7
8
9
10
11

# getOwnPropertyDescriptor

  • 作用:拦截Object.getOwnPropertyDescriptor(target, name)
  • 定义:getOwnPropertyDescriptor(target, name)
  • 入参:Object, String
  • 返回:Object | undefined
  • 调用:Object.getOwnPropertyDescriptor(proxy, name)

限制:configurable相关不能撒谎

  1. 必须返回Object或undefined
  2. 不可配置属性不可以返回可配置
  3. 属性存在且target不可扩展,不可返回不存在
  4. 属性不存在的属性且target不可扩展,不可返回存在
  5. 可配置或不存在的属性,不可以返回不可配置
  • 给所有属性加一个【proxy】标识


 
 
 
 













const proxy = new Proxy({ name: 'hdy' }, {
    getOwnPropertyDescriptor(target, name) {
        const descriptor = Object.getOwnPropertyDescriptor(target, name);
        descriptor.value = `proxy:${descriptor.value}`;
        descriptor.writable = false;
        return descriptor;
    }
})

console.log(Object.getOwnPropertyDescriptor(proxy, 'name'));
/**
{
  value: 'proxy:hdy',
  writable: false,
  enumerable: true,
  configurable: true
}
 */
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# isExtensible

  • 作用:劫持Object.isExtensible(target)
  • 定义:isExtensible(target)
  • 入参:Object
  • 返回:Boolean
  • 调用:Object.isExtensible(target)

  • 只能做操作,不能修改值
const proxy = new Proxy({}, {
    isExtensible(target) {
        console.log('自定义操作!');
        return Reflect.isExtensible(target);
    }
})
console.log(Object.isExtensible(proxy)); // 自定义操作! true
复制成功
1
2
3
4
5
6
7

# preventExtensions

  • 作用:劫持限制对象扩展类操作
  • 定义:preventExtensions(target)
  • 入参:Object
  • 返回:Boolean
  • 调用:
    1. Object.preventExtensions(proxy)
    2. Object.seal(proxy)
    3. Object.freeze(proxy)
const proxy = new Proxy({}, {
    preventExtensions(target) {
        throw Error('此对象不可禁止扩展');
    }
})

// Object.preventExtensions(proxy); // Error: 此对象不可禁止扩展
// Object.seal(proxy); // Error: 此对象不可禁止扩展
// Object.freeze(proxy); // Error: 此对象不可禁止扩展
复制成功
1
2
3
4
5
6
7
8
9
上次更新: 4/8/2025
Powered By Valine
v1.4.16