前言
在看本篇之前,建议知识储备需要有对象基础,可以查看链接学习对象基础。
目录
面对对象的理解
面向对象更贴近我们的实际生活, 可以使用面向对象描述现实世界事物. 但是事物分为具体的事物和抽象的事物。
现实生活中:万物皆对象,对象是 一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一个网页。
而在我们程序中,一切都是对象,面向对象编程,指的是程序中的所有操作都是通过对象来完成的,做任何事情都是通过对象,所有操作都是对象。
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的:
- 属性:事物的 特征,在对象中用 属性 来表示(常用名词)
- 方法:事物的 行为,在对象中用 方法 来表示(常用动词)
我们以人为例子,属性可以类似我们的手脚,方法可以类似我们打篮球,唱,跳,rap等动作。假如我们以椅子为例子,属性可以是椅子的四个脚,椅子移动则算动作。
假如我们给qi_bai(博主)当作一个对象,博主有名字,年龄,身高,体重等数据吧,博主还有很多功能,会吃饭,说话,那么这个对象就是表示博主,能吃饭
const qi_bai = {
name:'qi_bai',
age:19,
height:180,
weight:115,
//添加方法
eat(){
console.log(this.name,'在eat')
},
say(){
console.log(this.name,'说hello')
}
}
//现在还想再来一个对象,博主带上自己的宠物小白,我们定义小白这个对象
const xiaoBai = {
name:'xiao_bai',
age:19,
height:160,
weight:180,
//添加方法
eat(){
console.log(this.name,'在eat')
},
sleep(){
console.log(this.name,'sleeping')
}
}
console.log(qi_bai)
console.log(xiaoBai)
如上操作,我们就定义完成了两个对象,我们编译后可以看到这两个对象是什么样的。
但是我们发现,两个对象结构类似,我们无法区分这是人还是狗,并且我们在创建的时候使用了很多代码,如果我们要创建100个类似的对象,就会很麻烦。
这也引出几个问题,用Object创建对象,无法区分,不同类型的对象,不方便批量创建对象。
所以我们引出类这个概念。
类的理解与创建
在 ES6 中新增加了类的概念,可以使用 class
关键字声明—个类,之后以这个类来实例化对象。
- 类 抽象了对象的公共部分,它泛指某一大类(class),可以指人这个大类
- 对象 特指某一个,通过类实例化一个具体的对象,可以指博主,或者正在看文章的你
class Person {
} //创建对象的第一步,就是通过定义类
class Dog{
}
//通过类来创建一个对象
const p1 = new Person()
const d1 = new Dog()
console.log(p1)
console.log(d1)
console.log(p1 instanceof Person) //instanceof用来判断对象是不是由该类创建
也就是说类是对象的模板,我们通过类来创建对象,比如我们定义人与狗这个对象。通过同一个类创建对象,我们称作同类对象,可以使用instanceOf来检查一个对象是否是由某个类创建。
属性
类是创建对象的模板,创建对象的第一步就是定义类,就是我们如上操作,接下来则是属性
class Person{
}
const p1 = new Person();
我们在其中添加属性等操作
p1.name = 'qi_bai'
// 读
console.log(p1.name)
但是这样写有个问题,这样我们类与添加对象没有啥区别了,完全没有起到模板的作用,反而多了个创建类的操作,所以我们可以将属性直接定义在类中,如下
class Person{
name
age
weight = '60kg'
height = '180cm'//假如我们给height设置了值,这个就叫做实例属性
static test ='test静态属性'
//这个叫做静态属性,console.log打印不会出现,只能通过类当中访问
const p1 = new Person();
console.log(p1)
我们可以看到,四个属性已经被创建 ,age与name因为没有设置值所以为undefined,如果我们要读取,只需要通过对象名.属性的方法调用。这里有个test属性不能显示,因为定义了static,我们称为静态属性,当我们使用类名.静态属性名则可以调用。比如:采用 Person.test就能访问到,这种属性有两个好处,不用创建对象就能访问,假如我们用某些工具类就可以这样子创建。
注意: 类的代码块默认是严格模式
类的代码是用来设置对象的属性的,可以定义对象当中有哪些属性
方法
方法就是定义在类中的函数
class Person{
name
sayHello = function(){
console.log("Hello");
}
sayNiHao(){
console.log("NiHao");
}
static sayGood(){
console.log("Good");
//通过类来调用
}
}
const qi_bai = new Person();
console.log(qi_bai.name)
qi_bai.sayHello();
qi_bai.sayNiHao()
Person.sayGood();
我们通过 对象.方法形式调用,如果方法为静态的,可以通过类名调用。
构造函数
我们说,类的引入是为了简化创建同类对象,但是以上方法,如果创建100个Person类的对象,好像并没有简化创建。这里我们引入构造函数的概念。故名思意,这个就是一个函数,这个函数的名字为constructor,我们一开始创造对象的时候,通过设置参数,就改变了对象的name,age,与gender如下
class Persom{
constructor(name,age,gender){
this.name = name;
this.age=age;
this.gender=gender;
}
sayHello(){
console.log(this.name)
}
}
const p = new Person('xiaoBai',8,'男')
const p1 = new Person('a',9,'男')
const p2 = new Person('x',20,'男')
//知道构造函数之后,变量就不设置值了,我们在构造函数改变我们的变量值
console.log(p)
构造函数会在我们调用类创建对象的时候执行,并且通过构造函数,我们可以很方便的创建很多同类对象。
封装
面向对象的三大特点,封装,继承和多态。
我们平时所用的方法和类都是一种封装,当我们在项目开发中,遇到一段功能的代码在好多地方重复使用的时候,我们可以 把他单独封装成一个功能的方法,这样在我们需要使用的地方直接调用就可以了。
但是,对象作为一种复合数据结构,负责数据存储与安全,但是我们可以直接添加或者修改属性与值到对象,这对数据安全有一定隐患,那么如何确保数据安全。我们引入私有化形式。
class Person{
#name
#age
#gender
#address = 'zj'
/*
用#来确定私有属性,私有属性只能在某个类的内部访问
所以保证数据的安全性,把name,age,gender换成私有属性
但是外面访问不到这个数据,所以我们需要有个方法把数据暴露出去
提供getter 与 setter方法开放我们对数据的操作
*/
constructor(name,age,gender){
this.#name = name;
this.#age = age;
this.#gender = gender;
}
sayHello(){
console.log('hello')
}
getName(){
return this.#name
}
setName(name){
this.#name=name;
}
getAge(){
return this.#age
}
setAge(age){
if(age>=0)
this.#age=age
}
//默认情况下,我们的数据不是安全的,我们可以将数据类型转换成-20
get gender(){return this.#gender}
set gender(gender){this.#gender=gender}
/*
现在有个问题,我们设置了这么多有什么意义,不还是能更改数据
但getter setter的好处
可以控制代码的读写权限
可以在方法中对属性值进行验证
*/
}
const qi_bai = new Person('qi_bai',19,'男');
//新的写法,用这个方法写可以在调用的时候采用p1.gender就可以获取gender属性的值
总结,封装保证了数据的安全,属性私有化,并且可以通过getter 和 setter方法来操作属性
继承
class Dog{
constructor(name){
this.name = name;
}
sayHello(){
console.log('旺旺')
}
}
class Cat{
constructor(name){
this.name = name
}
sayHello(){
console.log('喵喵')
}
}
const cat = new Cat()
const dog = new Dog()
如果我们像写很多关于动物的类,就无可避免去创建很多重复的代码,为了解决这个问题,我们提出继承这个概念。当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类当中,通过继承可以减少重复的代码,在不修改一个类的前提对其进行扩展 ,使用 extends
关键字,使用类继承创建的类继承了另一个类的所有方法:
class Animal{
constructor(name){
this.name = name
}
sayHello(){
console.log('animal')
}
}
class Dog extends Animal{
}
class Cat extends Animal{
}
const dog = new Dog('dog1')
const cat = new Cat('cat1')
console.log(dog)
console.log(cat)
dog.sayHello();
cat.sayHello();
我们可以看到 Cat 与 Dog类继承了Animal类,但是我们需要将Cat的方法更换成关于Cat的,我们可以重新在Cat类中创建同名方法 即可实现重写。
class Dog extends Animal{
sayHello(){
console.log('dog')
}
}
class Cat extends Animal{
sayHello(){
console.log('cat')
}
}
如果我们想重写constructor,构造函数的第一行代码必须为super(),用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
class Car {
constructor(brand) {
this.carname = brand;
}
present() {
return 'I have a ' + this.carname;
}
}
class Model extends Car {
constructor(brand, mod) {
super(brand);
this.model = mod;
}
show() {
return this.present() + ', it is a ' + this.model;
}
}
let myCar = new Model("Ford", "Mustang");
多态
多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。
class Person {
constructor(name){
this.name = name;
}
}
class Dog {
constructor(name){
this.name = name;
}
}
const dog = new Dog('xiaobai');
const person = new Person('qibai');
function sayHello(obj){
console.log(obj.name)
}
sayHello(person);
sayHello(dog);
//以上就是多态的体现形式
//在JS中不会检查参数的类型,只要对象中有name属性就能调用这个函数
多态可以表现在重载与重写
重载:重载是有多个同名函数,但参数列表不同,调用时根据参数的多少动态匹配函数执行,比如
重写:重写是子类中定义与父类完全相同的方法以此来覆盖。
案例
tab栏切换
tab栏切换的主要功能:
- 增删改查四大功能
- 点击某一个tab栏,此tab栏高亮并对应的内容显示
- 动态添加tab栏及内容
- 点击删除某一个tab栏,此tab栏消失并内容跟着消失,高光改变
- 双击某一tab栏,可以修改此tab栏的标题和内容
以上内容如有错误,麻烦大家在评论区指正,让我们共同进步。 加油!!!
如果对您有帮助就给一个关注支持一下吧。