【JS一问】JavaScript 面向对象的理解和感悟

1,952 阅读8分钟

提问者的话

  1. 什么是面向对象,面向对象有哪些特点,以及这些特点的解释。

  2. JavaScript 如何实现这些特点,比如封装、继承、多态。

    • 如果关于上述三点,你能够解释到有多少种实现方式、优缺点是什么。
    • 以及近几年流行的解决方案是什么。这就是加分
    • 比如对于继承吧。类式继承、构造函数继承、组合继承、原型继承、寄生组合继承等
    • 说出大概的实现思路和优缺点,再介绍下 extends 或者 mixin 的实现
    • 甚至你可以衍生到JavaScript 的模块化发展
    • 甚至到为什么现在 TS 如此流行。
    • 那么可以说到这一环节解答的就非常棒了。
  3. 回答完 JavaScript 的面向对象,是不是可以从此衍生下为什么需要面向对象。

  4. 以及当先对于软件设计的高内聚、低耦合的思考?

  5. 来个对此题一个提纲挈领的总结?

    重学前端:面向对象还是基于对象?


JavaScript中的对象不合群

  1. JavaScript中有对象的概念,但没有类的概念,ES6中的class也只是一个语法糖
  2. JavaScript对象中可以自由添加(删除修改)属性
  3. 关于面向对象基于对象的争论
    • 这两个词都出现在JavaScript标准的各个版本中。
    • 基于对象的定义:“语言和宿主的基础设施由对象来提供,并且 JavaScript程序即是一系列互相通讯的对象集合”
    • 基于对象的定义并未弱化面向对象的意思,反而更加说明了对象对于语言的重要性

什么是面向对象

  1. Object:一切皆对象
  2. 对象并不是计算机领域凭空造出来的概念,它是顺着人类思维模式产生的一种抽象
  3. 人的成长过程中先认识到对象,然后才有过程、值的概念【一个苹果(实例)可以吃——所有苹果(类)可以吃——3(值)个苹果和3个梨的关系】
  4. 所以面对对象编程是更接近人类思维的一种编程范式
  5. 最成功的流派使用的方式来描述对象,如JAVA
  6. JavaScript选择了较冷门的原型因为一些公司政治原因,JavaScript 推出之时受管理层之命被要求模仿 Java,JavaScript 创始人 Brendan Eich 在“原型运行时”的基础上引入了 new、this 等语言特性,使之“看起来更像 Java”
  7. ES6之前,很多人试图将JavaScript变得更像基于类的语言,进而产生了很多所谓的“框架”,比如 PrototypeJS、Dojo事实上,它们成为了某种 JavaScript 的古怪方言,甚至产生了一系列互不相容的社群,显然这样做的收益是远远小于损失的
  8. 从运行角度谈论对象:任何代码执行都必定绕不开运行时的对象模型【运行时类的概念都是被弱化的
  9. JavaScript面向对象编程:
    • 命名空间是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能。 JavaScript中,命名空间只是另一个包含方法、属性、对象的对象
    • 注意:需要认识到重要的一点是:与其他面向对象编程语言不同的是,Javascript中的普通对象和命名空间在语言层面上没有区别。这点可能会让JavaScript初学者感到迷惑。
    • 创造的JavaScript命名空间背后的想法很简单:一个全局对象被创建,所有的变量,方法和功能成为该对象的属性。使用命名空间也最大程度地减少应用程序的名称冲突的可能性。
    • JavaScript有包括在其核心的几个对象,例如,Math,Object,Array和String对象

JavaScript对象的特征

  1. 对象具有唯一标识性:即使是完全相同的两个对象,也并非是同一个对象
let o1 = {a: 1}
let o2 = {a: 1}
console.log(o1 == o2) //false
  1. 对象具有状态:同一对象可能处于不同的状态之下
  2. 对象具有行为:对象的状态可能因为它的行为而产生变迁
a) 状态和行为在不同的语言中使用不同的术语来描述
b) java中称为属性(状态)和方法(行为)
c) javascript中将状态和行为统一抽象为“属性”
d) javascript中函数是一种特殊的对象
let age = 20
let o  = {
    name: 'Lily',
    sayHi(){
        console.log('Hi')
    }
}
  1. 除了上述3个基本特征外,JavaScript的独特之处:高度的动态性,运行时改变对象的状态和行为(属性)的能力
  2. JavaScript的属性比别的语言更复杂,它提供了数据属性和访问属性(getter/setterJavaScript对象的两类属性

封装&多态

  1. 在该案例中,Student类虽然不需要知道Person类的walk()方法是如何实现的,但是仍然可以使用这个方法;Student类不需要明确地定义这个方法,除非我们想改变它。 这就叫做封装,对于所有继承自父类的方法,只需要在子类中定义那些你想改变的即可。

  2. 有了封装来看多态

    • 先定义父类和多个子类
    • 有个工厂,根据不同的类型来调用不同的子类
  3. 看看下面两篇文章,他们算是面向对象编程中的封装吗?

    axios的封装

    模块化的发展 ≈ 封装方式的发展?

  4. vue组件的封装属于哪一类型的封装呢?


继承方案(继承的本质是复制)

原型对象相关知识点复习下

  1. 原型链继:多个实例对引用类型的操作会被篡改

  2. 构造函数继承

  3. 原型链+构造函数

  4. 原型式&寄生式:这个就不具体看了,用Object.create()

  5. 构造函数+寄生式:看下继承章节第一张图片“组合式继承”

  6. 混入方式继承多个对象:Object.assign

  7. ES6classextends

    阮一峰ES6 Class的继承:包括extendsMixin

    extends继承的核心代码如下,其实现和上述的构造函数+寄生式一样

function _inherits(subType, superType) {
  
    // 创建对象,创建父类原型的一个副本
    // 增强对象,弥补因重写原型而失去的默认的constructor 属性
    // 指定对象,将新创建的对象赋值给子类的原型
    subType.prototype = Object.create(superType && superType.prototype, {
        constructor: {
            value: subType,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    
    if (superType) {
        Object.setPrototypeOf 
            ? Object.setPrototypeOf(subType, superType) 
            : subType.__proto__ = superType;
    }
}
  1. 你用到继承了吗?

关于继承有人会问:虽然js的继承原理我知道,js代码我也一直在写,但是js的继承真的有用?写了这么久一直都没有用过,倒是看了下es6的继承感觉和java挺像,应该挺实用的,我知道自己水平不够,我想问一下js的继承一般用在哪呢?

  1. 来看下上面问题的简单答案:
    • 举个简单粗暴的?,页面上可以弹出来的元素有Modal/Tips/Alert/Confirm这么几种,其实这几种元素都可以抽象成是一个具有 show 和 hide 方法的元素,这个时候,我们就可以写个ModalCore 的对象,写好show 和 hide 方法。然后上面的几个元素对象,继承这个就好了。
    • 我觉得,需要不需要,主要是取决于平时的工作需要吧,如果开发一些库的代码我猜八成会需要的,但是如果只是平时写一下页面什么的,很少需要,没有那么的抽象必要
    • 根据二八原则,JavaScript 20% 的语法可以完成 80% 的功能,继承大概属于剩余的 80% 语法吧
    • 本人自己用ExtJs的时候倒是经常用extends;用VUE封装组件和原来ExtJS的extends挺像的;VUE封装是调用一个旧的组件封装成新的组件比较多,用继承的方法较少

...

软件的高内聚、低耦合:面向对象设计的七大原则

...


...

JavaScript设计模式:来自w3cshool

...


TypeScript相关

  1. TypeScript学习
  2. 使用TypeScript两年后
  3. VUETypeScript 支持

结语

相对于「一个程序只是一些函数的集合,或简单的计算机指令列表。」的传统软件设计观念而言,面向对象编程可以看作是使用一系列对象相互协作的软件设计。 在 OOP 中,每个对象能够接收消息,处理数据和发送消息给其他对象。每个对象都可以被看作是一个拥有清晰角色或责任的独立小机器。

面向对象程序设计【允许开发人员在一个独特,应用相关的名字的名称下捆绑所有功能的容器】的目的是在编程中促进更好的灵活性和可维护性,在大型软件工程中广为流行。凭借其对模块化的重视,面向对象的代码开发更简单,更容易理解,相比非模块化编程方法, 它能更直接地分析, 编码和理解复杂的情况和过程。