JS Proxy 的优势以及使用场景


本文摘自PHP中文网,作者Guanhui,侵删。

开汽车的人往往不了解汽车的构造,但是深入了解汽车的构造,可能会有如虎添翼的效果

前言

随着 vue3.x 的消息越来越多,proxy 的讨论也。相对于 Object.definePropertyproxy 有什么区别,有什么优势,以及可以应用在什么地方。该文章就简单的介绍下

Object.defineProperty

proxy 之前,先回顾下 Object.defineProperty 。大家都知道,vue2.x 以及之前的版本是使用 Object.defineProperty 实现数据的双向绑定的,至于是怎样绑定的呢?下面简单实现一下

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

28

29

30

function observer(obj) {

    if (typeof obj === 'object') {

        for (let key in obj) {

            defineReactive(obj, key, obj[key])

        }

    }

}

function defineReactive(obj, key, value) { //针对value是对象,递归检测

    observer(value) //劫持对象的key

    Object.defineProperty(obj, key, {

        get() {

            console.log('获取:' + key) return value

        },

        set(val) { //针对所设置的val是对象

            observer(val) console.log(key + "-数据改变了") value = val

        }

    })

}

let obj = {

    name: '守候',

    flag: {

        book: {

            name: 'js',

            page: 325

        },

        interest: ['火锅', '旅游'],

    }

}

 

observer(obj)

在浏览器的 console 执行一下,似乎能正常运行

但是实际上,Object.defineProperty 问题有以下几个

问题1.删除或者增加对象属性无法监听到

比如增加一个属性 gender ,由于在执行 observer(obj) 的时候,没有这个属性,所以这个无法监听到。删除的属性也是无法监听到

增加属性的时候, vue 需要使用 $set 进行操作,$set 的内部也是使用 Object.defineProperty 进行操作

问题2.数组的变化无法监听到

由上图得知,虽然数组属性实际上是修改成功了,但是不能被监听到

问题3. 由于是使用递归遍历对象,使用 Object.defineProperty 劫持对象的属性,如果遍历的对象层级比较深,花的时间比较久,甚至有性能的问题

proxy

对于 proxy ,在 mdn 上的描述是: 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)

简单来说就是,可以在对目标对象设置一层拦截。无论对目标对象进行什么操作,都要经过这层拦截

听上去似乎,proxyObject.defineProperty 要好用,并且简单很多,实际上就是如此。下面用 proxy 对上面的代码进行改写试下

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

function observerProxy(obj) {

    let handler = {

        get(target, key, receiver) {

            console.log('获取:' + key) // 如果是对象,就递归添加 proxy 拦截

            if (typeof target[key] === 'object' && target[key] !== null) {

                return new Proxy(target[key], handler)

            }

            return Reflect.get(target, key, receiver)

        },

        set(target, key, value, receiver) {

            console.log(key + "-数据改变了") return Reflect.set(target, key, value, receiver)

        }

    }

    return new Proxy(obj, handler)

}

let obj = {

    name: '守候',

    flag: {

        book: {

            name: 'js',

            page: 325

        },

        interest: ['火锅', '旅游'],

    }

}

let objTest = observerProxy(obj)

也是一样的效果

而且,能做到 Object.defineProperty 做不到的事情,比如增加一个属性 gender,能够监听到

操作数组,也能监听到

最后敲一下黑板,简单总结一下两者的区别

1.Object.defineProperty 拦截的是对象的属性,会改变原对象。proxy 是拦截整个对象,通过 new 生成一个新对象,不会改变原对象。

2.proxy 的拦截方式,除了上面的 get 和 set ,还有 11 种。选择的方式很多 Proxy,也可以监听一些 Object.defineProperty 监听不到的操作,比如监听数组,监听对象属性的新增,删除等。

proxy 使用场景

关于 proxy 的使用场景,受限于篇幅,这里就简单列举几个,更多的可以移步我的 github 笔记或者 mdn。

看到这里,两者的区别,和 proxy 的优势已经知道个大概了。但是在开发上,有哪些场景可以使用到 proxy 呢,下面列举个可能会遇上的情况

负索引数组

在使用 splice(-1)slice(-1) 等 API 的时候,当输入负数的时候,会定位到数组的最后一项,但是在普通数组上,并不能使用负数。 [1,2,3][-1] 这个代码并不能输出 3 。要让上面的代码输出 3 , 也可以使用 proxy 实现。

1

<br>

表单校验

在对表单的值进行改动的时候,可以在 set 里面进行拦截,判断值是否合法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

let ecValidate = {

    set(target, key, value, receiver) {

        if (key === 'age') { //如果值小于0,或者不是正整数

            if (value < 0 || !Number.isInteger(value)) {

                throw new TypeError('请输入正确的年龄');

            }

        }

        return Reflect.set(target, key, value, receiver)

    }

}

let obj = new Proxy({

    age: 18

},

ecValidate) obj.age = 16obj.age = '少年'

增加附加属性

比如有一个需求,保证用户输入正确身份证号码之后,把出生年月,籍贯,性别都添加进用户信息里面

众所周知,身份证号码第一和第二位代表所在省(自治区,直辖市,特别行政区),第三和第四位代表所在市(地级市、自治州、盟及国家直辖市所属市辖区和县的汇总码)。第七至第十四位是出生年月日。低17位代表性别,男单女双。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

const PROVINCE_NUMBER = {

    44 : '广东省',

    46 : '海南省'

}

const CITY_NUMBER = {

    4401 : '广州市',

    4601 : '海口市'

}

let ecCardNumber = {

    set(target, key, value, receiver) {

        if (key === 'cardNumber') {

            Reflect.set(target, 'hometown', PROVINCE_NUMBER[value.substr(0, 2)] + CITY_NUMBER[value.substr(0, 4)], receiver) Reflect.set(target, 'date', value.substr(6, 8), receiver) Reflect.set(target, 'gender', value.substr( - 2, 1) % 2 === 1 ? '男': '女', receiver)

        }

        return Reflect.set(target, key, value, receiver)

    }

}

let obj = new Proxy({

    cardNumber: ''

},

ecCardNumber)

数据格式化

比如有一个需求,需要传时间戳给到后端,但是前端拿到的是一个时间字符串,这个也可以用 proxy 进行拦截,当得到时间字符串之后,可以自动加上时间戳。

1

2

3

4

5

6

7

let ecArrayProxy = {

    get(target, key, receiver) {

        let _index = key < 0 ? target.length + Number(key) : key

        return Reflect.get(target, _index, receiver)

    }

}

let arr = new Proxy([1, 2, 3], ecArrayProxy)


推荐教程:《JS教程》

以上就是JS Proxy 的优势以及使用场景的详细内容,更多文章请关注木庄网络博客

相关阅读 >>

详解队列数据结构,js中怎样实现?

可以同时删除字符串前导和尾部空白的函数是什么

javascript有哪些不同版本

聊一聊javascript 中的 url 对象

es6字符串超实用的知识介绍

javascript怎么设置title

javascript如何改变鼠标指针形状

js filter是什么意思

vue前端ui框架有哪些?

javascript怎么判断是否为函数

更多相关阅读请进入《js》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...