当前位置: 首页 > news >正文

更好理解的面向对象的Javascript 1 —— 动态类型和多态

前言

曾经对Javascript的面向对象相关知识也有过了解,从各种博客、书籍上也学到了很多。但是最近在看《Javascript设计模式与开发实战》这本书时发现该书对这方面的知识点介绍的很易于理解,因此总结出该文章分享给大家,同时也作为自己日后复习的笔记。

动态类型语言和鸭子类型

编程语言按照数据类型大体可分为静态类型语言和动态类型语言。
静态类型语言就是指在编译时就已经确定了变量的类型,比如C++在声明变量时的int关键字。
动态类型语言的变量类型要在程序运行时被赋值后才能确定。
在Javascript中,我们在对一个变量赋值时,显然不需要考虑它的类型。因此,Javascript是一门经典的动态类型语言。

动态类型语言在实际编码时带来了很大的灵活性,我们可以尝试调用任意对象的任意方法,而不需要考虑它原本是否被设计拥有该方法。
这些都建立在鸭子类型的概念上,通俗的说法是“如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。”
好吧我知道你可能不太理解,我们可以通过一个故事来深入理解:

从前在Javascript王国里,有一个国王,他觉得世界上最美妙的声音就是鸭子的叫声,于是国王召集大臣,要组建一个100只鸭子组成的合唱团。但是大臣们找遍了全国,只找到了999只鸭子,最后大臣发现有一只非常特别的鸡,它的叫声跟鸭子一模一样,于是这只鸡就成为了合唱团的最后一员。

这个故事说明,国王只是想要鸭子的叫声,但它的主人是鸡还是鸭都不重要。我们只关注对象的行为,而不关注对象本身
通过代码你可能能更好的的理解:

let duck = {
  duckSay: function () {
    console.log('ga')
  }
}

let chicken = {
  duckSay: function () {
    console.log('ga')
  }
}

// 合唱团
let choir = []

let joinChoir = function (animal) {
  if (animal && typeof animal.duckSay === 'function') {
    choir.push(animal)
    console.log('加入合唱团成功')
  }
}

joinChoir(duck)  // 加入合唱团成功
joinChoir(chicken)  // 加入合唱团成功

我们发现,只要动物拥有duckSay方法,它就可以加入合唱团,而并不关心它到底是谁。
鸭子类型的概念至关重要,比如一个对象有了length属性,也可以依照下标来存取属性,这个对象就可以被当做数组来使用。(这里可以思考我们在处理函数传入的参数arguments时的一些操作)

多态

多态是指同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。也就是给不同对象发送同一消息,会有不同的反馈。
这里我们依旧通过一个故事来讲解:

主人家里养了两只动物,一只鸡和一只鸭,当主人发出叫的命令时,鸭会gagaga的叫,而鸡会gegege的叫。

上面的故事用代码实现就是:

let makeSound = function (animal) {
  if (animal instanceof Duck) {
    console.log('ga')
  } else if (animal instanceof Chicken) {
    console.log('ge')
  }
}

let Duck = function () {}
let Chicken = function () {}

makeSound(new Duck())  // ga
makeSound(new Chicken())  // ge

多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开,即将“不变的事情”和“可变的事情”分离开。这样的程序是可生长的,也是符合开放-封闭原则。
因此我们可以将不变的部分隔离出来,也就是所有的动物都会叫

let makeSound = function (animal) {
  animal.sound()
}

然后把可变的部分封装起来:

let Duck = function () {}
Duck.prototype.sound = function () {
  console.log('ga')
}

let Chicken = function () {}
Chicken.prototype.sound = function () {
  console.log('ge')
}

makeSound(new Duck())  // ga
makeSound(new Chicken())  // ge

再比如我们要开发一个地图应用,现在有百度和谷歌两家地图提供了API,且均提供了show()方法,代码如下:

let googleMap = {
  show: function () {
    console.log('开始渲染谷歌地图')
  }
}

let baiduMap = {
  show: function () {
    console.log('开始渲染百度地图')
  }
}

let renderMap = function (type) {
  if (type === 'google') {
    googleMap.show()
  } else if (type === 'baidu') {
    baiduMap.show()
  }
}

renderMap('google')  // 开始渲染谷歌地图
renderMap('baidu')  // 开始渲染百度地图

可以看到,虽然renderMap()函数保持了一定的弹性,但是这种弹性很脆弱,如果再换另一个地图,有需要修改renderMap函数,继续添加条件分支语句。
我们可以把程序相同的部分抽象出来,即显示某个地图:

let renderMap = function (map) {
  if (map.show instanceof Function) {
    map.show()
  }
}

结束语

希望能为你带来一些启发。

参考资料:
《JavaScript 设计模式与开发实战》 第一章

相关文章:

  • 一个.net下的轻量级的Serverless 文档数据库LiteDB
  • 微信小程序开发问题汇总
  • 基于我的Winform开发框架扩展而成的WCF开发框架
  • 「面试题」如何实现一个圣杯布局?
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • 预加载机制及变量提升
  • proguaid混淆maven工程问题总结
  • 图片编辑类
  • tcpdump
  • UGUI
  • mysql -- 优化之ICP(index condition pushdown)
  • 感恩送书第1期:2019年快来了,感谢各位网友,送《Spring 5开发大全》
  • 用工作单元(IUnitOfWork)带给我们的是什么?
  • EF架构~将数据库注释添加导入到模型实体类中
  • PHP生成随机字符串
  • [译] 怎样写一个基础的编译器
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 〔开发系列〕一次关于小程序开发的深度总结
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • Android优雅地处理按钮重复点击
  • C学习-枚举(九)
  • golang 发送GET和POST示例
  • mac修复ab及siege安装
  • Vue 2.3、2.4 知识点小结
  • windows下如何用phpstorm同步测试服务器
  • 不上全站https的网站你们就等着被恶心死吧
  • 飞驰在Mesos的涡轮引擎上
  • 给新手的新浪微博 SDK 集成教程【一】
  • 解析 Webpack中import、require、按需加载的执行过程
  • 聊聊hikari连接池的leakDetectionThreshold
  • HanLP分词命名实体提取详解
  • #LLM入门|Prompt#3.3_存储_Memory
  • $ git push -u origin master 推送到远程库出错
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (03)光刻——半导体电路的绘制
  • (Git) gitignore基础使用
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (力扣)循环队列的实现与详解(C语言)
  • (七)c52学习之旅-中断
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)linux下的时间函数使用
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转载)Linux网络编程入门
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .net wcf memory gates checking failed
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .net下的富文本编辑器FCKeditor的配置方法
  • .NET中使用Protobuffer 实现序列化和反序列化
  • .stream().map与.stream().flatMap的使用
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • [ Linux ] git工具的基本使用(仓库的构建,提交)
  • [AIGC] 如何建立和优化你的工作流?
  • [BeginCTF]真龙之力