JavaScript进阶(一):什么是面向对象,什么是面向对象思想

这篇博客讲解的都是些概念性的东西,略微枯燥,如果不感兴趣,可以跳过。

(吐槽一句,概念性的东西真不是我们这些普通人玩的,自己都写吐了)

面向对象这个东西,大家平常多多少少肯定是用到的。

但是第一,平常在工作里面自己动手去写原生对象的机会并不是那么多,基本都是用别人写好的框架,最多去继承个别人的什么东西之类的。

然后第二,面向对象这个东西,它不是简简单单的,我写个 class,然后把它 new 出来,里面放几个方法就完了。

实际上来说,它涉及到很多层面的东西,比方说,我到底要如何去看待一个对象,然后包括我这个对象到底都能做什么,以及我想让我的这个对象变成一个能够响应的状态,等等这些东西。其实全是我们接下来要说的一些事。

那么直接进入正题。

首先,我们都知道一件事:

面向对象这个东西非常的常见,而且又很重要,几乎所有的库,框架,还是各种各样的东西,它们都是以一个对象的形式来存在的。

那么为什么?

为什么它们要这么做?

这个东西跟我用函数去做,有没有什么实际的区别,有没有好处?

那么我们现在先涉及到第一个话题:

就是面向对象这个东西,你千万不要觉得它很陌生,因为我们经常会用。

比方说 React,它里面所有的组件就是一个 class,然后在它里面你可以去实现你具体的东西。

class Cmp extends Component {
    constructor(){
        this.state = {
     	    count: 0
        }
    }

    fnAdd(){
        this.setState({
            count: this.state.count + 1
        })
    }
}

还比方说 Vue,你可能会说 Vue 里面我们可不写 class,其实它里面搞的 json 背后,最终也都会被封成 class。就比如:

export default {
    data(){
        return {
        
        }
    },
    methods: {

    }
}

实际上来说,你会发现一个事情,就是不论面向对象这个东西以何种形式呈现,到底是个组件,还是个库,还是个什么东西,你会发现到最后,它主要关注的就是两件事:

一个就是状态的问题。实际上来说,状态有很多种叫法。

假设我是一个组件的话,那用户现在登没登录,这是一个状态。然后我现在已经获取到的数据,这也是一个状态。

以及我们其实还关注着另外一个东西,就是它有一些各种各样不同的操作

比如我点了这个按钮,有个什么反应。我点了那个按钮,数据多了还是少了一条,等等一系列的事。

你会发现,这些东西它们归根到底,到了最后都是这两件事:状态,操作。

你说能找出脱离这两件事的东西吗?不能。基本上都是他俩。

所以我们其实就可以做一个最简单的描述:

我们平常所说的对象,其实它就是两个东西:一个是状态,也就是数据、一个是操作。(对象 = 数据 + 操作)

在面向对象里面,其实涉及到很多概念性的东西。

既然我们想扯这些概念性的东西,那我们就扯的比较的官方一些,我们来看看官方是怎么定义的。

一般来讲,像是数据类的东西,它一般叫做属性。

首先,比较出乎大家意料的是,在类里面,属性这个东西它其实不是最官方的叫法,最官方的叫法叫变量。

这个东西,它本身就是用来表述我们对象内部不管是一些状态还是数据等等一系列,都是靠它。

当然了,其实它还有很多叫法:有的人就管它叫状态,也有的人叫数据。

然后,方法其实也不是最官方的叫法,最官方的叫法叫过程。

当然这个东西也有很多的叫法,比如方法,函数。

实际上来说,叫法这些东西很多,有各种各样的叫法,但这个不是重点,只要大家能够互相交流就可以了。

那么,首先我们也就明白了一个事:

我们的对象,其实它的组成就是包含这两种东西。

你所有的对象,不管是个组件,还是多大多复杂的东西,归根到底就是它们俩。

对象组成:

变量 --- 属性、方法、数据。

过程 --- 方法、函数。

然后我们在说下类(class)这个东西,它跟我们平常所说的对象有一点容易混淆。

其实真正的叫法当中,是不存在对象这个说法的,它叫做实例(instance)

首先举个最简单的例子,比如我们平常用的:

let date = new Date()

对于它来说,这个 Date 就是类,而 date 我们 new 出来以后的这个产物,它就是实例。

那么类和实例我们怎么区分呢?

其实非常的简单。类这个东西,它本身是没什么功能可言的。

比如,相信大家一般都不会这么做:Date.getTime()

因为即使你做了也出不来什么结果。所以我们不能直接在类上去进行一个很多的操作。

但是你却可以在实例上来进行这个事情:date.getTime()

说的更直白点:

类,它只是一个蓝图,设计图一样的东西。

它本身规定的就是,你这个类,它里面有什么东西。

比如我有几个属性,几个方法。

但是都是死的,什么时候能用,得到了它实例化以后能用。

用人话说,就是把它 new 出来以后就能用了。

而实例是由这个类创建的,真正有功能的那个东西,这才是实例。

当然,一般来讲,绝大多数人,在说到对象这两个字的时候,其实更多的指的是实例。

当然还有一个概念,也需要我们提前说明白,就是关于成员这个东西。

一般来讲,你看国外的文档,这个东西也经常性的会出现,它都是用 member 来形容这个事。

那什么叫成员呢?

很简单,比如说一个班级的成员,就包括所有的学生和老师,这就是成员。

我们的类也一样,它的成员,说白了就是它里面都包括一些什么,就这么简单。

说的更直白点,就是我们刚才说的属性和方法。

只不过像这个成员它有一些比较细分的一些东西。我们也得知道一下。

比如说,有一类,叫做实例成员。什么意思呢?

说白了,像这个东西,我们只有在那个实例上能够去用到的东西。

比如说一个最简单的例子:

我们假设 str 是一个字符串,那么我们可以去用 str 的 length:str.length

那你有没有试过在大写的 String 上面用 length:String.length

其实它身上的 length 是没有实实在在的用处的。

还比方说,我们平常可以在一个数组里面去 push 东西:arr.push()

这个 push 也是成员。

成员既包括成员属性,也包括成员方法,这个无所谓。

但是像这样写:Array.push()

肯定是不行吧?

所以像这一类的东西,我只有在实例上才能去使用的东西,叫实例成员。

当然,与它相对的,还有另外一个东西,叫类成员。

说白了,就是类上拥有的。

其实我们应该都用过,比如说:Math.random

可能你平常用不着,但是你知道 Math 上是有这么一个方法的。

像 Math 这个东西,应该是没有人把它 new 出来以后再去用的吧:new Math()

所以像这种就属于类成员。

所以,字面意思:

实例成员,就是实例上面有的成员。

类成员,类上面的成员。并且它们有一个特点,无需实例化,直接使用就行。

而成员,它是一个统称,它一般是实例成员和类成员的统称,这个无所谓,你知道有这么个事就行。

接下来,还有一个小东西有必要说一下,如果你平时经常去看一些技术文档之类的东西,你会经常见到这个词:抽象。

抽象这个词,说句实话,用的稍微有一点乱。

抽象一般来说,在我们的语言环境当中,会有两个含义:

1,当你去设计一个类,或者你去设计一个程序的时候,你往往要经过一个叫做抽象的过程。

什么意思呢?就好比说,我现在需要设计一个关于人的资料的数据库。

那么我到底需要这个人,他的哪些信息进来,这个过程其实就叫做抽象。

一个真真正正的人,他的特性太多了。年龄,身高,体重等等,但并不是所有的这些数据,你全都关心。

再比如说你在网上卖东西,其实也是需要经过抽象的一个过程。就是我需要设计一下。

比如我想出售一辆汽车,因为和车相关的东西也挺多的,那么我到底需要哪些:比如车的牌子,什么颜色,加的几号汽油等等。

所以抽象它的第一个含义,指的其实是我们平常来提取这些信息的这么一个过程。

然后还有另一种说法:

2,有这么一类东西,它不提供真正的实现,比如有一种类,叫做抽象类。

这个抽象类,它并不能真的拿去用,比如说你去 new 一个抽象类,不行,它 new 不出来,因为它本身就不允许你去 new。

你可能会问,那它有什么用呢?实际上它的作用是提供一个公共的基类。

比如说一个最简单的例子,像 React 里面,你定义一个组件,它一般都要去继承一个 Component,然后我才做具体的事:

class Cmp extends Component {

}

在这里,其实这个 Component 对应的功能,就是一个所谓的抽象类。

那么为什么需要这个东西呢?

因为我需要我所有的组件,都具备一些公共的东西。不可能说让每个类自己去实现这些东西,那这个学习成本和使用成本就太高了。

所以我们就可以搞一个这样的抽象类拿出来去用。

但是如果我们这样做:

let comp = new Component()

我们去实例化一个 Component,这样做几乎没有意义,因为它就是一个空的,它里面什么功能都没有。

所以抽象其实对于我们来说,是非常重要的一个东西,有了它作为保证之后,我们就可以很轻松的让很多的类保持一样的特性。

然后接下来,我们就要开始非常重要的一件事,就是面向对象思想层面的东西:

实际上来说,在我们周围,几乎很多人都在说面向对象思想,比如你在写程序的时候,是否就有人告诉你,要具有面向对象的思想之类的。

但是说句特别实在的话,到底啥是面向对象思想,就没人能把这事说清楚。

我们可以随便百度下,不管是文章还是百科之类的,你会发现关于面向对象的东西一大堆,但是看完之后也不知道在说啥。

接下来我会尽量用一个简单的方法,来对面向对象的思想做一个简单的介绍,然后在后面,我们再慢慢来体会这个事。

首先,面向对象思想,如果硬要说的话,就3条:

1,封装。

2,继承。

3,多态。

然后我们接下来,就来好好的说说,这三个思想到底代表什么。因为有了这三个东西之后,我们用起来就会很方便。

首先,先来说下封装:

封装它的一个最基本的目的,是为了保护我们类当中,实例当中的各种成员。

这时候大家可能很奇怪了,你说保护杯子,我拿一堆泡沫给它包起来,防止它摔了,这我能理解。但保护成员怎么理解?

其实我们在写代码的过程当中,不论你是用面向对象的思想还是其他什么思想,随便你怎么折腾,但是你有一个最终的目的:是为了让这个程序出错少,效率高,易于调试,将来好维护等等,你用什么都是为了这个目的。

那么接下来,我们就必须得面对一个现实:

程序都是人写的,是人就会有一些问题。比如说容易偷懒,会有一些侥幸心理。

比如:这个东西你不让我这么用,没事,我这样用可以出来,无所谓啦,出来就好了。

就类似于这种感觉,他会有一些偷懒,侥幸这样的一些心态在里边。

实际上来说,这个可以通过很多方式来解决。

比如说所有的代码在提交的时候,让组长或负责人检查一遍,然后一旦发现有违反编码规定的行为,然后就惩罚啥的。

但这个并不是最好的,因为这样负担会很大,负责人也不用干啥事了,每天提交上来的代码都看不完。

所以说最好的方式就是,不要靠人。

那么我们就需要的是,让代码本身就可以去有一些规则:我这个东西,就是只读的,你就改不了。

实际上来说,一个完整的对象,它里面的很多的属性,很多的方法,它们之间并不是独立的,它可能互相之间是通的,是有关系的。

比如说,我规定了:如果你要想加一个新东西,你就得通过我提供的一个方法,比如说 addItem。

你就得走我这个方法,你不能直接往这数组里面 push。

那么我问大家一个事,说了就好使吗?不一定。我们必须得通过一些其他的手段。

这个是封装的第一重含义,就是不要让用这个对象的人,有一些去乱搞的机会。

然后第二个,就是数据的隐藏。什么意思呢?

比如说上面为什么不让你直接的去改这个数组,直接 push 一下,大家都方便,对吧?

很简单,因为你去修改这个数据的时候,我是要同步的去修改我其他的东西,因为数据之间是有关系的

所以,如果说我的数据真的暴露出去了,就有人有机会去改。那么我就需要去把这个数据隐藏起来,你必须得通过我的方法来改。

当然这时候又有另一个小矛盾,就是我们都搞成方法,这个又很麻烦,比如我直接读就行了,你还得给我弄一个 getXXX() 一大堆啥的,很麻烦。所以,我们如何让它又简单,又能起到保护的作用,这是一个问题。

然后封装还有一个事,就是我们可以让他去强制规定一个访问的权限。

比方说,我有一些状态,我就是不允许你改,我给你看可以,但你不能改。或者这个东西你连看都看不了,你只能通过我的函数怎么怎么样,就是这样一个感觉。

当然,整个东西,最终的目就是为了可以让你这个代码,更易于被人理解。

实际上来说,我们都知道一件事,就是你不可能把整个公司的项目,它里面的每一行代码,全都看过,全都能理解,这个几乎是不可能的。

所以说,我们必然涉及到一个问题,就是说,我得去用别人去写好的,实现好的代码。

那这个时候,这个类,它本身易不易于理解,就很重要了。

所以封装在一定程度上面,还可以便于别人去理解。

所以,面向对象的第一个思想叫做封装。什么叫封装?

封装的目的就是为了让别人不能轻易的去破坏你里面的东西。我里面的东西你不能直接碰,你必须得通过我提供的方法来操作,按照我的那个要求来走,这样就可以起到一个保护的作用。

封装:

1,保护成员。

2,数据隐藏。

3,强制访问权限。

这三个东西其实就是封装里面最重要的三个基本思想。

然后第二个,继承。

假设你只用面向对象,不用继承,每个类我都是新写的,那么这个时候,你几乎只能发挥出这个面向对象的20%左右的功能。所以说继承这个东西它是非常重要的。

那到底什么是继承,我们从来没有给它下过一个很标准的定义。

很简单,那就是任何一个类,可以不用从零开始写起,而是可以直接在一个已有的,原有的类的基础之上,再去给它做一些修改,做一些添加,或者怎么怎么样,这个就叫继承。

说的简单点就是,父类的东西,我直接拿过来,子类就不用在实现一遍了,这就是继承。

然后继承的目的是干什么的?

非常简单,第一个就是为了重用代码。说白了,就是父类里面写过的,子类不用在实现一遍了。

然后第二个好处就是,你无需去修改父类。

实际上来说,继承有一个问题,就是如果父类本身不够用,需要一些新的功能,那我们直接去改不就好了吗,为什么要去继承呢?

很简单的一件事,第一,并不是所有的类,你都有权利去改。

比方说我现在这个类是来自于某一个库里面,就比如 React 里面的 Component。我就觉得缺一些东西,想给它加一些东西。

那么你能跑到人家库里边去改人家的代码吗?行,但是不太好。

为什么呢?因为将来只要一升级,就把你修改的代码给覆盖掉了,如果还想保留,那你又得改一遍。所以每次更新你都得搞一遍,这个东西就废了。

所以说我们修改父类有的时候是做不到的,这是第一种可能。

还有第二种可能,就是你可以修改,它也是你自己的代码,但是最好别改。这种情况出现在什么地方呢?

比方说,我这有一个类,它已经用了好几年了,不说它多好多坏,至少经过时间检验没什么大毛病,因为有毛病早就提出来改完了,不会说用了这么多年也没再出过事。

那么像这种类,你不要轻易的去动它,因为你一旦动了,就有可能引入一些新的问题,有可能出现一些新的BUG,所以说像这些情况,都是我们不应该去修改父级的情况。

所以在这时候,你就可以去继承。

那么说到继承,我们又有 2 个分支的小概念:

1,多重继承。

这个是在人类社会中绝对不可能发生的事,就是一个人有多个爹,并且都是亲生的。

说的更直白点,就是在我们的面向对象的过程当中,每个类它可以同时具有多个父类。

首先大家可能会觉得奇怪说,我为什么要有多个父类?因为你可能需要多个父类的特性。

比如还是 React 里面的例子:我现在有一个父类是 Component,它给我提供了很多的功能,比方说帮我提供了一些渲染,帮我提供了一些操作状态等等,这样的一些方法。

但是我还有另外一个父类 Ajax,这个父类里面给我提供了很多其他的一些功能,比如说数据的管理,跟服务器之间的数据交互等等一系列。

假设你现在的组件之内,这两种东西都需要,那么你就可以用多重继承来写。

class Cmp extends Component, Ajax {

}

这个就叫多重继承。

当然,这个在 js 里面是不直接支持的。为什么呢?

因为这个多重继承会让结构和状态变的特别混乱,所以我们一般不会直接去用它。相反,我们都是用其他的一些手段来完成目的。

接下来还有另一个概念:

2,抽象类。

这个在上面我们也提过。就是说,我这个类,本身它不具备实际的功能。

你说让我去干活,你 new 个我,白 new,没有意义,跟 new 了个 json 一样,空的。

但是我提供了一些公共的属性或者方法,一般是作为基类或者是父类来使用。

所以,继承的好处:

第一,是为了最大限度的去重用父级的代码。

第二,无需去修改父类。

当然顺便一说,重用父级的代码并不一定只是为了省事,还有另外一个目的。

就是万一将来父类的代码发生了任何修改,子类不用动,就可以享受到这种变化,这也是一个目的。

最后,还有一个多态。

首先,什么叫多态?很简单,它其实是一种抽象。

简单来说,就是说我现在这个类,我不提供真实的实现,至于到底谁来实现,我不管,反正将来总有人干,总有人会现实这个东西。

比如举一个最简单的例子:

假设,我们现在有一个类,是专门用来描述我们公司里面的员工。首先,它肯定是个抽象类,你公司里面不可能有真正意义的员工。

大家可能觉得奇怪,不对啊,我们公司到处都是员工。

实际上来说,你们公司里有的,只能是具体哪个人。你比方说有个前端,有个测试,有个运维等等。

其实你都是这些具体的,员工本身就是一个抽象概念。

然后这个时候我们可能碰到一个问题,就是工资管理系统。

我们不得不承认,不同的岗位,它计算工资的方式完全一样吗?差的还蛮多的。

那么在这种情况下,我要怎么办?

我先判断下,if 如果这个员工的 type === 前端,那我就用这套方式来算。

然后 else if 如果这个人是测试,我就这样这样算。else if else if 一大堆,这个是不是就乱了。

而反过来,如果你用了多态的这种写法之后,没关系,我现在就是把你们都当做员工,这个抽象概念来理解。

然后我就直接去用你们提供给我的那个计算的方法,我不去管到底怎么回事,你自己怎么算是你自己的事。

这个其实就是一个抽象的过程。

我们如果合理的去使用多态这种思想的话,其实是有3个好处的:

1,很大程度的去减少你的代码量。

2,也可以让你的逻辑变的非常清晰。

3,同时也能够帮助我们来简化我们的问题。

那么到此,这些概念性的东西就讲完了。

平时老有人提面向对象,什么意思,干什么的,什么叫面向对象啊?

我们分了几个点来说,这里稍微来回顾下。

第一,所谓面向对象。对象是什么,就是属性和方法。

你不要把它看得太复杂了,属性 + 方法就是面向对象,就这2个东西,多了没有。

第二,面向对象的过程当中,其实有很多的一些概念性的东西。

比如什么叫类?类只是一个蓝图,只是一个设计图,它没法直接的起任何作用,你需要把它实例化出来才能用。

实例化,用人话来说就是 new,new 出来就叫实例化。

然后,类实例化了之后,就会变成一个实例,这个实例是你可以真正去用的,以及它身上所有那些东西都统称为成员。

然后我们还说了一个很重要的东西,一直听人说面向对象思想,到底怎么样才叫面向对象思想?

第一,封装。

你要保护好自己的状态,不要让别人随便去碰。如果你什么东西都暴露在外面,1 不安全,2 别人需要花更多的精力来理解你这个东西。因为他看你这有个属性,他就总觉得有用,那这时候,如果我把它们都隐藏起来,别人就不关心了,那用起来反而方便。

第二,继承。

我要尽可能的在前人的基础之上去做事。所以说,继承一定是在别人已有的基础之上,不论是一个库所提供的某种东西,或者是你自己的其他部分代码的某种东西,都可以使用继承。比如,我们可以在类的基础上再去继承一个新的类出来,这样可以让它更加的灵活,更加的易用和重用。

第三,多态。它其实是一个挺抽象的概念,你很难直接的感觉到,其实说白了,就是把所有的子类,视作父类的一种特殊情况来对待,我一样用。你也可以简单的理解为,它能帮助我们简化问题。

整个面向对象,它的目的非常简单:

第一,其核心目的就是为了防止人犯错误。

第二,尽可能的去重用各种各样的代码。

当然,我们现在说的这些东西,实际上来说,大家可能都很难想到具体来说是怎么样的。

没关系。接下来的博客,我们就来看看,写成代码,长成什么样。

发布了78 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43921436/article/details/105461054