小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理

Author: 邵威儒
Wechat: 166661688


Object.defineProperty

Object.defineProperty这个并不是es6的语法,这个是给一个对象,添加属性,但是目前框架很多实用这个方法,来实现数据劫持,也就是数据双向绑定

 
   
  1. // 平时我们这样给一个对象添加属性

  2. let obj = {str:"hello swr"}

  3. obj.str = 'goodbye swr'

  4. console.log(obj.str) // 'goodbye swr'

那么当我们想在给一个对象,读取值或写入值时,进行别的操作,该怎么做呢?

 
   
  1. // 使用Object.defineProperty()

  2. // 接收的第一个参数为对象,第二个参数为属性名,第三个参数为配置对象

  3. let obj = {}

  4. Object.defineProperty(obj,'name',{

  5.    enumerable:true,// 是否可枚举,默认值 true

  6.                    // 如果为false的话,打印这个obj对象,是看不到name这个属性

  7.    writable:true,  // 是否可写,默认值 true

  8.                    // 如果为false的话,给name赋值,不会生效

  9.    configurable:true, // 是否可配置(是否可删除),默认值 true

  10.                       // 如果为true,delete obj.name,再打印obj,则显示{}

  11.                       // 如果为false,delete obj.name,再打印obj,则显示{name:undefined}

  12.   value:'swr', // name对应的值

  13. })

  14. // 上面的写法其实和下面的写法是一样的

  15. let obj = {}

  16. obj.name = 'swr'

那么既然一样,我们有必要写这么大串的代码吗?

其实核心是get和set,我们继续往下看

 
   
  1. // 需要注意的是,当使用get set时,则不能使用value和writable

  2. let obj = {}

  3. let str

  4. Object.defineProperty(obj,'name',{

  5.    enumerable:true,

  6.    configurable:true,

  7.    get(){ // 读,当我们读取时,则会执行到get,比如obj.name

  8.        // return 'swr' // 当我们obj.name进行读取时,会返回'swr'

  9.        return str

  10.    },

  11.    set(newValue){ // 写,当我们写入时,则会执行到set,比如obj.name = 'swr'

  12.                   // 并且会把newValue作为参数传进去

  13.        str = newValue

  14.    }

  15. })

  16. obj.name = 'swr' // 写入

  17. console.log(obj.name) // 'swr'  // 读取

这样一来,我们可以在get set函数中,写出对应的业务逻辑,

包括很多框架底层,例如

 
   
  1. // 一般不再选择这样的写法

  2. Fn.prototype.xxx = xxx

  3. // 更多的是选择这样的写法

  4. // 这样的好处就是当读取值的时候,可以做一系列我们想做的事情

  5. Object.defineProperty(Fn.prototype,'xxx',{...})

那么我们实现数据双向绑定呢?

这个问题在面试当中,会经常问这个问题,但是面试官更希望听到的是具体底层的实现方式,那么接下来我们也实现一下吧~ ( 简陋版的……(#^.^#)

 
   
  1. lang="en">

  2.   charset="UTF-8">

  3.   name="viewport" content="width=device-width, initial-scale=1.0">

  4.   http-equiv="X-UA-Compatible" content="ie=edge">

  5.  </span><span style="color:rgb(27,25,24);line-height:20px;font-size:13px;">对象的数据双向绑定</span><span style="color:rgb(242,44,64);line-height:20px;font-size:13px;">

  6.   id='input' type="" name="" value="">

  7.  

当我们在输入框输入内容时,再到控制台输入obj.name查看这个值时,会发现打印出"hello swr"

小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理_第1张图片

当我们在控制台,给obj.name赋值时,会发现输入框的内容也会作出相应更改

小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理_第2张图片

这样我们就实现了一个简陋版的数据双向绑定了,但是这也是有缺点的,这个只是针对对象进行了数据双向绑定,而尤大大的Vuejs就是基于Object.defineProperty实现的。

除了Object.defineProperty可以实现数据双向绑定之外,还有其他方式吗?

肯定是有其他方式可以实现的,利用es6的proxy代理也可以实现数据双向绑定,但是目前的框架还是比较少使用这种方式。


Proxy

Proxy代理也可以进行数据劫持,但是和Object.defineProperty不同的是,Proxy是在数据外层套了个壳,然后通过这层壳访问内部的数据,目前Proxy支持13种方式。

小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理_第3张图片

Proxy,我的理解是在数据外层套了个壳,然后通过这层壳访问内部的数据,就像下面的图

小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理_第4张图片

 
   
  1. let dog = {

  2.  name:"小黄",

  3.  firends:[{

  4.    name:"小红"

  5.  }]

  6. }

  7. // 1.首先new一个Proxy对象

  8. let proxy = new Proxy(dog,{ // 2.参数一为需要代理的数据,参数二为上图可以代理的13种的配置对象

  9.    get(target,property){ // 3.参数1为上面dog对象,参数2为dog的属性

  10.        console.log('get被监控到了')

  11.        return target[property]

  12.    },

  13.    set(target,property,value){ // 4.参数1为上面dog对象,参数2为dog的属性,参数3为设置的新值

  14.                                // 有点类似Object.defineProperty

  15.        console.log('set被监控到了')

  16.        target[property] = value

  17.    }

  18. })

  19. // 那么接下来我们设置一下这个属性

  20. // dog.name = '小红'  // set值时,发现不会打印 'set被监控到了'

  21. // dog.name // get值时,发现不会打印 'get被监控到了'

  22. // 思考:为什么在set/get值的时候不会打印出来我们需要的东西呢?

  23. // 上面说得很明白了,proxy相当于是一个壳,代理我们需要监控的数据,也就是我们要通过proxy来访问内部数据才会被监控到

  24. proxy.name = '小红' // 打印输出 'set被监控到了'

  25. proxy.name // 打印输出 'get被监控到了'

 
   
  1. // Reflect经常和Proxy搭配使用

  2. // 比如我们上面的例子中

  3. let proxy = new Proxy(dog,{

  4.    get(target,property){

  5.        console.log('get被监控到了')

  6.        return target[property]

  7.    },

  8.    set(target,property,value){

  9.        console.log('set被监控到了')

  10.        // target[property] = value

  11.        // 这里的target[property] = value 可以用下面的写法

  12.        Reflect.set(target,property,value)

  13.    }

  14. })

 
   
  1. // 那么我们该怎样实现深度的数据劫持呢?

  2. let dog = {

  3.  name:"小黄",

  4.  firend:{

  5.    name:"小红"

  6.  }

  7. }

  8. // 我们首先写一个set方法,希望是通过这样来调用

  9. set(dog.firend,funtion(obj){

  10.    console.log(obj) // { name:"小红" }  回调函数中的obj代表的是dog.firend的对象

  11. })

 
   
  1. // 实现

  2. let dog = {

  3.  name:"小黄",

  4.  firend:{

  5.    name:"小红"

  6.  }

  7. }

  8. function set(obj,callback){

  9.    let proxy = new Proxy(obj,{

  10.        set(target,property,value){

  11.            target[property] = value

  12.        }

  13.    })

  14.    // 最后把proxy传给我们的回调函数

  15.    callback(proxy)

  16. }

  17. set(dog.firend,function(obj){

  18.    console.log(obj) // { name:"小红" } 实际就是从set函数中传出来的proxy对象

  19. })


小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理_第5张图片


小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理_第6张图片

公众号

前端达人

长按识别左边二维码关注我


你可能感兴趣的:(小邵教你玩转ES6(二)——Object.defineProperty和Proxy代理)