三,面向对象
1.面向对象的概念,将具体的东西抽象化,就成了对象,然后对对象操作
2.类
类的概念
-类是对象模板,能放属性和方法,然后通过类来创建对象
-由同一个类创建的对象,称为同类对象
-a对象是由b类创建的,a对象就是b类的实例化
instanceof
-检查是一个对象是否由某个类创建
-对象名 instanceof 类名
创建类语法
-class 类名 {xxx} (类名用大驼峰)
- const 类名 = class {xxx}
用类创建对象
-new 类名()
3.类的属性
类的代码块,默认就是严格模式,
类的代码块是用来设置对象的属性的,不是什么代码都能写
实例属性 :属性名:属性值
-只能通过实例访问 对象名.属性名
静态属性:static 属性名=属性值
-只能通过类访问 类名.属性名
4.类的方法
实例方法 方法名=function(){语句...}
-只能通过实例访问 对象名.方法名()
静态方法 static 方法名(){ 语句...}
- 只能通过类访问 类名.方法名()
5. 构造函数
类中定义的特殊方法 constructor
new 类的时候就会调用构造函数
可以在构造函数中为实例属性赋值,实现动态设置
class Person {
constructor(name, age) {
this.name = name
this.age = age
//this.name 代表的是对象的属性
//第二个name 代表的是形式参数
}
}
const aa = new Person('孙悟空',18)
6.封装
对象不仅存储数据(装),还要负责数据的安全(封),让数据不能随意修改
保护数据的安全
1.私有化数据
将需要保护的数据设置私有,只能在类内部使用
先声明私有,再访问
私有的数据前面加#
2.提供setter和getter方法来开发对数据的操作
能够控制属性的读写属性(只想读,就设置一个get方法就好)
可以在方法中对属性的值进行验证(可以在set方法中,设置set传递值的的类型等)
get 属性名(){
return this.#属性名
}
set 属性名(参数){
this.#属性 = 参数
}
<script>
class Person {
//1.私有化数据
//1)声明私有化数据 #属性名
#name
#age
#gander
constructor(name, age, gander) {
//2.调用私有化数据
this.#name = name
this.#age = age
this.#gander = gander
}
//2.开发私有化数据
//1)get方法,读取属性 写get和set方式比较老
getAge(){
return this.#age
}
//2)set方法,设置属性
setAge(age){
this.#age = age
}
//3)set和get新方法
set gendr(gander){
this.#gander=gander
}
get gendr(){
return this.#gander
}
}
const aa = new Person('孙悟空',18,'男')
aa.getAge()
aa.setAge(-11)
aa.gendr='hh'
console.log(aa);
</script>
7.多态
要调用某个函数,无需指定的类型,只要对象满足某些条件即可
如果一个东西走路像鸭子,叫起来像鸭子,那么它就是鸭子,不会去纠结原本是人还是其他别的东西
作用:更加灵活
8.继承之extend
用extends关键字来完成继承, b extends A
将A 中的代码复制到 b中了,A被继承,称为父类(超类),b继承,称为子类
作用:减少重复代码,在不修改一个类的前提下能够扩展
9.三大特性
封装:安全性
继承:扩展性
多态:灵活性
10.继承之重写方法
继承:在不修改一个类的情况下,对其进行扩展
1. 继承后重写方法:创建同名方法来重写
2.继承后重写构造函数,在构造函数第一行必须写 super()
3.继承后调用父类的方法 super.父类中的方法
<script>
// 继承
class Animal {
constructor(name) {
this.name = name
}
sayHello() {
console.log('动物叫');
}
}
class Cat extends Animal {
//1.继承后重写方法:创建同名方法来重写
sayHello() {
console.log('喵喵喵');
//3.继承后调用父类的方法
super.sayHello()
}
}
class Dog extends Animal {
//2.继承后重写构造函数
/* constructor() {
super() 重写构造函数,第一行必须写super(),调用父类的构造函数
不写,那不就说父类的构造函数没有,那父类就不会实例化,就没有对象
*/
constructor(name,age) {
super(name)
this.age = age
}
}
const cat = new Cat('汤姆')
const dog = new Dog('旺财',18)
cat.sayHello()
console.log(cat);
console.log('----------');
dog.sayHello()
console.log(dog);
</script>
11.对象的结构
对象中存储数据的区域实际有2个
1.对象自身
直接通过对象添加的属性,位于对象自身中
在类中通过 x=y 的形式添加的属性,位于对象自身中
2.原型对象(prototype)
对象中的一些内容存储到其他对象里(原型对象)
对象中有属性存储原型对象,这个属性叫 __proto__
原型对象也负责存储属性
-当我们访问对象中的属性时,会优先访问对象自身的属性,
-对象自身不包含该属性时,才会去原型对象中寻找
会添加到原型对象中的情况:
-1.在类中通过xxx(){ } 方式添加的方法,位于原型中
-2.主动向原型中添加的属性或方法
12.原型对象
访问原型对象:
-对象名.__proto__
-Object.gerPrototypeOf(对象名)
原型对象中的数据:
1.对象中的数据(属性、方法等)
2.constructor(对象的构造函数)
原型对象也有原型,这样就构成了一条原型链,根据对象的复杂程度不同,原型链的长度也不同
构造函数
当没有显式定义构造函数时,JavaScript会提供一个默认的构造函数,这个默认构造函数就是整个类(对象)本身。这个默认构造函数不执行任何操作,但它确实与类相关联,无论是否进行了实例化。
原型链
读取对象属性时,会优先找对象自身属性
如对象中有,就使用,没有去对象的原型中找
如原型中有,就使用,没有就去原型的原型中找
直到找到Object对象的原型(Object的原型没有原型,为null )
如果依旧没有找到,返回undefined
原型链和作用域链
原型链:找属性的链,找不到返回undefined
作用域链:找变量的链,找不到报错
13.原型的作用
原型相当于是一个公共的区域,可以被所有该类实例访问
可以将该类实例中,所有的公共属性(方法)统一存储到原型中
这样我们只需要创建一个属性,就能被所有实例访问
所有同类的实例对象他们的原型对象是同一个,这意味着同类型对象的原型链是一样的
总结:原型就是一个类的公共区域,一个地球不可能所有人都有一个厕所,所以弄2个厕所,一个男,一个女就行
js继承就是通过原型来实现的, 当继承时,子类的原型就是一个父类的实例
14.原型的修改
大部分情况下不需要修改原型对象
修改方式
- 实例名.__proto__ 不推荐,这算是通过改变一个人的基因去改变全部人的基因,不合理
- 类名.prototype 推荐,通过类去改,从根源上改变基因
修改原则
尽量不要改
不用通过实例对象改
用 类. prototype 属性修改
不要直接赋值,应是添加属性
15.instanceof 和 in 和hasOwn
instanceof检查对象上的原型链上是否有该实例,有就返回true
(Object是所有对象的实例,所以)
in 检查属性时,无论属性在对象自身还是在原型,都会返回true
hasOwn 检查一个对象的自身是否含有某个属性(区分自身还是原型)
Object.hasOwn(对象,’属性名‘)
hasOwnProperty不推荐用
16.new
17.总结
面向对象本质:编写代码时所有的操作都是通过对象来进行的
面向对象编程的步骤:
1.找对象
2.搞对象(操作对象)
学习对象:
1.明确这个对象代表什么,有什么用?
2.如何获取到这个对象
3.如何使用这个对象(对象中的属性和方法)
对象的分类
内建对象
-由ES标准所定义的对象
-如Object Function String
宿主对象
-由浏览器提供的对象
-BOM DOM
自定义镀锡
-由开发人员自己创建的对象