Proxy&Reflect

大部分时候我们使用的都是前置代理, 即我们把直接和代理对象进行交互(所有操作都发生在代理对象身上)的方式叫做前置代理. 那什么是后置代理?

借助原型链机制, 我们直接和 obj 进行交互而不是和代理对象进行交互, 只有当 obj 不存在对应方法时才会通过原型链去查找代理对象.

var pobj = new Proxy({}, {
    get(target, key) {
        if (!target[key]) {   //若target[key]不存在;则这里不应该写在target上,应该写在Obj上,这样下次就不用再次进入原型链的代理里面了
            target[key] = function () {
                console.log(key);
            };
        }
        return target[key];
    }
});

var obj = Object.create(pobj);
obj.sayHello();
obj.sayGoodBye();

可以看出来的是, 对于原本存在于目标对象(target)上的属性, 使用代理前置开销更大, 因为明明已经具有对应属性了却还要经过一次代理对象, 而使用代理后置开销更小. 对于那些不存在的属性, 使用后置代理开销更大, 因为不仅要经过原型链查找还要经过一次代理对象, 而使用前置代理只需要经过一次代理对象. 当然也可能引擎有特殊的优化技巧使得这种性能差异并不明显, 所以也看个人喜欢采用哪种方式吧.

Reflect 的方法和 Proxy 的方法是成对出现的, 和以前的一些方法相比, Reflect 的方法对参数的处理不同或返回值不同, 尽管很细微的差别, 但是当和 Proxy 配合使用的时候, 使用以前的方法可能导致 Proxy 对象和普通对象的一些行为不一致, 而使用 Reflect 则不会有这样的问题, 所以建议在 Proxy 中都使用 Reflect 的对应方法.

另一方面是 Reflect 暴露的 API 相对更加底层, 性能会好一些.

最后是有些事情只能通过 Reflect 实现, 具体参考这个例子. 但是个人感觉这个例子并不是很好, 毕竟这个场景太少见了.

var pobj = new Proxy({}, {
    get(target, key) {
        if (!target[key]) {
            target[key] = function () {
                console.log(key);
            };
        }
        return target[key];
    }
});

var obj = Object.create(pobj);
obj.sayHello();
obj.sayGoodBye();
//在这个例子中, 调用 obj 上一开始不存在的方法最终都会通过原型链找到代理对象, 进而找到 target 也即空对象, 然后对空对象实例化对应的方法. //这里的原型链查找总是让人感觉不太爽, 明明进入到 get trap 就肯定说明 obj 一开始不存在对应方法, 那我们理应可以在这时候给 obj 设置对应方法, //这样下次调用的时候就不会进行原型链的查找了, 为什么非要给那个毫无卵用的空对象设置方法, 导致每次对 obj 进行方法调用还是要进行原型链查找?

改成receiver[key]

var pobj = new Proxy({}, {
    get(target, key, receiver) {
        if (!receiver[key]) {
            receiver[key] = function () {
                console.log(key);
            };
        }
        return receiver[key];
    }
});

var obj = Object.create(pobj);
obj.sayHello();
// RangeError: Maximum call stack size exceeded  栈溢出//因为obj.sayHello obj对内没这个属性 => 通过原型链 到proxy的get 里面找  receiver[key] ==obj[‘sayHello‘] =>又循环 一直调用导致stack内存溢出

若用receiver.hasOwnProperty(key)一样会死循环;用reflect.has去判断是否有该属性,reflect更底层不会向之前那个通过obj去找实例或者原型上的方法导致循环调用

var pobj = new Proxy({}, {
    get(target, key, receiver) {
        if (!receiver.hasOwnProperty(key)) {
            receiver[key] = function () {
                console.log(key);
            };
        }
        return receiver[key];
    }
});

var obj = Object.create(pobj);
obj.sayHello();
// RangeError: Maximum call stack size exceeded
//还是堆栈溢出, 因为 hasOwnProperty() 其实是 Object.prototype.hasOwnProperty(), 意味着在原型链的尽头, 而 pobj 在原型链上更近的位置, //于是相当于 receiver/obj 并不存在 hasOwnProperty(), 于是变成了对 obj.hasOwnProperty() 无限查找导致堆栈溢出.
用call解决var pobj = new Proxy({}, {
    get(target, key, receiver) {
        if (!Object.prototype.hasOwnProperty.call(receiver, key)) {
            receiver[key] = function () {
                console.log(key);
            };
        }
        return receiver[key];
    }
});

var obj = Object.create(pobj);
obj.sayHello();
obj.sayHello();
// sayHello
// sayHello

若用reflect解决更好

var pobj = new Proxy({}, {
    get(target, key, receiver) {
        if (Reflect.has(target, key)) {
            return Reflect.get(target, key);
        }
        Reflect.set(receiver, key, function () {
            console.log(key);
        });
        return Reflect.get(receiver, key);
    }
});

var obj = Object.create(pobj);
obj.sayHello();
obj.sayHello();
console.log(obj.hasOwnProperty(‘sayHello‘));

参考:https://juejin.im/post/5b7aa257e51d4538c86cf6bb#heading-2

原文地址:https://www.cnblogs.com/little-ab/p/11110006.html

时间: 2024-10-04 10:57:00

Proxy&Reflect的相关文章

Proxy/Reflect

Proxy:像拦截器,对目标对象修改等进行拦截,是一种元编程(meta programming),即修改JS语言本身. //生成proxy实例,两个参数都是对象,targetObj是要拦截的目标对象,handlerObj参数用来定制拦截行为 var proxy = new Proxy(targetObj, handlerObj) Reflect:将Object对象一些明显的语言内部的方法,放到Reflect对象上. 修改某些Object的方法使返回更合理 1.规范语言 2.是Proxy对象的修改

es6总结(七)--proxy & reflect

利用ES6中的Proxy和Reflect 实现简单的双向数据绑定

利用ES6中的Proxy (代理) 和 Reflect 实现一个简单的双向数据绑定demo. 好像vue3也把 obj.defineProperty()  换成了Proxy+Reflect. 话不多说,直接上代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>proxy</title> </hea

Proxy详解

一.Proxy基础 1. 什么是Proxy? Proxy是一个构造函数,可以通过它生成一个Proxy实例. const proxy = new Proxy(target, handler); // target: 目标对象,待操作对象(可以是普通对象,数组,函数,proxy实例对象) // handler: 一个对象,最多可包含13个操作方法,也可以是空对象 2. Proxy的作用 1. Proxy是一个ES6语法,它的作用主要是通过handler对象中的拦截方法拦截目标对象target的 某些

[Node.js]REPL(交互式解释器)

摘要 REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应. Node交互解释器 node自带的交互解释器可以完成以下任务: 读取:读取用户输入,解析输了js数据结构并存储在内存中. 执行:执行输入的数据结构. 打印:输出结果. 循环:循环操作以上步骤知道用户两次按下ctrl+c按钮退出. windows启动node交互解析器 如上图,在> 后面就可以

理解了这些词句涵义用法等,你就熟练ES6了。

let const 块级作用于 暂时性死区 解构赋值:变量的解构赋值.对象的解构赋值.字符串的解构赋值.数值和布尔值的解构赋值. String的扩展 正则表达式的扩展 Number的扩展 Array的扩展 Object的扩展 函数的扩展对象的扩展 Symbol Set和Map Iterator和for of Proxy Reflect Promise Generator Class async函数 Module Decorator

vue源码分析

双向数据绑定 1. js的eventloop micro 微队列,promise的then,async await,observer, 优先级高于宏队列.window.MutationObserver属于observer macro 宏队列,setTimeout,setInterval, click,event事件,ajax, var MutationObserver = window.MutationObserver; var target = document.querySelector('

深入浅出的webpack构建工具---babel之配置文件.babelrc(三)

阅读目录 一:理解 babel之配置文件.babelrc 基本配置项 二:在webpack中配置babel 回到顶部 一:理解 babel之配置文件.babelrc 基本配置项 1. 什么是babel? 它是干什么用的? ES6是2015年发布的下一代javascript语言标准,它引入了新的语法和API,使我们编写js代码更加得心应手,比如class,let,for...of promise等等这样的,但是可惜的是这些js新特性只被最新版本的浏览器支持,但是低版本浏览器并不支持,那么低版本浏览

深入理解webpack(三) babel之配置文件

一:理解 babel之配置文件.babelrc 基本配置项 1. 什么是babel? 它是干什么用的? ES6是2015年发布的下一代javascript语言标准,它引入了新的语法和API,使我们编写js代码更加得心应手,比如class,let,for...of promise等等这样的,但是可惜的是这些js新特性只被最新版本的浏览器支持,但是低版本浏览器并不支持,那么低版本浏览器下就需要一个转换工具,把es6代码转换成浏览器能识别的代码,babel就是这样的一个工具.可以理解为 babel是j