目录
一、面向对象简介
面向对象编程(OOP)
1. 程序是干嘛的?
- 程序就是对现实世界的抽象
2. 对象是干嘛的?
- 一个事物抽象到程序中后就变成了对象
- 在程序的世界中,一切皆对象
3. 面向对象的编程
- 面向对象的编程指程序中的所有操作都是通过对象来完成的
- 做任何事之前都需要先找到它的对象
- 一个事物通常由两部分组成:数据和功能
- 一个对象由两部分组成:属性和方法
- 事物的数据到了对象中,体现为属性
- 事物的功能到了对象中,体现为方法
<script>
const five = {
//添加属性
name:"王老五",
age:48,
height:180,
weight:100,
//添加方法
sleep(){
console.log(this.name + "睡觉了~~");
},//注意对象的每个属性或方法应该应英文逗号隔开
eat(){
console.log(this.name + "吃饭了~~");
}
}
console.log(five.name)
five.sleep()
</script>
二、类的简介
使用 Object 创建对象的问题:
1. 无法区分出不同类型的对象
2. 不方便批量创建对象
在JS中可以通过类(class)来解决这个问题:
1. 类是创建对象的模板,可以将对象中的属性和方法直接定义在类中
定义后,就可以直接通过类来创建对象
2. 通过同一个类创建的对象,我们称为同类对象
可以使用 instanceof 来检查一个对象是否由某个类创建
如果某个对象是由某个类所创建,则我们称该对象是这个类的实例
语法:对象 instanceof 类名 若是则返回true,否则返回false
语法:
class 类名{ } //类名要使用大驼峰命名
const 类名 = class { }
通过类创建对象: new 类( ) //调用构造函数创建对象
<script>
//Preson类专门用来创建人的对象
class Person{
}
//Dog专门用来创建狗的对象
class Dog{
}
const p1 = new Person()//调用构造函数创建对象
const d1 = new Dog()
console.log(p1)
console.log(d1);
console.log(p1 instanceof Person)//true
</script>
三、属性(实例属性和静态属性)
类是创建对象的模板,要创建第一件事就是定义类
类的代码块,默认就是严格模式,类的代码块是用来设置对象的属性的,不是什么代码都能写
实例属性:只能通过实例访问
静态属性(类属性):只能通过类访问,使用 static 声明的属性是类属性。
<script>
class Person{
name = "孙悟空"//Person的实例属性name
age = 18 //实例属性只能通过实例访问
static test = 'test静态属性'//使用static声明的属性,是静态属性(类属性)
//静态属性只能通过类去访问Person.test
}
const p1 = new Person()//调用构造函数创建对象
console.log(p1)
</script>
四、方法(实例方法和静态方法)
实例方法,实例方法中的this 就是当前实例
方法名 (){ }
静态方法(类方法),通过类来调用,静态方法中this 指向的是当前类
static 方法名(){ }
还可以使用 方法名 = function(){ } 添加方法,但一般不用。
<script>
class Person{
name = "孙悟空"//Person的实例属性name
// sayHello = function(){
// }//添加方法的一种方式
sayHello(){
console.log('大家好,我是'+this.name)
}//添加方法(实例方法) 实例方法中this 就是当前实例
static test(){
console.log("我是静态方法")
}//静态方法(类方法)通过类来调用,静态方法中this 指向的是当前类
}
const p1 = new Person()//调用构造函数创建对象
console.log(p1)
Person.test()//调用静态方法
</script>
五、构造函数(constructor)
当我们在类中直接指定实例属性的值时,意味着我们所创建的对象的属性都是这个值
构造函数:
在类中可以添加一个特殊的方法constructor(){ },该方法我们称为构造方法(构造函数)
构造函数会在我们调用类创建对象时执行
可以在构造函数中,为实例属性进行赋值
在构造函数中,this 表示当前所创建的对象
<script>
class Person{
constructor(name,age,gender){
//可以在构造函数中,为实例属性进行赋值
//在构造函数中,this表示当前所创建的对象
this.name = name//左边是对象的属性,右边是传入的参数
this.age = age
this.gender = gender
console.log("构造函数执行了");
}
}
const p1 = new Person("孙悟空",18,"男")
console.log(p1)
</script>
六、封装
面向对象的特点:封装、继承和多态。
封装
- 对象就是一个用来存储不同属性的容器
- 对象不仅负责属性,还要负责数据的安全
- 直接添加到对象中的属性,并不安全,因为他们可以被任意的修改
- 如何确保数据的安全:
1. 私有化数据(实例属性使用 # 开头就变成了私有属性,私有属性只能在类内部访问)
- 将需要保护的数据设置为私有,只能在类内部使用,不能直接
2.提供 setter 和 getter 方法来开放对数据的操作
- getter 方法用来读取属性, setter 方法用来设置属性
属性设置私有,通过getter , setter 方法操作属性带来的好处:
1. 可以控制属性的读写权限(通过是否设置setter, getter方法控制)
2. 可以在方法中对属性的值进行验证
<script>
class Person{
#address = "花果山"//实例使用# 开头就变成了私有属性,私有属性只能在类内部访问
#name
#age
constructor(name,age,gender){
this.#name = name//在构造函数中的私有属性要先在构造函数外部定义之后才能使用
this.#age = age
this.gender = gender
}
sayHello(){
console.log(this.#name);
}
//getter方法,用来读取属性
getName(){
return this.#name
}
//setter方法,用来设置属性
setName(name){
this.#name = name
}
getAge(){
return this.#age
}
setAge(age){
//在方法中对属性的值进行验证
if(age>=0){
this.#age = age
}
}
}
const p1 = new Person("孙悟空",18,"男")
console.log(p1);
// p1.#address//报错,私有属性无法访问
console.log(p1.getName());
p1.setName('猪八戒')
console.log(p1);
</script>
封装主要用来保证数据的安全
实现封装的方式:
1. 属性私有化,加#
2. 通过getter 和 setter 方法来操作属性
get 属性名(){
return this.#属性
}
set 属性名(参数){
this.#属性 = 参数
}
调用setter方法的话,为实例.属性名
调用getter方法的话,为实例.属性名 = 参数,即与未私有化之前使用或修改属性一样。
<script>
class Person{
#address = "花果山"//实例使用# 开头就变成了私有属性,私有属性只能在类内部访问
#name
#age
constructor(name,age,gender){
this.#name = name
this.#age = age
this.gender = gender
}
//getter方法,用来读取属性
get name(){
console.log("getter执行了~")
return this.#name
}
set name(name){
this.#name = name
}
}
const p1 = new Person("孙悟空",18,"男")
console.log(p1.name);//调用get方法
p1.name = '猪八戒' //调用set方法为其重新赋值
console.log(p1);
</script>
七、多态
多态
- 在JS中不会检查参数的类型,所以这就意味着任何数据都可以作为参数传递
- 要调用某个函数,无需指定类型,只要对象满足某些条件即可
- 程序中如何判断是否是鸭子:如果一个东西走路像鸭子,叫起来像鸭子,那么他就是鸭子
- 多态为我们提供了灵活性
<script>
class Person{
constructor(name){
this.name = name
}
}
class Dog{
constructor(name){
this.name = name
}
}
const dog = new Dog("旺财")
const person = new Person("孙悟空")
console.log(dog)
console.log(person)
//定义一个函数,这个函数将接收一个对象作为参数,他可以输出hello并打印对象的name属性
function sayHello(obj){
// if(obj instanceof Person){
console.log("hello",obj.name)
// }
}
sayHello(dog)//调用sayHello,只需满足含有name属性即可,既可以传人的类,也可以传狗的类
</script>
八、继承
可以通过 extends 关键字来完成继承
当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)
继承发生时,被继承的类称为父类(超类),继承的类称为子类。
通过继承可以减少重复的代码,并且可以在不修改一个类的前提对其进行扩展。
封装——确保安全性
继承——扩展性
多态——灵活性
<script>
class Animal{
constructor(name){
this.name = name
}
sayHello(){
console.log("动物在叫");
}
}
class Dog extends Animal{
}
class Cat{
constructor(name){
this.name = name
}
sayHello(){
console.log("喵喵喵");
}
}
const dog = new Dog("旺财")
const cat = new Cat("汤姆")
console.log(dog)
dog.sayHello()
cat.sayHello()
</script>
在子类中,可以通过创建同名方法来重写父类的方法
重写构造函数时,构造函数的第一行代码必须为super(), 表调用父类的构造函数
在方法中可以使用super来引用父类的方法,如super.sayHello()
构造函数用来创建对象,当重写构造函数时,就相当于父类的构造函数没了,这样就没有办法创建父类的实例
通过继承可以在不修改一个类的情况下对其进行扩展
OCP 开闭原则
- 程序应该对修改关闭,对扩展开放
<script>
class Animal{
constructor(name){
this.name = name
}
sayHello(){
console.log("动物在叫");
}
}
class Dog extends Animal{
//重写构造函数
constructor(name){
//重写构造函数时,构造函数的第一行代码必须为super()
super(name)//调用父类的构造函数
}
sayHello(){
//调用一下父类的sayHello
super.sayHello()//在方法中可以使用super来引用父类的方法
console.log("汪汪汪")
}
}
class Cat extends Animal{
sayHello(){
console.log("喵喵喵");
}
}
const dog = new Dog("旺财")
const cat = new Cat("汤姆")
dog.sayHello()
cat.sayHello()
console.log(dog.name);
</script>