一、什么是面向对象
面向对象 (Object Oriented)是一种以 对象 为中心编程思想,简称 OO。面向对象将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。面向对象主要是把事物给对象化,对象包括属性与行为。
类 (Class)和 对象 (Object)是面向对象的核心概念。类是对一类事物的描述,是抽象的、概念上的定义;对象是实际存在的该类事物的每个个体,因此也称为实例(instance);
二、面向对象的特征
2.1、封装
对象就是一个用来存储不同属性的容器,对象不仅要存储属性,还要负责数据的安全。直接添加到对象的属性并不安全,因为它们可以被任意的修改。
如何确保数据的安全:
-
私有化属性
- 实例属性使用 # 开头就变成了私有属性;
- 私有属性只能在类内部访问;
- 私有属性一定要先声明在访问,否则报错;
-
提供 setter 和 getter 方法来开放对数据的操作
- 使用 setter 和 getter 方法,可以控制属性的读写权限;
- 使用 setter 和 getter 方法,可以在方法中对属性的值进行验证;
get 属性名(){ return this.#属性; } set 属性名(参数){ this.#属性 = 参数; }
class Person{
#name;
#age;
#gender;
constructor(name,age,gender){
this.#name = name;
this.#age = age;
this.#gender = gender;
}
sleep(){
console.log(this.name + '睡觉了');
}
// getter方法,用来读取属性
get name(){
console.log('getter执行了');
return this.#name;
}
// setter方法,用来设置属性
set name(name){
console.log('setter执行了');
this.#name = name;
}
// getter方法,用来读取属性
getAge(){
return this.#age;
}
// setter方法,用来设置属性
setAge(age){
if(!isNaN(age)){
if(age >= 0){
this.#age = age;
}
}
}
getGender(){
return this.#gender;
}
setGender(){
this.#gender = gender;
}
}
let p1 = new Person('Sakura',10,'女');
console.log(p1);
p1.setAge('abc');
console.log(p1.getAge());
p1.setAge(-11)
console.log(p1.getAge());
p1.setAge(12);
console.log(p1.getAge());
console.log(p1.name);
p1.name = 'Mikoto';
console.log(p1.name);
封装主要保证数据的安全,实现封装的方式:1.属性私有化;2.通过 getter 和 setter 方法来操作属性;
2.2、继承
我们可以使用 extends 关键字来完成继承,当一个类继承另一个类是,就相当于将i另一个类中的代码复制到了当前类中(简单理解)。继承发生时,被继承的类称为 父类(超类),继承的类称为 子类。通过继承可以减少重复的代码,并且可以在不修改一个类的前提对其进行扩展。在子类中,可以通过创建同名方法来重写父类的方法。
class Animal{
constructor(name){
this.name = name;
}
sound(){
console.log('动物在叫');
}
}
class Dog extends Animal{
// 重写构造函数
constructor(name,age){
// 重写构造函数时,构造函数的第一行代码必须为super()
super(name); // 调用父类的构造方法
this.age = age;
}
// 子类重写父类的同名方法
sound(){
super.sound(); // 在方法中可以使用super来引用父类的方法
console.log('汪汪汪');
}
}
class Cat extends Animal{
sound(){
console.log('喵喵喵');
}
}
let dog = new Dog('斯派克',3);
let cat = new Cat('汤姆');
console.log(dog);
dog.sound();
console.log(cat);
cat.sound();
重写构造函数时,构造函数的第一行代码必为为 super(),表示调用父类的构造函数;
在方法中可以使用 super 来引用父类的方法;
2.3、多态
在 JS 中不会检查参数的类型,所以意味着任何数据都可以作为参数传递。要调用某个函数,无需指定的类型,只要对象满足某些条件即可。
class Person{
constructor(name){
this.name = name;
}
}
class Dog{
constructor(name){
this.name = name;
}
}
let dog = new Dog('大黄');
let person = new Person('张三');
console.log(dog);
console.log(person);
// 定义一个函数,这个函数将接收一个对象作为参数,它可以输出hello并打印对象的name属性
function hello(obj){
console.log('hello,' + obj.name);
}
hello(dog);
hello(person);
三、对象的结构
对象中存储属性的区域实际上有两个:
- 对象自身
- 比如直接通过对象所添加的属性,位于对象自身
- 在类中通过 x = y 的形式添加的属性,位于对象自身中
- 原型对象
- 在对象中会有一个属性用来存储原型对象,这个属性叫做
__proto__
- 原型对象也负责为对象存储属性
- 当访问对象的属性时,会优先访问对象自身的属性,对象自身不包含该属性时,才会去原型对象中寻找
- 会添加到原型对象的情况
- 在类中通过 xxx(){} 方式添加的方法,位于原型中
- 主动向原型中添加的属性和方法
- 在对象中会有一个属性用来存储原型对象,这个属性叫做
class Person{
name = 'Sakura';
age = 10;
constructor(){
this.gender = '女';
}
hello(){
console.log('My name is ' + this.name);
}
}
let person = new Person();
person.addredd = '友枝町';
console.log(person);
四、原型对象
4.1、访问原型对象
访问对象的原型对象:对象名.__proto__
(不推荐)或者 Object.getProtoTypeOf(对象名)
class Person{
name = 'Sakura';
age = 10;
hello(){
console.log('My name is ' + this.name);
}
}
let person = new Person();
console.log(person);
console.log(person.__proto__);
console.log(Object.getPrototypeOf(person));
console.log(person.__proto__ == Object.getPrototypeOf(person));
原型中的数据主要有两个部分:①、对象中的数据(属性、方法等);②、constructor(对象的构造函数);
原型对象也有原型,这样就构成一条原型链,根据对象的复杂程度不同,原型链的长度不同;即 person对象的原型链:person对象 --> 原型 --> 原型的原型 --> null;
读取对象属性时,会优先对象自身属性,如果对象中有,则使用,没有则去对象的原型中查找;如果原型中有,则使用,没有则去原型的原型中寻找,直到找到 Object 对象的原型,Object 对象的原型没有原型(为 null);如果依然没有找到,则返回 undefined;
4.2、原型的作用
所有的同类型对象它们的原型对象都是同一个。原型就相当于是一个公共的区域,可以被所有该类的实例访问。我们可以将所有的公共属性(方法)统一存储到原型中,这样我们只需要创建一个属性,即可被所有实例访问。在对象中有些值是对象的独有的,像属性(name,age,gender)每个对象都应该有自己的值,但是有些值对于每个对象来说是一样的,像各种方法,对于一样的值没必要重复的创建。
class Person{
name = 'Sakura';
age = 10;
hello(){
console.log('My name is ' + this.name);
}
}
let p1 = new Person();
let p2 = new Person();
console.log(p1 == p2);
console.log(Object.getPrototypeOf(p1) == Object.getPrototypeOf(p2));
console.log(p1.hello == p2.hello);
class Animal{
}
class Cat extends Animal{
}
// cat --> Animal实例 --> object --> Object原型 --> null
let cat = new Cat();
console.log(cat);
console.log(Object.getPrototypeOf(cat));
console.log(cat.__proto__.__proto__);
console.log(cat.__proto__.__proto__.__proto__);
console.log(cat.__proto__.__proto__.__proto__.__proto__);
在 JS 中。继承通过原型实现的,当我们继承是,子类的原型就是父类的实例。
4.3、修改原型
可以通过类的 prototype 属性,来访问实例的原型。
class Person{
name = 'Sakura';
age = 10;
hello(){
console.log('My name is ' + this.name);
}
}
let p1 = new Person();
let p2 = new Person();
// 通过类访问Person实例的原型对象
console.log(Person.prototype);
Person.prototype.sleep = () => {
console.log('在睡觉');
}
p1.sleep();
p2.sleep();
千万不要通过类的实例去修改原型;
4.4、instanceof
instanceof 检查一个类是否是一个类的实例。instanceof 检查的是对象的原型链上是否有该类的实例,只要原型链上有该类实例,就会返回 true。Object 是所有对象的原型,所以任何对象和 Object 进行 instanceof 运算都会返回 true。
class Animal{
}
class Dog extends Animal{
}
let dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
4.5、hasOwn
- 使用 in 运算符检查属性时,无论属性在对象自身还是原型中,都会返回 true。
- 对象.hashOwnProperty(属性名) 用来检查一个对象的自身是否含有某个属性(不推荐使用)。
- Object.hasOwn(对象,属性名) 用来检查一个对象的自身是否含有某个属性(推荐使用)。
class Person{
name = 'Sakura';
age = 10;
hello(){
console.log('My name is ' + this.name);
}
}
let person = new Person();
console.log('hello' in person);
console.log(person.hasOwnProperty('name'));
console.log(person.hasOwnProperty('hello'));
console.log(Object.hasOwn(person,'name'));
console.log(Object.hasOwn(person,'hello'));
标签:console,log,对象,name,面向对象,原型,属性,07
From: https://www.cnblogs.com/nanoha/p/17067161.html