什么?JS也和迪丽热巴一样有专业替身?没听过的快来补补课…


highlight: atom-one-dark

本文正在参加「金石计划 . 瓜分6万现金大奖」,欢迎大家来交流。

什么?JavaScript和大名鼎鼎的迪丽热巴一样都有专业替身?没听过的快来补补课…

image.png

写在前面

这是设计模式系列的第二节,学习的是patterns.dev里设计模式中代理模式内容,由于是资料是英文版,所以我的学习笔记就带有翻译的性质,但并不是翻译,记录的是自己的学习过程和理解

第一节:设计模式之JavaScript真正单例模式的写法

工作了多年了,在设计组件时往往关注业务逻辑的实现,而忽略了代码组织模式结构上的优化,一些本可以复用的代码却不知道怎么复用,或者在不熟悉复用逻辑的情况喜爱就直接复制一份,造成了一些代码的维护成本和体量的冗余。最近开始封装一些业务组件,想把设计模式使用进去,所以正好学习一番,感兴趣的朋友可以一起来探讨。

后续可能会专门针对每种设计模式设计一个比较适合的组件,敬请期待或催更,点个关注不迷路!

代理模式

基本释义

通过替身,拦截并控制目标对象和其他对象的互动

没错,咱们JavaScript里专业替身代理模式,就是通过Proxy关键字实现的。

const person = {
  name: "John Doe",
  age: 42,
  nationality: "American"
};

const personProxy = new Proxy(person, {});

交互实现:通过第二个控制器参数handler实现与被代理对象进行交互


const personProxy2 = new Proxy(person, {
  get: (obj, prop) => {
    console.log(`The value of ${prop} is ${obj[prop]}`);
  },
  set: (obj, prop, value) => {
    console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
    obj[prop] = value;
  }
});

通过new一个Proxy对象,这个对象构造函数接收两个参数,第一个就是被代理的目标对象,第二个就是控制器handler,通常是获取或修改目标对象属性get和set方法。

替身的使用

替身的使用和被代理的对象的使用一样,直接调用就可以了。

personProxy.name;
personProxy.age = 43;
person.age = 45; // 被代理的对象依然可以直接使用

当然new Proxy构造函数的第二个控制器参数是必传的,如果是和替身的属性一一对应,没有其他操作,可以传个空对象{},否则的话可以重写get或set方法,处理特殊逻辑。

其中get方法有两个参数,第一个参数是被代理的Object本身,第二个参数是当前被使用的属性的名称,return返回值会直接生效,不return会默认拿取被代理对象的该属性值

set方法有三个参数,第一个参数是被代理的Object本身,第二个参数是当前被修改的属性名称,第三个参数是将要设置的值;重写set方法必须要重写修改逻辑obj[prop] = value,不重新修改逻辑,set的属性值无效,而且set必须return true,否则会报错;

最佳实践

代理模式通常用来校验属性值,比如类型、是否为空,是否非法等等,从而最大限度的保证数据的有效性;

const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    if (!obj[prop]) {
      console.log(
        `Hmm.. this property doesn't seem to exist on the target object`
      );
    } else {
      console.log(`The value of ${prop} is ${obj[prop]}`);
    }
  },
  set: (obj, prop, value) => {
    if (prop === "age" && typeof value !== "number") {
      console.log(`Sorry, you can only pass numeric values for age.`);
    } else if (prop === "name" && value.length < 2) {
      console.log(`You need to provide a valid name.`);
    } else {
      console.log(`Changed ${prop} from ${obj[prop]} to ${value}.`);
      obj[prop] = value;
    }
  }
});

Reflect映射

Reflect对象和Proxy构造函数的第二个参数handler控制器一样,通过getset获取或者修改目标对象的属性;所以personProxy就可以改造为下面这种写法:

const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    console.log(`The value of ${prop} is ${Reflect.get(obj, prop)}`);
  },
  set: (obj, prop, value) => {
    console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
    Reflect.set(obj, prop, value);
  }
});

handler的get方法中,通过Reflect.get方法获取目标对象obj的prop属性的值;通过Reflect.set方法设置目标对象obj的prop属性的值为value。

总结

代理模式对于控制目标对象的行为是非常有用的。代理有很多使用场景,比如说验证格式化警告以及调试

但是过度使用代理对象,或者是在handler控制器中做很耗性能的操作,就会很容易影响到你整个应用的性能表现。所以最好不要在高性能要求的代码中使用代理模式。

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容