目录
Class类基础知识梳理
1、什么是Class类
- 在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
- class 的本质是 function。
- 它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
2、怎么声明一个类
注意:不可重复声明
// 创建一个Person父类
class Person {
// constructor 方法是类的默认方法,创建类的实例化对象时被调用。
constructor(name, age) {
// 构造器中的this是谁?—— 类的实例对象
console.log("Person-constructor",this)
// 实例属性
this.name = name;
this.age = age;
}
}
3、类的实例化
- class 的实例化必须通过 new 关键字。
- 类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。
// 创建一个Person父类
class Person {
// constructor 方法是类的默认方法,创建类的实例化对象时被调用。
constructor(name, age) {
// 构造器中的this是谁?—— 类的实例对象
console.log("Person-constructor",this)
// 实例属性
this.name = name;
this.age = age;
}
}
let p1 = new Person();
let p2 = new Person();
console.log('p1、p2是否共享原型对象',p1._proto_ == p2._proto_)
// 用于验证constructor中this是否就是new出来的实例对象
p1.sex = 'man'
4、类中的方法
原型方法(一般方法)
:写在原型中的方法可以被所有的实例共享, 实例化的时候不会在实例内存中再复制一份,占有的内存消耗少。ES6 中,prototype 仍旧存在,虽然可以直接在类中定义方法,但是其实方法还是定义在 prototype 上的。静态方法
:在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。实例方法
:对象方法需要通过实例化对象去调用 。
// 创建一个Person父类
class Person {
// constructor 方法是类的默认方法,创建类的实例化对象时被调用。
constructor(name, age) {
// 构造器中的this是谁?—— 类的实例对象
console.log("Person-constructor",this)
// 实例属性
this.name = name;
this.age = age;
// 实例方法
this.sleep = () => {
console.log(this.name + "正在睡觉")
}
}
/*
注意:
1、如果静态方法包含this关键字,这个this指的是类,而不是实例。
类中一般方法:类的方法内部如果含有this,它默认指向类的实例。
2、静态方法不能被实例调用,只能通过类来调用。
*/
// 原型方法(一般方法)
work(a, b) {
console.log('原型方法中的this', this)
console.log('打工人,冲~~');
console.log('工作', a+b);
}
// 静态方法,
//在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
static study(a, b) {
console.log('静态方法中的this', this)
console.log('学习码代码呀~~');
console.log('study', a+b);
}
}
let p1 = new Person('张三', 18);
let p2 = new Person('李四', 19);
console.log('p1、p2是否共享原型对象',p1._proto_ == p2._proto_)
p1.work(1,1)
// 用于验证constructor中this是否就是new出来的实例对象
p1.sex = 'man'
p2.work(2,2)
p1.sleep()
// 下面这种调用会报错
// Person.sleep()
// 下面这种调用会报错,study方法是一个静态方法,可以直接在Person类上调用(Person.study()),而不是在Person类的实例上调用
// p1.study(1,2)
Person.study(1,2)
直接使用类调用对象方法,会报如下错误:
上方demo执行结果如下:
总结
:
- class的一般方法定义在类的原型上,实例会继承原型上的所有方法。
- class的静态属性和静态方法定义在类上,实例不会继承静态属性和静态方法。访问静态属性或调用静态方法,均通过类名调用。
5、类中的属性
公共属性
:定义在原型上,所有实例会继承原型上的属性静态属性
:class 本身的属性
,即直接定义在类内部的属性( Class.propname ),不需要实例化。 ES6 中规定,Class 内部只有静态方法,没有静态属性。实例属性
:定义在实例对象( this )上的属性。
// 创建一个Person父类
class Person {
// constructor 方法是类的默认方法,创建类的实例化对象时被调用。
constructor(name, age) {
// 构造器中的this是谁?—— 类的实例对象
console.log("Person-constructor",this)
// 实例属性
this.name = name;
this.age = age;
}
// 静态属性
static healthy = true
}
// 公共属性
Person.prototype.country = 'china';
let p1 = new Person('张三', 18);
let p2 = new Person('李四', 19);
console.log('Person调用静态属性', Person.healthy)
console.log('p1调用静态属性', p1.healthy)
6、类中的私有属性与私有方法
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。
class
加了私有方法/私有属性。方法是在方法/属性名之前,使用#
表示。
// 创建一个Person父类
class Person {
// constructor 方法是类的默认方法,创建类的实例化对象时被调用。
constructor(name, age) {
// 构造器中的this是谁?—— 类的实例对象
console.log("Person-constructor",this)
// 实例属性
this.name = name;
this.age = age;
// 实例方法
this.sleep = () => {
console.log(this.name + "正在睡觉")
}
}
// 定义私有变量
#count = 0
// 定义私有方法
#sum() {
console.log('我是类的私有方法')
}
/*
注意:
1、如果静态方法包含this关键字,这个this指的是类,而不是实例。
类中一般方法:类的方法内部如果含有this,它默认指向类的实例。
2、静态方法不能被实例调用,只能通过类来调用。
*/
// 原型方法(一般方法)
work(a, b) {
console.log('原型方法中的this', this)
console.log('打工人,冲~~');
console.log('工作', a+b);
console.log("调用私有变量", this.#count + 1)
console.log("调用私有方法", this.#sum())
}
}
let p1 = new Person('张三', 18);
let p2 = new Person('李四', 19);
p1.work(1,1)
//下方两行代码直接调用私有变量/私有方法不可取
// console.loh('p1.#count', p1.#count)
//console.log('p1.#sum()', p1.#sum())
若直接使用类/实例调用会报如下错
上方demo执行结果如下:
7、类的继承
7.1 extends与super
- 通过
extends
实现类的继承 - 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
- 调用父类构造函数,只能出现在子类的构造函数。
- 调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类
//创建一个Student类,继承于Person类
class Student extends Person {
constructor(){
super();
// 调用父类普通方法
console.log(super.work());
}
static staticStudy(){
// 调用父类静态方法
super.study();
}
}
console.log(Student.staticStudy())
上方demo执行结果如下:
7.2 关于constructor
类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
//创建一个Student类,继承于Person类
class Student extends Person {
speak(){
//speak方法放在了哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name}, 我年龄是${this.age}, 我读的是${this.grade}年级`);
}
}
let stu = new Student('张三', 18, '高三')
console.log('stu', stu.speak())
上方demo执行结果如下,子类中没有写constructor,代码运行并没有报错,只是由于没有在constructor中进行一些初始化操作,导致获取到的grade为undefined,而name跟age由于在父类中的构造器中已经接收,所以子类继承了父类的构造器,拿来即用,所以能打印出值
。
那如果想获取到年级我们该怎么办呢?看如下代码:
//创建一个Student类,继承于Person类
class Student extends Person {
constructor(name,age,grade){
super(name, age); //调用父类构造器
this.grade = grade
}
speak(){
//speak方法放在了哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name}, 我年龄是${this.age}, 我读的是${this.grade}年级`);
}
}
let stu = new Student('张三', 18, '高三')
console.log('stu', stu.speak())
7.3 重写从父类继承过来的方法
//创建一个Student类,继承于Person类
class Student extends Person {
constructor(name,age,grade){
super(name, age);
this.grade = grade
// 调用父类普通方法
console.log(super.work());
}
static staticStudy(){
// 调用父类静态方法
super.study();
}
speak(){
//speak方法放在了哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name}, 我年龄是${this.age}, 我读的是${this.grade}年级`);
this.work();
}
//重写从父类继承过来的方法
work(){
//work方法放在了哪里?——类的原型对象上,供实例使用
//通过Student实例调用work时,work中的this就是Student实例
console.log('要努力工作~~');
}
}
let stu = new Student('张三', 18, '高三')
console.log('stu', stu.speak())
8、类中使用赋值语句
// 创建一个Person父类class Person { // constructor 方法是类的默认方法,创建类的实例化对象时被调用。 constructor(name, age) { // 构造器中的this是谁?—— 类的实例对象 console.log("Person-constructor",this) // 实例属性 this.name = name; this.age = age; } // 赋值语句 // 类中可以直接写赋值语句,如下代码的含义是:给Person的实例对象添加一个属性,名为state,值为在职 state = '在职'}let p1 = new Person('张三', 18);
9、总结:
- 类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
- 类中所定义的方法,都放在了类的原型对象上,供实例去使用。
this指向问题
构造器中的this是谁? 答:类的实例对象
静态方法中的this是谁?答:这个this指的是类,而不是实例
一般方法中的this是谁? 答:类的一般方法内部如果含有this,它默认指向类的实例。
箭头函数中的this是谁?答:箭头函数没有自己的this,它只会从自己的作用域链的上一层继承this。不同于在普通函数里,this的值在定义时是不明确的,要等到调用时才能确定,谁调用就指向谁。在箭头函数中,this是在创建时就已经确定了,箭头函数内部的this就是外层代码块的this。
注意:箭头函数内部的this是词法作用域,由上下文确定
完整的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Class 类</title>
</head>
<body>
<script>
/*
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
class 的本质是 function。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
*/
// 创建一个Person父类
class Person {
// constructor 方法是类的默认方法,创建类的实例化对象时被调用。
constructor(name, age) {
// 构造器中的this是谁?—— 类的实例对象
console.log("Person-constructor",this)
// 实例属性
this.name = name;
this.age = age;
// 实例方法
this.sleep = () => {
console.log(this.name + "正在睡觉")
}
}
// 赋值语句
// 类中可以直接写赋值语句,如下代码的含义是:给Person的实例对象添加一个属性,名为state,值为在职
state = '在职'
// 静态属性
static healthy = true
// 定义私有变量
#count = 0
// 定义私有方法
#sum() {
console.log('我是类的私有方法')
}
/*
注意:
1、如果静态方法包含this关键字,这个this指的是类,而不是实例。
类中一般方法:类的方法内部如果含有this,它默认指向类的实例。
2、静态方法不能被实例调用,只能通过类来调用。
*/
// 原型方法(一般方法)
work(a, b) {
console.log('原型方法中的this', this)
console.log('打工人,冲~~');
console.log('工作', a+b);
console.log("调用私有变量", this.#count + 1)
console.log("调用私有方法", this.#sum())
}
// 静态方法,
//在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
static study(a, b) {
console.log('静态方法中的this', this)
console.log('学习码代码呀~~');
console.log('study', a+b);
}
}
//创建一个Student类,继承于Person类
class Student extends Person {
constructor(name,age,grade){
super(name, age);
this.grade = grade
// 调用父类普通方法
console.log(super.work());
}
static staticStudy(){
// 调用父类静态方法
super.study();
}
speak(){
//speak方法放在了哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name}, 我年龄是${this.age}, 我读的是${this.grade}年级`);
this.work();
console.log(this.running)
}
//重写从父类继承过来的方法
work(){
//work方法放在了哪里?——类的原型对象上,供实例使用
//通过Student实例调用work时,work中的this就是Student实例
console.log('要努力工作~~');
}
//自定义方法————要用赋值语句的形式+箭头函数
running = () => {
console.log('箭头函数中的this', this)
}
}
// 公共属性
Person.prototype.country = 'china';
// // 下方这种写法也能定义静态属性
Person.reading = '马克思主义';
let p1 = new Person('张三', 18);
let p2 = new Person('李四', 19);
console.log('Person调用静态属性', Person.healthy)
console.log('p1调用静态属性', p1.healthy)
console.log('p1、p2是否共享原型对象',p1._proto_ == p2._proto_)
p1.work(1,1)
// 用于验证constructor中this是否就是new出来的实例对象
p1.sex = 'man'
p2.work(2,2)
p1.sleep()
// 下面这种调用会报错
// Person.sleep()
// 下面这种调用会报错,study方法是一个静态方法,可以直接在Person类上调用(Person.study()),而不是在Person类的实例上调用
// p1.study(1,2)
Person.study(1,2)
//下方两行代码直接调用私有变量/私有方法不可取
// console.loh('p1.#count', p1.#count)
// console.log('p1.#sum()', p1.#sum())
console.log(Student.staticStudy())
let stu = new Student('张三', 18, '高三')
console.log('stu', stu.speak())
console.log('箭头函数中', stu.running())
</script>
</body>
</html>