JavaScript进阶(二):面向对象的写法和本质,类型检测

上篇博客我们讲了面向对象,但是说的在好,没用。你要把它变成可以执行的代码,才能够真正的工作起来。

所以接下来,我们就来看一看,面向对象怎么写。

首先我们都知道,任何一个语言,它发展都是一个过程,尤其是我们的 js,更是如此。

所以大家常见的,我们的面向对象一般情况下,其实有 2 种写法。

一个是旧版本的写法,就是在 ES6 出现之前的那种写法。

还有个,就是我们现在新版的写法,也就是 ES6 里面的 class。

我们前面说过,对于一个面向对象的东西来说,它应该具有类。

所以我们得先写一个类,然后接下了来有了类之后,再去实例化,等到实例化完了之后,就会得到一个实例(也有人管它叫对象)。

类 -> new -> 实例(对象)

那么在我们 ES6 之前的写法当中,我们会用一个 function 来做。

首先,我们知道任何一个类,其实它都需要一个东西,就是构造函数。(也叫做构造器)

什么叫构造函数呢?

很简单,就是当你去实例化我这个类的时候,我要做一些初始化的工作。

实际上来说,在老写法里面,类和构造函数是不分的,这也是它最大的一个问题。

比如: function A() { }

那么就这个函数 A,它到底是什么?

其实它既是构造函数,同时它也是类。所以这个就搞的很不清不楚的,很模糊。

那么我们来试试:

可以看到控制台上的输出:被创建了。

实际上来说,这里的被创建了,就是 new 的过程。

所以,我们知道了2件事:

1,在 ES5 里面,构造函数和类,是不分的。

2,在我们去实例化一个类的过程当中,它就会运行你的构造函数,然后里面的代码就都可以被执行到。

那么在 ES6 里面,它又是怎么样的一个写法呢?

首先它有了一个专门的关键字叫做 class,类的英文就是 class。

原来的 ES5 有一个最大的问题,就是一眼望过去,我压根不知道它到底是个类还是个普通函数,因为都是 function。

而现在有了一个专门的关键字,这个是件好事,这是第一。

第二,它有了专门的一个构造函数,constructor。

所以到了 ES6 里面你会发现,类是类,构造函数是构造函数,两者终于拆开了,这样至少从概念上没有那么容易混淆。

可以看到,这个时候我们的构造函数也会被执行。

从这点来说,两边的写法上是完全一样的。

然后前面我们也说过,所有的类也好,对象也好,它上面就 2 个东西,一个是属性,一个是方法。

所以接下来,我们就给它上面添加属性:

可以看到,加属性的这个环节,不论是 ES 几里面,都还是这样的。

所以,不论是以前的写法,还是现在的写法,其实它里面的意思差不多。

只不过写法上更加的清晰了而已。

然后接下来,类还包括一个东西,叫做方法。

在原来的写法中,我们是通过给原型 prototype 添加方法:

这个写法其实有一个特别大的问题。

就是你把这个类给拆散了,相当于你是毫无关联,独立的 2 部分。

函数是一块,下面的 prototype 是另外一块。

不是说它不行,就是比较乱。

而到了 ES6 里面,它的写法就好了很多,这个方法可以直接写在类里面,它是一个整体:

可以看到,效果上 2 边是一样的。

当然,顺带一提,有的人可能问会:class 里面的方法为什么不用加 function,是简写吗?

我们加一个试试:

加完这个之后,你会发现不行,报错了。

实际上来说,它并不是一个简写,它本身就不是一个函数,它就是一个方法。

那么 constructor 有参数吗?

其实 constructor 就是一个普通的函数,所以它传参的方式,就和我们平常函数的传参是一样的。

那么 constructor 的参数是在哪来的?

就是你在 new 的时候来的,我们在 new 的时候,其实就是在调用那个 constructor。

那么我们来总结下,ES5 这种写法到底有什么样的缺陷:

第一,它本身没有专用的类,它就是 function,那我跟普通函数根本分不清。

第二,它会额外的需要一个 prototype 来帮忙。

不是说不好,这个 prototype,它本身的作用不是在给你写这个类的时候去用的,而是将来我需要对一个类动态的修改的时候,我才需要用到它,以及 prototype 在继承的阶段还会有很多其他的问题。

而新版的写法就方便很多,干净很多。

那么到这为止,我们对类的写法已经没问题了。

其实很多的技术,它本质上就纯粹是为了方便你,并不是说离了它不行。

比方说我们的这个面向对象,就是其中的一个。

我希望大家可以不光会用面向对象,你还要了解面向对象它的本质是什么,现在我们要说的就是这个事。

实际上来说,我现在写出的这个代码,本质上它完全可以用另一套写法来代替。

什么意思呢?我们再创建个 html,两边对比着来看:

其实这个东西,就完全等价于右边这个写法。

当我想要用 a.show() 的时候,其实完全等价于 a_show()。

或者我说的更直白点,为什么要把它拆了?

很简单,因为这些所谓的属性也好,方法也好,你可以认为,它是在这个类当中给你提供了一个命名的空间。

什么意思呢?

比方说,因为各个实例之间,它都有自己的 name 和 age,这个它们之间是不冲突的,其实就相当于在右边定义了 2 套变量,是一样的效果。

实际上来说,类就是这么个东西,它本质上就是一个命名空间,它只是帮你把这些东西包在一起,为了让你用着方便而已。

所以,实际上类这个东西,并不是非用不可。它做的事,就是可以不用你自己去搞右边的那套东西罢了。

你比如说我需要 100 个 A 的实例,那我是不是只要 new 100次就行了,甚至我还可以直接写个循环 new。

那这样的话,比我自己去定义几百个变量和函数,是不是要方便的多呢?

这个,就是类的本质。

那么总结下:

其实一个类,它仅仅只是一个空间,帮你把这些属性也好,方法也好,都给你变成一个在这个空间之内是唯一的。

这样的话,你就可以放心大胆的去用,这些相同的类里面,不同的实例,它们是不会冲突的,就是为了这个。

或者说的更直白点,其实类,它最重要的一个目的,就是为了大量的去用。

你比如说,我这个类将来就只会 new 一个,多了没有。

那么这时候,其实你这个类存在的价值并不大,你干脆还不如直接把他定义成一个变量,反正你就用一次。

所以说,类这个东西,它只是为了你方便,没有说你必须得用它。

现在我们已经了解了类最基本的东西 。

接下来我们要说的其实是一个小事,就是我们如何去做类型检测。

说个更直白点,比如我现在有个参数,这参数到底是什么类型的,我得知道吧?因为有的时候,我需要根据不同的类型来做不同的事。

在我们 js 里面,常用的一共有三种方式的类型检测:

1,typeof。

2,instanceof。

instance 我们前面也说过,它是实例的意思。那么 instanceof 它的意思就是:是不是某一个东西的实例。

比如,A instanceof B 的意思你也可以理解为:看 对象A 的原型链上 有没有 函数B 的原型。

3,constructor。第三种其实不算是官方推荐的方法,它是一个野路子。

(Object.prototype.toString.call() 这里就不说了)

首先,typeof 很简单,它更适合于用来检测基本类型。

什么叫基本类型?

我们 js 里面就那几种:number,string,boolean,function,object,undefined。

typeof 它只适合基本类型,什么意思呢?我们来试试。

那么为什么说 typeof 它更适合用来判断基本类型呢?

很简单,因为它区分不了你具体是哪种对象。比如:

可以看到,它都是 object。

这就是为什么 typeof 仅仅适合用来判断基本类型,因为如果你是对象类型,它就都是返回 object,这个就不太合适。

如果我现在就想检测它到底是哪种对象,这个时候你就可以用 instanceof。

它的用法很简单,比如:a instanceof Array,意思就是,a 是不是 Array 的实例。

所以,instanceof 是用来检测一个东西到底是不是这个类的实例,它更适合于用来去判断这个实例的具体类型。

但是,instanceof 它不光对你那个具体的类型有反应,它还对父类有反应。

什么意思呢?我们先让 A 这个类继承自 Array,然后在 new 出 A 的实例:

你会发现一个有意思的问题,这 2 个都是 true。

第一个还好理解,那么第二个 true 怎么理解呢?

我尽管是一个 A,但是我的父类是 Array。

换句话说,instanceof 不光能检测你的直接类型,也能检测你的父类型。

这时候我们可能就有一个想法了,就是如何判断它到底是不是一个 Array。

如果我们有一个这样的需求,constructor 就可以出来了,它可以帮助你很精确的去检出它具体的类型。

constructor 它的作用很简单,就是告诉你,这个实例,是用哪个东西构造出来的。

可以看到,constructor 它就标明了,当初是谁把我制造出来的。

实际上来说,它就是干这个用的。

然后这个时候,如果我想检测 a 到底是不是 Array,多了不行,那么我们就可以这么做:

所以,如果我真的是一个数组的话,那这时候就是 true。

然后我们需要知道一点,这个 constructor 它本身并不是说专门用来检测类型的,它的作用是可以帮助你来返回实例的构造器。

只不过我们可以利用它间接的来完成一个精确的类型判断。用人话说就是,只包括子级,不包括父级。

一般来说,typeof 和 instanceof 用的多一些,constructor 其实是个野路子,极少的情况会用到它。

我们只需要知道 constructor 偶尔也能用来判断类型就行了。

然后顺便一提,关于类型判断,有一道面试题相信大家都听说过,就是如何判断是不是一个数组,相信到这里,你已经没问题了。

那么,如果是文档节点的类型,又该怎么判断呢?比如,如何判断一个东西里面装的是不是 div?

关于 DOM 里面的东西,很多人都不是特别了解,比如 div 又是谁构造出来的?

这时候,constructor 就可以登场了。

可以看到,它是一个 HTMLDivElement,那么我们就可以用这个东西去判断:

那我不想判断它是不是 div,就想广义的判断它是不是元素。

这个时候我们是不是就可以用 instanceof 来判断,你是不是一个 HTMLElement:

因为不管你是 div 也好,a 也好,body 也好,你是什么都可以,最后你这些东西都是从 HTMLElement 这个类里面继承出来的。

所以我们就看到了,这个类型的检测还是比较丰富的。

一些基础类型的,我们可以用 typeof。

然后一些对象类型的,我个人是用 instanceof 更多一些,因为我们一般来讲,会把子级视作父级来处理。

最后的 constructor,它可以帮助你去返回精确的类型。

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

猜你喜欢

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