JavaScript ES6语法学习笔记 04、Promise与Class(下)
二、Class类
2.1、Class的基本使用
2.1.1、构造函数
在ES6
之前,我们是通过new
构造函数来创建的指定对象,为了更好的区分类与函数,ES6
推出了类相关的关键字如class
。
重要几点内容:
- class表示声明一个类,其底层实际上就是一个函数。
- 定义了一个class后,默认会自带一个无参构造器,若是自定义构造函数(使用
constructor
),就会覆盖掉默认的无参构造函数,之后使用new都会走该构造函数。 - 在构造函数中使用
this.属性
是为对应生成的对象添加的实例属性,而不是该类自带的属性。
构造函数示例:
<script>
class Person {
//默认自带了一个无参的构造函数
//下面是自定义的构造函数
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
}
//查看一下Person类状态
console.log(Person);
//Person的本质:就是一个函数
console.log(typeof Person);;
//构造函数
console.log(new Person());;
console.log(new Person('changlu', '男'));;
</script>
2.1.2、函数定义(两种情况)
构造函数中使用this.函数形式(不推荐,每次创建实例时对应的speak函数都会指向不同的内存地址,造成内存浪费)
<script>
class Person {
constructor() {
this.speak = () => { console.log("hello"); }
}
}
//创建的多个实例都是引用的一个函数
console.log(new Person().speak === new Person().speak);
console.log(Person);
</script>
类中义定义函数(推荐,本质在对象原型链中添加函数)
<script>
class Person {
//类中创建函数
speak() {
console.log("Person类的speak()方法.....");
}
}
//创建的多个实例都是引用的一个函数
console.log(new Person().speak === new Person().speak);
console.log(Person);
</script>
speak()
函数方法被添加到了Person
的原型对象中去了。这实际上与之前执行Person.prototype.speak = ()=>{};
效果一致。
2.2.2、Class的两种定义形式(声明与表达形式)
声明形式
<script>
//声明形式
class Person {
constructor() { }
}
console.log(new Person());
</script>
表达式形式
实际与快速执行函数基本类似,如下:能够快速定义类并且创建类实例
<script>
//表达式形式
console.log(
//new (class xxx{})()
new (class Person {
constructor() { }
})()
);
</script>
2.2、class中的实例属性
认识class类中的实例属性
什么是实例属性呢?
- 之前我们在构造器中使用
this.属性
进行赋值实际上就是给生成的对象创建并赋值属性,这里的实例属性也是指的这个效果。
<script>
class Person {
//实例属性:不能使用任何关键字来进行定义如let、var、const等等
//本质就是会执行this.name='xxx';this.age=0;
name = 'xxx';
age = 0;
}
//查看下这个Person类:在该类中并没有实例属性!
console.log(Person);
//创建好实例后就会得到对应的实例属性
console.log(new Person().name)
console.log(new Person().age)
</script>
结论:直接在类中创建的属性就是实例属性!
构造器中如何赋值实例属性
<script>
class Person {
//实例属性 本质就是会执行this.name='xxx';this.age=0;
name = 'xxx';
age = 0;
constructor(name, age, sex) {
//根据参数传递情况来是否对实例属性进行赋值
if (sex !== undefined) {
this.name = name;
this.age = age;
this.sex = sex;
} else if (age !== undefined) {
this.name = name;
this.age = age;
}
}
}
console.log(new Person());
console.log(new Person('changlu', 18, '男'));
</script>
2.3、静态方法与静态属性
2.3.1、静态方法与静态属性的基本定义
静态方法
定义位置与方式:直接在类中定义static
开头的函数名即可。
本质:就是在下面Perosn
构造函数上添加了一个方法,不会出现在原型链中!
- 相当于之前执行
Person.xxx = ()=>{}
,直接添加在构造函数上!
示例:
<script>
class Person {
//静态方法:添加到Person构造函数上去的
static speak() {
console.log("i am Person!")
}
}
console.log(Person);
//实例化对象的原型链中没有对应静态方法
console.log(new Person());
new Person().speak();//报异常,没有对应定义
</script>
静态属性
定义的方式很简单:直接在类中对属性添加static
即可。
说明:静态属性只有在类中有,实例中没有!
class Person {
static num = 1;
static str = "hello";
}
console.log(Person);
console.log(Person.num);
console.log(Person.str);
2.3.2、this对象介绍(静态方法、构造函数与普通函数)
静态方法中的this
结论:静态方法中的this指向的是类。
<script>
class Person {
static num = 1;
//静态方法:添加到Person构造函数上去的
static speak() {
console.log("i am Person!")
console.log('静态方法中的this对象为:', this);
//能够访问到Person类中的值
console.log(this.num);
}
}
//调用静态方法
Person.speak();
</script>
普通方法与构造函数中的this
结论:普通方法与构造函数中的this都指向了对应创建与调用的对象实例!
<script>
class Person {
constructor() {
console.log(`constructor中的this为:${this}`)
}
speak() {
console.log(`普通函数中的this为:${this}`)
}
}
//结论:原型对象链中的函数以及构造函数中的this指向了对应创建与调用的实例
new Person().speak();//执行了构造函数以及调用了函数
</script>
2.4、私有属性与方法
问题说明:对于类中你定义静态方法或者实例方法、属性,在外部浏览器都很容易受到篡改等危险操作,那么我们就需要来定义私有属性与方法来进行防范!
有两种方式来进行定义私有属性:
- 模拟私有属性与方法:通过在变量名开头添加_来俗成约定表示私有属性或方法。(此方法仅仅只是约定,并没有进行了约束!)
- 将私有属性与方法移除类:需要搭配立即调用函数表达式
()()
来进行移出私有属性或方法!
方式一:模拟私有属性与方法,该方式还是不太可靠的
<script>
class Person {
constructor(name, age) {
this._name = name;
this._age = age;
}
//通过普通函数来获得
getName() {
return this._name;
}
getAge() {
return this._age;
}
}
//创建一个实例
const p = new Person('changlu', 18)
console.log(p);
console.log(p.getName());
console.log(p.getAge());
</script>
方式二:将属性移出类,配合立即调用函数表达式
<script>
(function () {
//将指定的属性移到外面去
let name = '';
let pw = '';
class Person {
constructor(username, password) {
//定义的私有属性必须与传入的形参名称不同,否则无法识别
name = username;
pw = password;
}
//获取属性并不是从自己本身对象中获取的,而是通过该作用域来得到!
getUsername() {
return name;
}
getPassword() {
return pw;
}
}
//绑定到window上
window.Person = Person;
})();
//通过()()形式,创建多个实例也是可以的,会形成多个局部作用域
//创建第一个实例,只能够通过调用方法才能够获取到对应的值
console.log("第一个实例:");
const p = new Person("changlu", '123456')
console.log(p);
console.log(p.getUsername());
console.log(p.getPassword());
//想要尝试直接获取是不允许的!!!
console.log(p.name);
console.log(p.pw);
//创建第二个实例,只能够通过调用方法才能够获取到对应的值
console.log("第二个实例:");
const p1 = new Person("liner", '987654')
console.log(p1);
console.log(p1.getUsername());
console.log(p1.getPassword());
//想要尝试直接获取是不允许的!!!
console.log(p1.name);
console.log(p1.pw);
</script>
2.5、extends继承
2.5.1、初识extends继承(三个结论)
结论:
- 子类的类原型对象指向着父类的类原型对象,意味通过子类的类就能够调用执行父类的静态方法!
- 子类的实例原型对象指向着父类的实例原型对象,意味着子类的实例能够调用执行父类实例的普通方法!
- 子类能够具有父类的实例属性。
示例:
<script>
class Person {
//实例变量
num = 1;
//静态变量
st_num = 2;
//静态方法
static st_speak() {
console.log("st_speak()");
}
//实例方法
speak() {
console.log("speak()");
}
}
//自定义类继承Person类
class Student extends Person {
}
//查看Studnent类的结构
console.log(Student);
const p = new Student();
//查看Student进行new的实例
console.log(p);
//调用Student实例的speak()方法
p.speak();
</script>
2.5.2、继承后子类调用父类普通方法或构造函数中的的this
结论:
new 子类
时,会首先去调用父类的构造函数,紧接着调用子类的构造函数。其中的this
在父类构造器中指向的就是父类实例对象,在子类构造中就是指向子类实例对象。- 子类调用父类中的普通方法时,该方法其中的
this
指向子类对象实例。
结论一证明
<script>
class Person {
name = 'Person';
constructor() {
console.log(this, this.name);
}
}
//自定义类继承Person类
class Student extends Person {
name = 'Student';
constructor() {
super();//若是不写会报错(原因是父类的无参构造是自定义的),此时会多执行一次父类的默认的构造函数
console.log(this, this.name);
}
}
const p = new Student();
//查看Student进行new的实例
console.log(p);
</script>
结论二证明
<script>
class Person {
name = "Person";
info() {
//普通方法函数中,子类执行调用的函数this就是指向子类实例
console.log(this, this.name);
}
}
//自定义类继承Person类
class Student extends Person {
name = 'Student';
}
new Student().info();
</script>
2.5.3、重写(覆盖)父类的属性与方法
仅仅只需要在子类中声明定义父类的属性与方法即可,这里就不进行演示了!
2.6、super用途(对象、函数)
2.6.1、作为函数调用
super()
:作为函数时,只能够使用于子类的构造函数中,用在其他地方就会报错!
重点:super虽然代表了父类的构造方法,但是内部的this
指向子类的实例(暂时还是持怀疑的!!!)。
示例:
<script>
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
console.log(this);
}
}
//自定义类继承Person类
class Student extends Person {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
}
console.log(new Student('changlu', 18, '男'));
</script>
2.6.2、作为对象使用
super.属性
:在构造方法中或一般方法中使用,super
代表父类的原型对象,通过super
调用父类的方法时,方法内部的this
依旧指向当前的子类实例。
注意:定义在父类实例上的方法与属性是无法通过super
调用得到的,但是方法是可以执行的!
<script>
class Person {
name = "Person";
getName() {
console.log(this);//子类调用时指向子类实例
return this.name;
}
}
//自定义类继承Person类
class Student extends Person {
name = "Student";
//证明父类中的方法里使用的this是子类的对象
getName() {
return super.getName();
}
}
console.log(new Student().getName());
</script>
注意事项
1、在子类构造器中使用super
前需要先执行super()
。
2、在使用super
的时候,必须显示是作为函数super()
,还是作为对象使用,否则会报错(如仅仅是是输出super
)!
整理者:长路 时间:2021.6.23
Student’;
}
new Student().info();
</script> ```
[外链图片转存中…(img-mFVe8OFz-1651988840423)]
2.5.3、重写(覆盖)父类的属性与方法
仅仅只需要在子类中声明定义父类的属性与方法即可,这里就不进行演示了!
2.6、super用途(对象、函数)
2.6.1、作为函数调用
super()
:作为函数时,只能够使用于子类的构造函数中,用在其他地方就会报错!
重点:super虽然代表了父类的构造方法,但是内部的this
指向子类的实例(暂时还是持怀疑的!!!)。
示例:
<script>
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
console.log(this);
}
}
//自定义类继承Person类
class Student extends Person {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
}
console.log(new Student('changlu', 18, '男'));
</script>
[外链图片转存中…(img-X9zPtbiw-1651988840423)]
2.6.2、作为对象使用
super.属性
:在构造方法中或一般方法中使用,super
代表父类的原型对象,通过super
调用父类的方法时,方法内部的this
依旧指向当前的子类实例。
注意:定义在父类实例上的方法与属性是无法通过super
调用得到的,但是方法是可以执行的!
<script>
class Person {
name = "Person";
getName() {
console.log(this);//子类调用时指向子类实例
return this.name;
}
}
//自定义类继承Person类
class Student extends Person {
name = "Student";
//证明父类中的方法里使用的this是子类的对象
getName() {
return super.getName();
}
}
console.log(new Student().getName());
</script>
[外链图片转存中…(img-evCEq4md-1651988840423)]
注意事项
1、在子类构造器中使用super
前需要先执行super()
。
2、在使用super
的时候,必须显示是作为函数super()
,还是作为对象使用,否则会报错(如仅仅是是输出super
)!
- 点赞
- 收藏
- 关注作者
评论(0)