09-JavaScript面向对象

面向对象基本概念

面向对象思想

  • 面向对象(Object Oriented,OO)是软件开发方法
  • 面向对象是一种对现实世界抽象的理解,是计算机编程技术发展到一定阶段后的产物
  • Object Oriented Programming-OOP ——面向对象编程



面向对象和面向过程区别

  • 面向过程

    • 强调的是功能行为
    • 关注的是解决问题需要哪些步骤
  • 回想下前面我们完成一个需求的步骤:

    • 首先搞清楚我们要做什么
    • 然后分析怎么做
    • 最后我用代码体现
    • 一步一步去实现,而具体的每一步都需要我们去实现和操作
  • 在上面每一个具体步骤中我们都是参与者, 并且需要面对具体的每一个步骤和过程, 这就是面向过程最直接的体现


  • 面向对象是基于面向过程而言

  • 面向对象和面向过程都是一种思想

  • 面向对象

    • 将功能封装进对象,强调具备了功能的对象
    • 关注的是解决问题需要哪些对象
  • 当需求单一, 或者简单时, 我们一步一步去操作没问题, 并且效率也挺高。 可随着需求的更改, 功能的增加, 发现需要面对每一个步骤非常麻烦, 这时就开始思索, 能不能把这些步骤和功能再进行封装, 封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰多了, 用的时候, 找到对应的类就可以了, 这就是面向对象思想


  • 示例
  • 买电脑
    • 面向过程

      • 了解电脑
      • 了解自己的需求
      • 对比参数
      • 去电脑城
      • 砍价,付钱
      • 买回电脑
      • 被坑
    • 面向对象

      • 找班长
      • 描述需求
      • 班长把电脑买回来

  • 吃饭
    • 面向过程

      • 买菜
      • 摘菜
      • 洗菜
      • 切菜
      • 炒菜
      • 盛菜
    • 面向对象

      • 去饭店
      • 点菜

  • 洗衣服
    • 面向过程
      • 脱衣服
      • 放进盆里
      • 放洗衣液
      • 加水
      • 放衣服
      • 搓一搓
      • 清一清
      • 拧一拧
      • 晒起来
    • 面向对象
      • 脱衣服
      • 打开洗衣机
      • 丢进去
      • 一键洗衣烘干
    • 终极面向对象
      • 买电脑/吃饭/洗衣服
      • 找个对象
  • 现实生活中我们是如何应用面相对象思想的
    • 包工头
    • 汽车坏了
    • 面试

面向对象的特点

  • 是一种符合人们思考习惯的思想
  • 可以将复杂的事情简单化
  • 将程序员从执行者转换成了指挥者
  • 完成需求时:
    • 先要去找具有所需的功能的对象来用
    • 如果该对象不存在,那么创建一个具有所需功能的对象
    • 这样简化开发并提高复用

类与对象的关系

  • 面向对象的核心就是对象,那怎么创建对象?

    • 现实生活中可以根据模板创建对象,编程语言也一样,也必须先有一个模板,在这个模板中说清楚将来创建出来的对象有哪些属性行为
  • JavaScript中的类相当于图纸,用来描述一类事物。

  • JavaScript中可以自定义类, 但是也提供了一个默认的类叫做Object


使用默认类创建对象

  • 通过 new Object() 创建对象

  • 通过字面量创建对象

  • 使用工厂函数创建对象
    • 上面的创建方式, 没多创建一个人都需要将代码再写一遍, 冗余代码太多, 所以我们可以创建创建对象的代码封装到一个函数中
    • 专门用于创建对象的函数我们称之为工厂函数



函数中的this关键字

  • 每个函数中都有一个this关键字, 谁调用当前函数, this关键字就是谁


如何设计一个类

  • 生活中描述事物无非就是描述事物的属性行为
    • 如:人有身高,体重等属性,有说话,打架等行为。
事物名称(类名):人(Person)
属性:身高(height)、年龄(age)
行为(功能):跑(run)、打架(fight)
  • JavaScript中用类来描述事物也是如此
    • 属性:对应类中的成员变量。
    • 行为:对应类中的成员方法。
  • 定义类其实在定义类中的成员(成员变量和成员方法)
  • 拥有相同或者类似属性(状态特征)和行为(能干什么事)的对象都可以抽像成为一个类

如何分析一个类

  • 一般名词都是类(名词提炼法)
    • 飞机发射两颗炮弹摧毁了8辆装甲车
飞机
炮弹
装甲车
    • 隔壁老王在公车上牵着一条叼着热狗的草泥马
老王
热狗
草泥马

如何定义一个类

  • 在JavaScript中可以通过构造函数来定义一个类
  • 构造函数也是一个函数, 只不过函数的名称必须大写(帕斯卡命名)
  • 构造函数也是一个函数, 只不过调用时必须通过new来调用

如何通过类创建一个对象

  • 不过就是创建结构体的时候, 根据每个对象的特征赋值不同的属性罢了

与工厂函数的不同之处

  • 不用我们自己手动创建对象
  • 不同我们手动返回创建好的对象
  • 构造函数名称首字母必须大写
  • 构造函数必须通过new调用

构造函数作用

  • 使用构造函数的好处不仅仅在于代码的简洁性,更重要的是我们可以识别对象的具体类型了
  • 每个对象都可以访问一个名称叫做constructor的属性, 属性指向创建该实例的构造函数


构造函数的内存优化问题

  • 每当通过一个构造函数创建一个对象, 就会在内存中开辟一块存储空间, 该存储空间中保存了对象的所有属性和方法
  • 如果构造函数中的某些属性或方法是需要变化的, 那么每份存储空间中都应该保存一份独有, 但是如果某些属性和方法是不变的, 那么每份存储空间中都保存一份则造成了内存浪费

  • 09-JavaScript面向对象_第1张图片
  • 对于这种问题我们可以把需要共享的函数定义到构造函数外部

  • 09-JavaScript面向对象_第2张图片
  • 但是如果有多个需要共享的函数, 就会造成全局命名空间冲突的问题(同一作用域不能出现同名的标识符)

  • 09-JavaScript面向对象_第3张图片

构造函数的内存优化问题(更好的方案)

  • JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。
  • 这个对象的所有属性和方法,都会被构造函数的所拥有
  • 也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上

  • 构造函数、实例、原型三者之间的关系
  • 09-JavaScript面向对象_第4张图片
  • 任何函数都具有一个 prototype 属性,该属性是一个对象
  • 构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数
  • 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 proto

原型链


  • 09-JavaScript面向对象_第5张图片
  • 方法查找规则
    • 先查找当前对象, 当前对象有就使用当前对象的方法
    • 当前对象没有再逐层在原型链上查找, 最先找到那个就使用哪个
    • 如果找到null都没找到就报错

  • 属性查找规则
    • 先查找当前对象, 当前对象有就使用当前对象的方法
    • 当前对象没有再逐层在原型链上查找, 最先找到那个就使用哪个
    • 如果找到null都没找到就输出undefined

  • 属性的设置规则
    • 不会修改原型链上的属性, 会给当前对象新增一个属性


自定义原型对象

  • 原型对象是构造函数的一个属性, 所以我们可以通过修改属性值的方式来自定义原型对象
  • 需要注意的是, 自定义原型对象不能破坏原有的三角恋关系


原型对象使用建议

  • 私有成员(一般就是非函数成员)放到构造函数中
  • 共享成员(一般就是函数)放到原型对象中
  • 如果重置了 prototype 记得修正 constructor 的指向

面向对象三大特性

  • 封装性
    • 封装性就是隐藏实现细节,仅对外公开接口
    • 09-JavaScript面向对象_第6张图片
    • 类是数据与功能的封装,数据就是成员变量,功能就是方法
  • 为什么要封装?
    • 不封装的缺点:当一个类把自己的成员变量暴露给外部的时候,那么该类就失去对该成员变量的管理权,别人可以任意的修改你的成员变量
    • 封装就是将数据隐藏起来,只能用此类的方法才可以读取或者设置数据,不可被外部任意修改是面向对象设计本质(将变化隔离)。这样降低了数据被误用的可能 (提高安全性灵活性)

  • 封装原则
    • 将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共的方法对其访问

实例属性和实例方法/静态属性和静态方法

  • 通过构造函数创建出来的对象访问的属性和方法,我们称之为实例属性和实例方法
    • 实例属性和实例方法都是绑定在构造函数创建出来的对象上的

  • 通过构造函数直接调用的属性和方法,我们称之为静态属性和静态方法
    • 静态属性和静态方法都是绑定在构造函数上的


  • 继承性
    • 儿子继承父亲的物品就是继承最好的体现
    • js中继承目的: 把子类型中共同的属性和方法提取到父类型中
    • 较少代码的冗余度, 提升代码的复用性


      09-JavaScript面向对象_第7张图片

      09-JavaScript面向对象_第8张图片

  • 借用原型链实现继承
    • 直接将子类的原型对象修改为父类对象, 这样就能使用原型链上的属性和方法


  • 借用构造函数实现继承
    • 在子类中调用父类构造函数, 并且将父类构造函数的this修改为子类对象


  • 借用构造函数+借用原型链组合继承
    • 通过借用构造函数实现属性继承
    • 通过借用原型链实现方法继承

  • 终极方案


对象的增删改查



Object对象的bind-call-apply方法

  • 默认情况下所有对象都有bind-call-apply方法
  • 这三个方法的作用是用于修改指定函数中this的指向

JavaScript中继承远不止如何, 更多惊喜尽在后续ES6中


对象的拷贝

  • 浅拷贝
    • 对于基本类型属性无论是深浅拷贝,都会复制一份
    • 对于引用类型属性浅拷贝拷贝的是引用类型的地址
    • 简而言之, 浅拷贝修改引用类型属性, 拷贝前拷贝后的对象都会受到影响

  • 深拷贝
    • 对于基本类型属性无论是深浅拷贝,都会复制一份
    • 对于引用类型属性深拷贝会将引用类型复制一份
    • 简而言之, 深拷贝修改引用类型属性, 只会影响当前修改对象

你可能感兴趣的:(09-JavaScript面向对象)