Proxy 代理

一、介绍

定义: 用于定义基本操作的自定义行为

本质: 修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(meta programming)

元编程(Metaprogramming,又译超编程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作

一段代码来理解

#!/bin/bash
# metaprogram
echo '#!/bin/bash' >program
for ((I=1; I<=1024; I++)) do
    echo "echo $I" >>program
done
chmod +x program

这段程序每执行一次能帮我们生成一个名为program的文件,文件内容为1024行echo,如果我们手动来写1024行代码,效率显然低效

  • 元编程优点:与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译

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

二、用法

Proxy为 构造函数,用来生成 Proxy实例

var proxy = new Proxy(target, handler)

// 例子
const proxy = new Proxy({}, {
  get(target, proper(Key)) {
    console.log('你的访问被我拦截到了') 
    return 1
  },
  set(target, properKey, properValue) {
    console.log('你修改这个属性被我拦截到了')
  }
})

参数

target表示所要拦截的目标对象(任何类型的对象,包括原生数组,函数,甚至另一个代理))

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

handler解析

关于handler拦截属性,有如下:

  • get(target,propKey,receiver):拦截对象属性的读取
  • set(target,propKey,value,receiver):拦截对象属性的设置
  • has(target,propKey):拦截propKey in proxy的操作,返回一个布尔值
  • deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值
  • ownKeys(target):拦截Object.keys(proxy)for...in等循环,返回一个数组
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc),返回一个布尔值
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作

Reflect

若需要在Proxy内部调用对象的默认行为,建议使用Reflect,其是ES6中操作对象而提供的新 API

基本特点:

  • 只要Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在
  • 修改某些Object方法的返回结果,让其变得更合理(定义不存在属性行为的时候不报错而是返回false
  • Object操作都变成函数行为

下面我们介绍proxy几种用法:

get()

get接受三个参数,依次为目标对象、属性名和 proxy 实例本身,最后一个参数可选

var person = {
  name: "张三"
};

var proxy = new Proxy(person, {
  get: function(target, propKey) {
    return Reflect.get(target,propKey)
  }
});

proxy.name // "张三"

get能够对数组增删改查进行拦截,下面是试下你数组读取负数的索引

function createArray(...elements) {
  let handler = {
    get(target, propKey, receiver) {
      let index = Number(propKey);
      if (index < 0) {
        propKey = String(target.length + index);
      }
      return Reflect.get(target, propKey, receiver);
    }
  };

  let target = [];
  target.push(...elements);
  return new Proxy(target, handler);
}

let arr = createArray('a', 'b', 'c');
arr[-1] // c

注意:如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则会报错

const target = Object.defineProperties({}, {
  foo: {
    value: 123,
    writable: false,
    configurable: false
  },
});

const handler = {
  get(target, propKey) {
    return 'abc';
  }
};

const proxy = new Proxy(target, handler);

proxy.foo
// TypeError: Invariant check failed

set()

set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身

假定Person对象有一个age属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy保证age的属性值符合要求

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // 对于满足条件的 age 属性以及其他属性,直接保存
    obj[prop] = value;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错

如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用

const obj = {};
Object.defineProperty(obj, 'foo', {
  value: 'bar',
  writable: false,
});

const handler = {
  set: function(obj, prop, value, receiver) {
    obj[prop] = 'baz';
  }
};

const proxy = new Proxy(obj, handler);
proxy.foo = 'baz';
proxy.foo // "bar"

注意,严格模式下,set代理如果没有返回true,就会报错

'use strict';
const handler = {
  set: function(obj, prop, value, receiver) {
    obj[prop] = receiver;
    // 无论有没有下面这一行,都会报错
    return false;
  }
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
// TypeError: 'set' on proxy: trap returned falsish for property 'foo'

deleteProperty()

deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除

var handler = {
  deleteProperty (target, key) {
    invariant(key, 'delete');
    Reflect.deleteProperty(target,key)
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error(`无法删除私有属性`);
  }
}

var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: 无法删除私有属性

注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错

取消代理

Proxy.revocable(target, handler);

三、使用场景

Proxy其功能非常类似于设计模式中的代理模式,常用功能如下:

  • 拦截和监视外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理

使用 Proxy 保障数据类型的准确性

let numericDataStore = { count: 0, amount: 1234, total: 14 };
numericDataStore = new Proxy(numericDataStore, {
    set(target, key, value, proxy) {
        if (typeof value !== 'number') {
            throw Error("属性只能是number类型");
        }
        return Reflect.set(target, key, value, proxy);
    }
});

numericDataStore.count = "foo"
// Error: 属性只能是number类型

numericDataStore.count = 333
// 赋值成功

声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey

let api = {
    _apiKey: '123abc456def',
    getUsers: function(){ },
    getUser: function(userId){ },
    setUser: function(userId, config){ }
};
const RESTRICTED = ['_apiKey'];
api = new Proxy(api, {
    get(target, key, proxy) {
        if(RESTRICTED.indexOf(key) > -1) {
            throw Error(`${key} 不可访问.`);
        } return Reflect.get(target, key, proxy);
    },
    set(target, key, value, proxy) {
        if(RESTRICTED.indexOf(key) > -1) {
            throw Error(`${key} 不可修改`);
        } return Reflect.get(target, key, value, proxy);
    }
});

console.log(api._apiKey)
api._apiKey = '987654321'
// 上述都抛出错误

还能通过使用Proxy实现观察者模式

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行

observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数

const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
}

观察者函数都放进Set集合,当修改obj的值,在会set函数中拦截,自动执行Set所有的观察者

参考文献

TODO: 待整理

ES6 Proxy拦截器详解

Proxy的原意是“拦截”,可以理解为对目标对象的访问和操作之前进行一次拦截。提供了这种机制,所以可以对目标对象进行修改和过滤的操作。

    const proxy = new Proxy({}, {
        get(target, proper(Key)) {
            console.log('你的访问被我拦截到了')
            return 1
        },
        set(target, properKey, properValue) {
            console.log('你修改这个属性被我拦截到了')
        }
    })

Proxy 实际上重载了点运算符,即用自己的定义覆盖了语言的原始定义。 语法:

const proxy = new Proxy(target, hanlder)

new Proxy生成一个 proxy的实例, target表示要拦截的目标,可以是对象或函数等。凡是对目标对象的一些操作都会经过拦截器的拦截处理。 hanlder 参数也是一个对象,它表示拦截配置,就如上例所示。Proxy 实例也可以作为其他对象的原型对象。对这个目标对象进行操作,如果它自身没有设置这个属性,就会去它的原型对象上面寻找,从而出发拦截行为。如下例:

  const proxy = new Proxy({}, {
      get(target, key) {
          consoloe.log(`你访问的属性是${key}`)
      }
  })
  const newObject = Object.create(proxy)
  newObject.a  // 你访问的属性是a

提醒 同一个拦截器可以拦截多个操作,只需要在第二个参数(hanlder)配置添加 如果对这个目标对象没有设置拦截行为,则直接落在目标对象上。

Proxy 支持的拦截操作 get(target, propKey, receiver) 拦截对象属性读取 set(target, propKey, value, receiver) 拦截对象的属性设置 has(target, propKey) 拦截propkey in proxy deleteProperty(target, propKey) 拦截delete proxy[propKey] ownKeys(target) getOwnPropertyDescriptor(target, propKey) 返回对象属性的描述对象拦截 defineProperty(target, propKey, propDesc) proventExtensions(target) getPrototypeOf(target) isExtensible(target) setPrototypeOf(target, proto) apply(target, object, args) construct(target, args) 拦截 proxy 实例作为构造函数调用的操作

Proxy 实例的方法 get(target, key): 当访问目标对象属性的时候,会被拦截。 target: 目标对象 key: 访问的key值

   const proxy = new Proxy({a:1,b:2}, {
       get(target, key) {
           console.log('called')
           return target[key]
       }
   })
   proxy.a // 1
   // called 会被打印出来

上面的代码中,当读取代理对象属性的时候,会被get方法拦截。所以可以在拦截前做一些事情,比如必须访问这个对象存在的属性,如果访问对象不存在的属性就抛出错误! 如下例:

    const obj = {
        name: 'qiqingfu',
        age: 21
    }
    const proxy = new Proxy(obj, {
        get(target, key) {
            if (key in target) {
                return target[key]
            } else {
                throw Error(`${key}属性不存在`)
            }
        }
    })

以上代码读取代理对象的属性,如果存在就正常读取,负责提示错误访问的key值不存在。 如果一个属性不可配置(configurable), 或者不可写(writeble),则该属性不能被代理

    const obj = Object.defineProperties({}, {
        foo: {
            value: 'a',
            writeble: false,  // 不可写
            configurable: false, //不可配置
        }
    })
    const proxy = new Proxy(obj, {
        get(target, key) {
            return 'qiqingfu'
        }
    })
    proxy.value // 报错

场景例子: 通过get()方法可以实现一个函数的链式操作

    const pipe = (function(){
        return function (value) {
            const funcStack = []; // 存放函数的数组
            const proxy = new Proxy({}, {
                get(target, fnName) {
                    if (fnName === 'get') {
                        return funcStack.reduce((val, nextfn) => {
                            return fn(val)
                        }, value)
                    }
                    funcStack.push(window[fnName])
                    return proxy  //返回一个proxy对象,以便链式操作
                }
            })
            return proxy
        }
    }())

    var add = x => x * 2;
    var math = y => y + 10;
    pipe(3).add.math.get // 16

set(target, key, value)方法用于拦截某个属性的赋值操作 target: 目标对象 key: 要设置的key值 value: 设置的value值 返回值: Boolean 假如有一个prosen对象,要设置的值不能小于100,那么就可以使用 set方法拦截。

    const prosen = {
        a: 101,
        b: 46,
        c: 200
    }
    const proxy = new Proxy(prosen, {
        set(target, key, value) {
            if (value < 100) {
                throw Error(`${value}值不能小于100`)
            }
            target[key] = value
        }
    })

上面代码对prosen对象赋值,我们可以拦截判断它赋值如果小于100就给它提示错误。 使用场景 可以实现数据绑定,即数据发生变化时,我们可以拦截到,实时的更新DOM元素。 还可以设置对象的内部数据不可被修改,表示这些属性不能被外部访问和修改,这是可以使用get和set, 如下例 规定对象的内部属性以_开头的属性不能进行读写操作。

    const obj = {
        name: 'qiqingfu',
        age: 21,
        _money: -100000,
        _father: 'xxx'
    }
    function isSeal(key) {
        if (key.charAl(0) === '_') {
            return true
        }
        return false
    }
    const proxy = new Proxy(obj, {
        get(target, key) {
            if (isSeal(key)) {
                throw Error(`${key},为内部属性,不可以读取`)
            }
            return target[key]
        },
        set(target, key, value) {
            if (isSeal(key)) {
                throw Error(`${key},为内部属性,不可以修改`)
            }
            target[key] = value
            return true
        }
    })

以上代码obj对象设置了内部属性,以_开头的不支持读写。那么可以使用Proxy对其进行拦截判断。get和set中的key属性如果是以_开头的属性就提示错误。 set方法修改完值后,返回的是一个布尔值。 true成功,反则false为修改失败

apply(target, context, args) 方法可以拦截函数的调用,call()、apply() target: 目标对象, context: 目标对象的上下文对象 args: 函数调用时的参数数组

const proxy = new Proxy(function(){}, {
    apply(target, context, args) {
            console.log(target, 'target')
            console.log(context, 'context')
            console.log(args, 'args')
            }
        })
    const obj = {
        a: 1
    }
    proxy.call(obj,1,2,3)

上面的代码是拦截一个函数的执行,分别打印: target -> function(){}: 目标对象 context -> {a: 1}: 目标对象的上下文对象,也就是函数的调用者,这里我们使用call,让obj对象来调用这个函数。 args -> [1,2,3]: 目标对象函数调用时我们传递的参数,这里会以数组的形式接受。 例子: 再说下面一个例子之前,先了解一下Reflect.apply(), 下面是 MDN 的解释 Reflect.apply() 通过指定的参数列表发起对目标(target)函数的调用。 语法: Reflect.apply(target, context, args) target: 目标函数 context: 目标函数执行的上下文 args: 函数调用时传入的实参列表,该列表应该是一个类数组的对象 该方法和ES5的 function.prototype.apply() 方法类似。 下面对 sum 函数的调用进行拦截,并且将函数的执行结果 *2

    const sum = (num1, num2) => {
        return num1 + num2
    }
    const proxy = new Proxy(sum, {
        apply(target, context, args) {
            // 我们可以通过 Reflect.apply()来调用目标函数
return Reflect.apply(...arguments)* 2
        }
    })

    proxy(3,4)  // 14

以上代码是对 sum函数进行代理,并且将其执行结果 * 2

has(target, key ) 方法即拦截 hasProperty操作, 判断对象是否具有某个属性时,这个方法会生效 target: 目标对象, key: 对象的属性 返回值是一个布尔值 如果原对象不可配置或者禁止扩展, 那么has拦截会报错。 for in循环虽然也有 in 操作符,但是has对 for in 循环不生效. has在什么情况下会进行拦截: 属性查询: 例如 foo in window 继承属性查询: foo in Object.create(proxy) with检查: with(proxy) {} Reflect.has() 例1: 使用 has方法隐藏属性,使其不被 in 操作符发现。 就比如说对象以_开头的属性不能被发现。

    const prosen = {
        name: 'qiqingfu',
        _age: 21
    }
    const proxy = new Proxy(prosen, {
        has(target, key) {
            if (key.chatAt(0) === '_') {
                return false
            }
            return key in target
        }
    })

例2: with检查 with的定义总结 在with语句块中,只是改变了对变量的遍历顺序,由原本的从执行环境开始变为从with语句的对象开始。当尝试在with语句块中修改变量时,会搜索with语句的对象是否有该变量,有就改变对象的值,没有就创建,但是创建的变量依然属于with语句块所在的执行环境,并不属于with对象。 离开with语句块后,遍历顺序就会再次变成从执行环境开始。 with语句接收的对象会添加到作用域链的前端并在代码执行完之后移除。 关于js with语句的一些理解

    let a = 'global a'
    const obj = {
        a: 1,
        b: 2
    }
    const fn = key => {
        console.log(key)
    }
    const proxy = new Proxy(obj, {
        has(target, key) {
            console.log(target, 'target')
            console.log(key, 'key')
        }
    })
    with(proxy) {
        fn('a')
    }
    //依此打印
    // {a: 1, b: 2} target
    // fn  key
    // a

以上代码是对obj对象进行代理, 通过with检查, 访问代理对象的 a 属性会被 has方法拦截。那么拦截的第一个target就是目标对象, 而第二个参数key是访问 a时的with语句块所在的执行环境

construct(target, args) 方法用于拦截 new 命令。 target: 目标函数, args: 构造函数的参数对象 返回值必须是一个 对象, 否则会报错。

    const proxy = new Proxy(function() {}, {
        construct(target, args) {
            console.log(target, 'target')
            console.log(args, 'args')
            return new target(args)
        }
    })
    new proxy(1,2)

    // function() {}  'target'
    // [1,2]  'args'

如果返回值不是对象会报错

deleteProperty(target, key) 拦截对象的 delete操作 target: 目标对象 key: 删除的哪个key值 返回值: 布尔值, true成功,false失败 目标对象不可配置(configurable)属性不能被deleteProperty删除, 否则会报错

const obj = Object.defineProperties({}, {
    a: {
        value: 1,
        configurable: false,
    },
    b: {
        value: 2,
        configurable: true
    }
})
const proxy = new Proxy(obj, {
    deleteProperty(target, key) {
        delete target[key]
        return true;
    }
    })

delete proxy.a  // 报错
delete proxy.b // true

以上代码拦截 obj对象, 当进行删除不可配置的属性a时,会报错。删除b属性时则成功。 应用场景: 我们可以指定内置属性不可被删除。如以_开头的属性不能被删除

const obj = {
    _a: 'a',
    _b: 'b',
    c:  'c'
}
const proxy = new Proxy(obj, {
    deleteProperty(target, key) {
        if (key.charAt(0) === '_') {
            throw Error(`${key}属性不可被删除`)
            return false
        }
        delete target[key]
        return true
    }
})

defindeProperty(target, key, descriptor)方法拦截Object.defindProperty()操作 target: 目标对象, key: 目标对象的属性 descriptor: 要设置的描述对象 返回值: 布尔值, true添加属性成功, false则会报错v

const proxy = new Proxy({}, {
    defineProperty(target, key, descriptor) {
        console.log(target, 'target')
        console.log(key, 'key')
        console.log(descriptor, 'descriptor')
        return true
    }
})
Object.defineProperty(proxy, 'a', {
    value: 1
})

以上代码是拦截一个对象的Object.defindProperty()添加属性的操作, 如果返回值为true,表示添加成功。返回值false则会报错。 以上代码的执行结果:


getPrototypeOf(target) 方法,用来拦截获取对象原型。 target: 代理对象 可以拦截一下获取原型的操作: Object.prototype. proto Object.prototype.isPrototypeOf() Object.getPrototypeOf() 获取一个对象的原型对象 instance 操作符 Object.prototype.isPrototypeOf() 方法 检测一个对象的原型链上有没有这个对象 语法: Objectactive.isPrototypeOf(object), 检测object对象的原型链上有没有Objectactive这个对象, 如果有返回true, 否则返回false

    const Objectactive = {a: 1}
    const object = Object.create(Objectactive)
    Objectactive.isPrototypeOf(object) // true

以上代码 Objectactive作为 object的原型对象,然后通过 isPrototypeOf 检测object对象的原型链上有没有Objectactive这个对象。 理所当然返回 true 使用 getPrototypeOf()拦截

const Objectactive = {a: 1}
    const object = Object.create(Objectactive)
    const proxy = new Proxy(object, {
        getPrototypeOf(target) {
            console.log(target, 'target')
            return Object.getPrototypeOf(target)
        }
    })
    let bl = Objectactive.isPrototypeOf(proxy)
    console.log(bl)

    // 依此打印结果: 
    /*
        {
            __proto__:
            a: 1,
            __proto__: Object
        } 'target'
        
        true
    */

以上代码对 object对象进行代理,当访问原型对象时,通过getPrototypeOf()方法拦截,target就是代理对象 getPrototypeOf()方法的返回值必须是 null 或者对象,否则报错。

isExtensible(target) 方法拦截 Object.isExtensible()方法 Object.isExtensible() 方法返回一个布尔值,其检查一个对象是否可扩展。 target: 目标对象 isExtensible()方法有一个强限制,它的返回值必须与目标对象的 isExtensible属性保持一致。

    const testObj = {
        name: 'apy'
    }
    const proxy = new Proxy(testObj, {
        isExtensible(target) {
            console.log('拦截对象的isExtensible操作')
            return true; // 这里要返回true, 因为目标对象现在是可扩展的,如果返回 false会报错
        }
    })
    console.log(Object.isExtensible(proxy))

    // 打印:
    
    // 拦截对象的isExtensible操作
    // true

以上代码通过Object.isExtensible()检测一个对象是否可扩展,会被配置选项中的 isExtensible方法拦截。 那么什么情况下可以 return false 呢 Object.preventExtensions(object): 将一个对象设置为不可扩展的

const testObj = {
    name: 'apy'
}
Object.preventExtensions(testObj) // 将 testObj对象设置为不可扩展

const proxy = new Proxy(testObj, {
    isExtensible(target) {
        console.log('拦截对象的isExtensible操作')
        return false // 因为testObj对象不可扩展,返回值要和目标对象的 Object.isExtensible一致。
    }
})

Object.isExtensible(testObj)

以上代码通过 proxy拦截对象的 Object.isExtensible方法, 并且拦截的返回值与Object.isExtensible一致。否则报错

ownKeys(target)方法用于拦截对象自身的属性读取操作 target: 目标对象 返回值: Array<String, Symbol>, 返回值为数组,且数组中只能包含字符串或Symbol类型的 会被 ownKeys 拦截的读取操作 Object.getOwnPropertyNames() Object.getOwnPropertySymbols() Object.keys() 使用Object.keys 方法时,有三类属性会被 ownKeys 过滤掉,并不会返回. 目标对象 target 上压根不存在的属性 属性名为 Symbol 还有就是目标对象上不可遍历的属性

    const obj = {
        a: 1,
        b: 2,
        [Symbol.for('c')]: 3
    }
    Object.defineProperty(obj, 'd', {
        value: 4,
        enumerable: false
    })
    const proxy = new Proxy(obj, {
        ownKeys(target) {
            return ['a', 'b', [Symbol('c')], 'd', 'e']
        }
    })
    Object.keys(obj).forEach(key => {
        console.log(key)
    })
    // a
    // b

以上代码定义了一个 obj对象, 有其属性a, b, [Symbol],d。并且d属性是不可扩展的。那么 ownKeys方法显式返回 不可遍历的属性(d)、Symbol和不存在的属性e都会被过滤掉,那么最终返回a和b 注意: 如果目标对象包含不可配置(configurable)的属性,那么该属性必须被 ownkeys方法返回。 如果目标对象是不可扩展(preventExtensions)的对象,那么 ownkeys返回必须返回这个对象的原有属性,不能包含额外的属性。

setPrototypeOf(target, proto) 方法拦截 Object.setPrototypeOf方法 target: 目标对象 proto: 要设置的原型对象 返回值 布尔值 设置一个对象的原型对象操作,会被 setPrototypeOf拦截。

    const obj = {}
    const proxy = new Proxy(obj, {
        setPrototypeOf(target, proto) {
            console.log('拦截设置原型操作')
            // 内部手动设置原型,并且返回 boolean
            return Object.setPrototype(target, proto)
        }
    })
    Object.setPrototypeOf(proxy, {a: 1})

以上代码拦截Object.setPrototypeOf方法,所以会打印 拦截设置原型操作 使用场景, 禁止修改一个对象的原型,否则报错 如上例子,拦截一个修改对象原型的操作,抛出相应的错误就可以。

    const foo = {}
    const proxy = new Proxy(foo, {
        setPrototypeOf(target, key) {
            throw Error(`${target}不可以修改原型对象`)
        }
    })
    Object.setPrototypeOf(proxy, {a: 1})  // 报错
上次更新:
Contributors: jiangjingmin, jingmin.jiang, kyxiao