JS的代理模式---小彭对小美的追求日记

291 阅读13分钟

🚩前言

大家学习编程的时候,有没有试过这种方式?将现实世界的故事映射到“代码世界”,通过代码,来实现故事的情节,而此时我们就成为了这个故事的“导演”和“作者”,下面本文就将以这种方式,通过小彭同学对小美同学的追求日记,来介绍一下JS的几个重要特性,包括但不限于JS的基础语法,JS的面向对象编程,JS的设计模式...内容丰富,让我们开始吧!

🗺️背景介绍

随着春天的到来,小彭最近对隔壁班的小美产生了很多好感,准备开启一段追小美的故事。
而小彭是一个热爱编程boy,他准备以 JS 的方式来表达自己这一段刻骨铭心的爱情故事......


小彭的自我介绍🙋‍♂️

首先,让咱们来介绍一下咱们的男主角小彭吧,小彭是计算机系的一个阳光大男孩,他的年龄19岁,身高为175,家乡在上海,目前还是单身

所以咱们在代码世界中: 小彭为他自己创建了一个对象:

const xPeng = {
  name: '小彭',
  age: 19, 
  tall: 175,
  hometown: 'ShangHai',
  isSingle: true, 
  
  eat:function(){
  console.log("吃饭")
  },
  
  sleep:function(){
  console.log("睡觉")
  }
  
}

小彭の注释💻:

1.这里我们用代码世界中的“对象” 来表示现实世界的小彭,一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。

2.对象是一个容器,封装了属性(property)和方法(method) :属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把小彭抽象为xPeng对象,使用“属性”记录具体的身高,年龄,家乡...,使用“方法”表示小彭的具体行为(吃饭、睡觉...)。

小美的自我介绍🙋‍♀️

接着,继续让咱们隆重介绍一下咱们的女主角小美,肤白貌美大长腿,是计算机学院中,无人不知的大美女,年龄18岁,身高165,家乡在北京。

同样地,小彭也为她的女神创建了一个对象:

const xMei = {
  name: '小美',
  age: 18,
  tall:165,
  hometown: 'BeiJing'
  
  sing:function(){
  console.log("唱歌")
  }
  
  dance:funciton(){
  console.log("跳舞")
  }
}

小彭の注释💻:

1.在上面两段创建小彭和小美的代码中,首先 {}里面包括里面的内容,咱们称为字面量,为啥叫字面量呢?因为咱们看字面意思就能理解到其代表啥,比如这里代表这个对象的名字是小美,年龄18岁,身高165。行为有唱歌和跳舞

2.我们知道{...}的内容代表了小彭或者小美的信息,但是实际上在代码世界里是什么呢?其实在代码世界里,是咱们内存中开辟了一个空间,里面存放了一个对象

3.而咱们分别以xPeng和xMei作为变量名进行声明,实际上声明的是地址,可以指向内存里的内容,咱们把 “=”右边的内容:也就是我们2里面创建的空间的地址赋值给左边的变量名

4.很多人会发现,咱们对xPeng和xMei声明的时候,并没有像其它语言一样指定类型,但是咱们JS是怎么知道具体的类型的呢?实际上在赋值的时候就能自动识别类型了。实际上这就是JS的弱类型特性,如果后续我们需要查看其类型的话,可以使用typeof

typeof xPeng; //Object
typeof xMei; //Object
typeof xPeng.Name //String
typeof xPeng.age //Number
typeof xPeng.isSingle //Boolean

数据类型⌨️

注意:在JS中,数据类型分为简单数据类型和复杂数据类型,其中,Number,String,Boolean,Null,Undefined是简单数据类型,其余的都是复杂数据类型;

分类简单数据类型(原始类型)复杂数据类型(引用类型)
存储位置栈内存(直接存储值)堆内存(存储实际内容),栈内存存储引用地址
访问方式按值访问按引用访问(通过地址指针)
可变性不可变(修改会创建新值)可变(可直接修改属性或元素)
赋值行为拷贝实际值(独立副本)拷贝地址(共享同一对象)
函数参数传递按值传递(不影响原变量)按地址值传递(影响原对象内容)
示例var a = 10; var b = a;var obj1 = {}; var obj2 = obj1;
比较方式=== 比较值是否相同=== 比较地址是否相同

追求日记第一章:这九十九朵玫瑰就是我对你的爱:

小彭首先准备主动向小美表达爱意,小彭询问了身边的好哥们,希望他们能出出主意,室友说:“追女孩子很简单,送花就行了,当初我就是这么追到我女朋友的”,于是小彭准备好99朵玫瑰花,直接对小美出击。

于是小彭为自己创建了一个送花的方法

xPeng.sendFlower =  function(girl) {
    console.log(xPeng.name + '给' + girl.name + '送了99朵玫瑰')
    girl.receiveFlower(xPeng)
  }

小彭の注释💻:

可能有些学习过其它语言的朋友们会懵,在对象中不是要先声明吗?这里为什么可以直接赋值?实际上JS的对象是一个key/value, 每个键(属性)对应每个值,所以咱们这里实际上是给map新增一对键值对,键是函数名,值是函数体。其实这是JS这门语言的动态性和灵活性。

而且咱们还能在浏览器引用JS时,直接在控制台里面操作,给大家秀一波:

image.png


JS的三大运行方式⌨️:

这里作为题外话,聊聊JS的运行方式吧

  1. ✔ 运用命令行 使用Node.js将js运行在后端:Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,可以让 JS 脱离浏览器运行在服务器端。
  2. ✔ 浏览器的方式本地运行js:浏览器是 JavaScript 最传统的运行环境,可以直接在 HTML 中嵌入 JS 代码或引入外部 JS 文件。
  3. ✔ 当远程访问网站时,通过http协议的方式来运行:当用户访问网站时,浏览器会从服务器请求 HTML、CSS 和 JS 文件,然后执行 JS 代码。

追求日记第二章:女神看到玫瑰后,转手就让我滚了

没谈过恋爱的小彭不懂得时机,恰好在小美心情最低落的时候大庭广众之下给小美送去了玫瑰,结果小美看到花之后,当场对小彭说:“你知不知道滚字怎么写?”,没错,小彭在众目睽睽下成了小丑,送的花不仅没被收,还被自己喜欢的人当众拒绝。

沮丧的小彭,哭着脸在代码日记里写下:

xm.emotion = -999;
xm.receiveFlower = function(sender) {
    console.log('看到了' + sender.name + '送的99朵玫瑰')
    console.log('快!点!给!我!滚!')
  }

小彭の注释💻:

在这里同样的,咱们也可以给小美通过同样的方式新增一个方法。


追求日记第三章:既然失败,那就从头再来

小彭在被拒绝后,沮丧了很长一段时间,但是他发现自己心里的爱的火焰还是没有被燃尽,准备再一次发动攻势,但是上次被舍友坑了以后,小彭去找了另一个人,短短大学四年就拥有九段爱情经历的学长,学长听说了小彭的事后,给了小彭几个建议:”小彭啊,首先,咱可不能当着很多人的面表白,万一又被拒绝了,那就更惨了,听哥的,咱得取巧,请小美的闺蜜小红吃个饭,然后先把花交给闺蜜小红,让小红先去调节一下小美的心情,之后再让小红把玫瑰和你的爱意传递给小美。“小彭听后于是照做了。

首先小彭创建了一个"小红"的对象,小红和小美都住在同一个宿舍408,同时她们俩是很好的闺蜜,而且小红的心情比较稳定。

//补充小美的宿舍信息
xm.room = 408 

//小红的信息
const xh = {
  name: '小红',
  room: '408',
  hometown: '上海', //  老乡
  emotion: 50,
}

 xHong.receiveFlower = function(sender) {
    //待实现的内容
  }

小彭の注释💻:

面向接口编程:当小美和小红都有同样名字的方法receiverFlower时,其实是这两个对象实现了同一个方法,虽然这个方法名字相同,但是里面的内容是不一样的,只不过这里的小红对receiverFlower的实现还没开始写,后文会补充。

咱们把上文两个对象共同的方法名称为接口,并且两个对象对这个共同方法名的内容不同,我们称为实现接口,接口是面向对象中的一个重要特性,它定义了代码中的对象应该实现哪些方法!不论在什么语言中,接口都是十分常用,十分重要的!

只不过我们这里使用的是JS的ES5的语法,没有显式的声明接口,实现接口,但是使用这种方法名一致性同样可以达到相同的效果,当然,提一嘴,在后续的ES6时,我们就能使用抽象类和抽象方法来声明和实现接口了。


追求日记第四章:不成功,便成仁!

在小彭请小红吃过一顿饭之后,小彭对小红讲了他追求的策略:小红先接收小彭的信,收到后会给小彭一个回复,然后把信交给小美,小红先去调节小美的心情,待小美的心情变好了之后,再进行赠送。这样子一定会成功哒!

小彭把这次行动的计划继续写至代码日记中:

//小红称为代理
 xHong.receiveFlower = function(sender) {
    console.log('收到了小彭送来的玫瑰,准备调节小美的心情');
    
    setTimeout(() => {
      xMei.emotion = 100;
      console.log('心情调节完毕,准备送小美玫瑰');
      xMei.receiveFlower(sender)
    },3000)
  }

xMei.receiveFlower = function(sender) {
    console.log('看到了' + sender.name + '送的99朵玫瑰')
    if(xMei.emotion > 50) {
      console.log('开心的给' + sender.name + '送了一朵玫瑰')
      return;
    }
    else {
      console.log('快!点!给!我!滚!')
    }
    
  }

小彭の注释💻:

注意,接下来开始介绍代理模式,代理模式是设计模式的一种,设计模式是前人总结的一些软件开发的代码经验,被后人反复使用,使用设计模式的目的是可重用代码,提高代码的可扩展性和可维护性。

代理模式(Proxy)的核心思想是:通过一个代理对象来控制对原始对象的访问。代理对象和原始对象通常具有相同的接口,客户端通过代理间接访问原始对象。

在这个案例中,小红就是小美的代理,他们实现了相同的接口receiveFlower,而小彭则是客户端,他想要访问小美,则可以通过小红代理进行间接访问。

只不过我们的ES5中,没有原生Proxy的支持,所以我们是用上文的方式来实现的。


代理模式ES6实现⌨️

下面也可以给大家引入ES6的代理模式的代理模式的实现,之后咱们如果想要使用,可以用下面的两个模板

1.使用ES6的Proxy对象实现

// 目标对象
const realSubject = {
    request() {
      console.log('RealSubject: 处理请求');
      return '真实响应';
    }
  };

  // 创建代理
const proxy = new Proxy(realSubject, {
    get(target, prop) {
      console.log(`Proxy: 拦截对属性 "${prop}" 的访问`);

        // 可以添加额外的控制逻辑
    if (prop === 'request') {
        return function() {
          console.log('Proxy: 在调用真实对象前执行操作');
          const result = target[prop].apply(target, arguments);
          console.log('Proxy: 在调用真实对象后执行操作');
          return result;
        };
    }
    return target[prop]; // 其他属性直接返回
    }
  });

  console.log(proxy.request()); 

2.使用类继承方式实现

// 真实主题
class RealSubject {
  request() {
    console.log('RealSubject: 处理请求');
    return '真实响应';
  }
}

// 代理类
class ProxySubject {
  constructor(realSubject) {
    this.realSubject = realSubject || new RealSubject();
  }

  request() {
    if (this.checkAccess()) {
      console.log('Proxy: 在调用真实对象前执行操作');
      const result = this.realSubject.request();
      console.log('Proxy: 在调用真实对象后执行操作');
      return result;
    }
    return '访问被拒绝';
  }

  checkAccess() {
    console.log('Proxy: 检查访问权限');
    return true; // 这里可以添加实际的权限检查逻辑
  }
}

// 使用代理
const proxy = new ProxySubject();
console.log(proxy.request());
// 输出:
// Proxy: 检查访问权限
// Proxy: 在调用真实对象前执行操作
// RealSubject: 处理请求
// Proxy: 在调用真实对象后执行操作
// 真实响应

追求日记第五章:有情人终成眷属💏

最后,小彭按照计划照做了,小美被感动了,于是也给小彭送了一朵玫瑰,然后,两个人的感情逐渐升温,最后,他们之间的爱情得到了一个美好的结局。

xPeng.sendFlower =  function(girl) {
    console.log(xPeng.name + '给' + girl.name + '送了99朵玫瑰')
    girl.receiveFlower(xPeng)
  }

 xPeng.sendFlower(xHong)

小彭の注释💻:

函数调用:上文我们定义了小美和小红共同实现的接口receiveFlower,还定义了小彭的送花方法sendFlower,此时,我们进行调用函数sendFlower把xHong作为实参传给girl然后就会执行方法体里的内容:调用接口。小红和小美都实现了这个接口,小彭对象可以通过小红对象控制对小美对象的访问。

调用成功,查看结果: image.png


🗼总结

通过上面这个案例,我相信大家对JS会有一些新的认识,特别是JS的灵活性、动态性、弱类型性。我们也可以认识到JS是一个脚本语言,能够了解到打开运行JS的三种方式:使用node.js命令行后端运行、前端通过本地运行、远程访问通过http协议的方式来运行。而且我们还介绍了JS的面向对象特性,了解到JS的面向接口编程,最后,我们还介绍了一下代理模式---设计模式中重要的一种,有了代理模式,允许你通过代理对象控制对另一个对象的访问,能够实现额外的逻辑而不需要修改原始对象。

🌇结尾

感谢你看到最后,最后再说两点~
①如果你持有不同的看法,欢迎你在文章下方进行留言、评论。
②如果对你有帮助,或者你认可的话,欢迎给个小点赞,支持一下~
我是3Katrina,一个热爱编程的大三学生

(文章内容仅供学习参考,如有侵权,非常抱歉,请立即联系作者删除。)

作者:3Katrina
链接:juejin.cn/post/750351…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。