美文网首页
多态、封装、继承

多态、封装、继承

作者: YHuang | 来源:发表于2016-12-06 23:47 被阅读0次

多态

同一个操作作用于不同的对象,得到不同的结果。
重点分离:做什么 和 谁去做。
鸭子辩型:如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。

// 做什么
function sayName( people ) {
    people.sayName()
}

// 谁去做
const xiaoming = {
    name: '小明',
    sayName() {
        console.log( this.name )
    }
}

const lihong = {
    name: '李红',
    sayName() {
        console.log( this.name )
    }
}

// 同一个操作作用在不同的对象
sayName( xiaoming )  // 小明
sayName( lihong )  // 李红

封装

只让别人知道你想让别人知道的,不让别人知道你不想让别人知道的。
封装的目的是将信息隐藏,数据、类型、实现、变化都能封装。

  • 封装数据

Javascript 中要达到封装的目的,只有通过创建作用域来达到隐藏的目的。

const xiaoming = ( function() {
// 封装私有数据,变量命名前缀加_下划线是潜规则,表示是私有的
 const _name = '小明',
     _age = 22
// 向外界提供公共方法
  return {
    getName() { return _name },
    getAge() { return _age }
  }
})()

xiaoming.getName()  // '小明'
xiaoming.getAge()  // 22
xiaoming._name  // undefined
xiaoming._age  // undefined
  • 封装类型

Javascript是弱类型语言,封装类型没多大意义。(完)

  • 封装实现

对象只需要对外提供一个固定的接口,而内部如何实现这个接口,对外界来说应该是透明的,外界不用关心这个对象内部如何去实现这个接口,外界只需要调用这个接口就行了。只要这个接口不变,内部的实现如何去改变都不会影响外界。只要你打一个电话给我,你都会得到我送给你的一颗同品牌同口味同款式的棒棒糖,你不用管我怎么得到这颗棒棒糖,你想吃的时候只需要打一个电话给我就好了嘿嘿嘿。

/**
 * 有一个 obj对象,这个对象有一个接口
 * 外界只要调用这个接口,就会返回一个数字 666
 * 接口的实现对于外界来说是透明的,外界只需调用接口
 */
const obj = ( function() {
  let _num = 666
  return {
    generate666() { return _num }
  }
})()

// 外界只需要调用这个对象的generate666接口就行了
obj.generate666()  // 666

/**
 * 某天这个对象的内部实现改了,但是接口没有改
 * 依旧是对外返回一个数字 666
 */
const obj = ( function() {
  let _num = 600 + 60 + 6
  return {
    generate666() { return _num }
  }
})()

// 外界依旧只是调用这个对象的generate666接口就行了
obj.generate666()  // 666
  • 封装变化:

考虑你的设计中哪些地方可能变化,这种方式与关注会导致重新设计的原因相反。它不是考虑什么时候会迫使你的设计改变,而是考虑你怎样才能够在不重新设计的情况下进行改变。这里的关键在于封装发生变化的概念,这是许多设计模式的主题。
----《设计模式》

找到变化的东西,封装起来,和不变的分开。这样就能够容易地把变化的东西替换掉。

继承

顺藤摸瓜图

Javascript 是基于原型的语言,没有类的概念。但是你可以用类的概念去理解,实际上在 Javascript 中,“类”也是一个对象。
原型模式是一种设计模式,也是一种编程泛型。
在诸如 JAVA 或者 Python 这种以类为中心的面向对象的语言来说,类和对象的关系就像是设计图和成品的关系。要创造成品(对象),就先设计图纸(类),然后按照图纸(类)制造出(new)成品(对象)。
而在 Javascript 中,虽然有 new 这个关键字,但是他做的事情和 JAVA 或者 Python 做的事情不一样。
Javascript 中 new 做的事情是通过找到一个对象的原型进行克隆,获得新对象。
Javascript 中绝大部分数据都是对象,根对象是 Object.prototype,所有对象都克隆这个根对象。

// 所有对象都克隆 Object.prototype
const o1 = {}
const o2 = new Object()

Object.getPrototypeOf( o1 ) === Object.prototype  // true
Object.getPrototypeOf( o2 ) === Object.prototype  // true

// 如果在 Object.prototype 上增加一个属性,那么所有对象都会增加这个属性
Object.prototype.six = 666

const o3 = {}
const o4 = new Object()
const o5 = new Date()
const o6 = new Number( 66 )

o3.six  // 666
o4.six  // 666
o5.six  // 666
o6.six  // 666

要创建一个新对象,不是通过实例化类,而是找到一个原型对象进行克隆,得到新对象。
而在现代浏览器中,都会向外暴露一个 __proto__ 属性(在ECMA规范中的描述是 [[ Prototype ]]),这个属性指向这个对象的原型对象。原型对象会有一个 constructor 属性来存储它的构造函数名。
这样在访问一个对象的时候,如果本身有属性,就返回本身的属性,如果没有,对象会去它的原型对象上找这个属性,如果原型对象上还没有,又会去原型对象的原型对象找,一层一层找,如果你愿意一层一层一层地拨开原型对象的属性,直到尽头 null。如果找不到了,你会发现,你会流泪,就返回 undefined。这不就是原型链的原理吗嘿嘿嘿。
这个克隆的过程是引擎帮忙实现的。但是我们也可以实现一下 new 的过程。

function A() {
  this.ownNum = 666
  this.ownFn = function() { console.log(123) }
}
A.prototype.protoNum = 888
A.prototype.protoFn =  function() { console.log(456) }
A.prototype.constructor = A  // 隐含属性,无需设置

const a = new A()
a.constructor === A  // true
a.__proto__ === A.prototype  // true

/** new 的过程 **/
// 克隆 Object.prototype
const o1 = new Object()

// 让 o1 的 [[ Prototype ]] 指针指向一个原型对象
o1.__proto__ = A.prototype

// A.call( o1 ):给 o1 设置新的属性,o1可能被改变(取决于 A构造函数 中是否使用了 this)
// o2 = A.call( o1 ):A 构造函数可能直接 return 一个新对象,而不是默认 return 构造出来的 o1 对象
// 比如 function A() { ... return {b: 9} },那么在 new A() 之后返回的是 { b: 9 },而不是默认的 o1
const o2 = A.call( o1 )

// 总是返回一个这个新建的对象
// 无论是 o1 还是 { b: 9 },如果有 { b: 9 },那么这个的优先级是最高的
return typeof o2 === 'object' ? o2 : o1

而这上述所有过程涉及到的继承是这样的:(每一个函数都有 prototype 属性)

/*-------------------< 代码的层面 >---------------------*/
// 引擎实现的代码 [native code]
function Object() {}
Object.prototype.xx = xx

// 引擎实现的代码 [native code]
function Function() {}
Function.prototype.yy = yy

// 用户编写的代码
function A() {}
A.prototype.zz = zz

// new一个对象
const a = new A()
/*-------------------< 代码的层面 />---------------------*/

/*-------------------< 背后的逻辑 >---------------------*/
a.__proto__ => A.prototype

A.__proto__ => Function.prototype  // typeof A === 'function'
A.prototype.constructor => A 
A.prototype.__proto__ => Object.prototype  // typeof A.prototype === 'object'

Function.__proto__ => Object.prototype
Function.prototype.constructor => Function
Function.prototype.__proto__ => Object.prototype  // typeof Function.prototype === 'function'

Object.__proto__ => Function.prototype  // typeof Object === 'function'
Object.prototype.constructor => Object
Object.prototype.__proto__ => null
/*-------------------< 背后的逻辑 />---------------------*/

在访问 a 的时候,如果本身有想要的属性,就会返回本身的属性,此时用 a.hasOwnProperty( 'a自有属性' ) 进行属性检测是返回 true 。如果 a 本身的自有属性没有,此时就展现原型链的作用了。a会通过 a.__proto__ 访问到 A.prototype ,在 A.prototype 找到了就返回,如果还没找到,就继续一直往下找,直到终点 null ,还没找到,就返回 undefined

可以说,最初的时候,每一个对象都是一样的,都是从 Object.prototype 上克隆而来,都是空白的。但是 Javascript 的世界是缤纷多彩的,大家都一样就没什么意思了。
于是有些对象被人为(引擎)增加了 length 长度的数据属性,有了 pushshift 等方法属性,再用语法层面的 [] 符号表示,就成为了定义中的数组,即Array

function Array() {
  this.length = xxx
}
Array.prototype.push = function() {}
Array.prototype.shift = function() {}

var arr = []
// 等价
var arr = new Array()

是不是和 function A() {} 的套路很像?是的。
同样道理,Date 对象也是如此,因为这个对象有了 GMT 时间的数据属性等,又有了 getDaygetMonth 等方法,就成了定义中的日期对象。
Javascript 的继承的另一种理解,更像是“借用”。类似 Object.prototype.toString.call( ... )
继承的本质是改变这个对象的 __proto__ 指向。
一个对象有两种属性,自有属性和继承属性。自有属性就是本身自己身上有的东西,访问之后直接就给你返回的,继承属性其实算是借用的属性,是通过访问自身的 __proto__ 的指向对象属性,这时候已经是在访问其他对象了,不是自己的属性。
就好比如两个对象其实是不认识,不相关的,但是你把 A对象 的 proto 属性改为 B对象,这两个对象就产生了关系,这时候你也可以称为继承。
这种理解和其他基于 CLASS 类与对象的继承有很大的区别。
此外,继承也是代码重用的手段,Javascript 中的继承不是通过严格意义上的拓展父类来实现的,而是通过原型实现的。
形如 Photoshop,一般都不会直接操作原始图层,都会复制(克隆)一份图层,在复制的图层上进行编辑。

相关文章

  • iOS开发之一OC语言的特性

    三大特性:封装、继承、多态 一、封装(成员变量的封装) 二、继承 三、多态(多种形态)

  • java多态

    概述 面向对线的四个基本特征:抽象,封装,基础,多态。抽象,封装,继承是多态的基础,多态是抽象,封装,继承的表现 ...

  • Python基础-day12

    list ​ 封装 ​ 继承 面向对象的三大特性 ​ 封装 继承 多态 封装 ​ 生活里 ​ ...

  • 31-封装、继承、多态

    封装、继承、多态 面向对象编程有三大重要特征:封装、继承和多态。 继承 继承机制实现了代码的复用,多个类公用的代码...

  • Java的基本特性

    关系 Java的四个基本特性分别是抽象,封装,继承,多态。概括起来可以这么理解,抽象、封装、继承是多态的基础,多态...

  • iOS基础 - oc的三大特性

    封装、继承、多态 oc是面向对象的程序设计语言,而面向对象的三大特征:封装、继承、多态。 封装 没有绝对的封装,只...

  • Java笔记

    Java基础 Java面试通关要点 1.面向对象的特征 封装 继承 多态 封装继承多态 2.final,final...

  • iOS 语言三大特性

    封装 、 继承、多态OC学习篇之---类的三大特性(封装,继承,多态) - Android应用安全防护和逆向分析-...

  • day09-Java面向对象三(多态/抽象类/接口)

    9.0 Java面向对象三大特性 封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的 多态概念:指...

  • 2020-05-16--Java--day09【继承、super

    1.继承 三大特征:封装,继承,多态 继承是多态的前提,没有继承就没有多态 继承的主要解决的问题就是:共性抽取 在...

网友评论

      本文标题:多态、封装、继承

      本文链接:https://www.haomeiwen.com/subject/exqdmttx.html