class
class 关键字是ES6中用于声明一个类的关键字, 类的声明格式如下:
// 声明一个类, 名为 Person
class Person {
}
类可以理解为是ES5构造函数的一个语法糖, 上面的类等价于如下ES5构造函数:
function Person() {
}
类创建实例同ES5一样使用new
关键字
class Person {
}
const p1 = new Person();
console.log(p1); // Person {}
// 判断实例
console.log(p1 instanceof Person); // true
// 原型对象
console.log(p1.__proto__ === Person.prototype); // true
// 构造函数
console.log(p1.__proto__.constructor === Person); // true
实例化传递实例属性
在类中需要定义一个构造函数constructor
(固定名称), 每次对该类使用new
关键字生成实例时该函数都会自动调用, 它就可以接受外部传递的参数
class Person {
constructor(name, age) {
// this 就是当前的实例
this.name = name;
this.age = age;
}
a = 1; // 所有的 Person 实例都会在自身赋值 a = 1
}
const p1 = new Person("张三", 18);
const p2 = new Person("李四", 20);
console.log(p1); // Person {a: 1, name: '张三', age: 18}
console.log(p2.name); // 李四
console.log(p2.a); // 1
类中定义方法
在类中定义的方法, 跟构造函数类似, 定义一个函数即可, 注意每个函数(方法)不要使用,
号分割
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
} // 注意: 这里不要加逗号
sayHi() {
const { name, age } = this;
console.log(`你好, 我叫${name}, 年龄${age}岁`);
}
}
const p1 = new Person("张三", 18);
p1.sayHi(); // 你好, 我叫张三, 年龄18岁
const p2 = new Person("李四", 20);
p2.sayHi(); // 你好, 我叫李四, 年龄20岁
在类中定义的方法, 其实都会定义到该类的原型对象中, class
其实就是ES5构造函数的一个语法糖
// 代码同上...
console.log(p1.__proto__.sayHi === Person.prototype.sayHi); // true
Person.prototype.sayHi = function () {
console.log("通过类的原型对象修改后的 sayHi 方法");
}
p1.sayHi(); // 通过类的原型对象修改后的 sayHi 方法
p2.sayHi(); // 通过类的原型对象修改后的 sayHi 方法
在类的方法中默认开启了严格模式
class Person {
constructor() {
Person.test()
}
static test() {
"user strict" // 相当于默认添加这个一行代码
var a = 1;
delete a; // SyntaxError: Delete of an unqualified identifier in strict mode.
b = 2; // ReferenceError: b is not defined
console.log(window.b);
}
}
new Person();
getter 和 setter
class
中也是可以使用存取器get
和set
的
class Person {
constructor() {
}
// 这个属性(value)被`读取时`触发该函数, 其返回值是什么外面读取到的就是什么
get value() {
return "getter value";
}
// 这个属性(value)被`修改时`时触发该函数, 接受一个参数该参数就是修改的值
set value(newVal) {
console.log("setter value", newVal);
}
}
const p1 = new Person();
console.log(p1.value); // getter value
p1.value = "hello"; // setter value hello
私有属性
在类中可以定义私有的实例属性, 该属性只能在该类中才可以访问, 外界无法访问
class Person {
// 声明私有变量
#age;
constructor(name) {
this.name = name;
this.#age = 18;
}
getAge() {
// 在类中可以访问
return this.#age;
}
}
const p1 = new Person("张三", 18);
// console.log(p1.#age); // SyntaxError: Private field '#age' must be declared in an enclosing class
console.log(p1.getAge()); // 18
私有属性可以和getter/setter
或者提供读取(getXxx
)和设置(setXxx
)方法一起使用, 以getter/setter
为例:
class Person {
#age;
constructor(name) {
this.name = name;
this.#age = 18;
}
get age() {
return this.#age;
}
set age(newVal) {
if (typeof newVal !== "number") {
throw "年龄只能是数字";
} else if (newVal < 0) {
throw "年龄不能是负数";
}
this.#age = newVal;
}
}
const p1 = new Person("张三");
// console.log(p1.#age); // SyntaxError: Private field '#age' must be declared in an enclosing class
console.log(p1.age); // 18
p1.age = 28;
console.log(p1.age); // 28
静态属性
类中静态属性通过static
关键字设置, 被该关键字修饰的属性或者方法只能使用类名去访问
class Person {
static name = "Person";
constructor(name) {
this.name = name;
}
}
const p1 = new Person("张三");
console.log(p1.name); // 张三
console.log(Person.name); // Person
继承
继承使用extends
关键字, 继承可以让一个类(B)获得另一个类(A)定义的属性或方法, 这个A类可以叫基类或者父类, B类可以叫子类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
const { name, age } = this;
console.log(`你好, 我叫${name}, 年龄${age}岁`);
}
}
// Student 继承 Person 不写构造函数时, 默认传递接受的所有参数给父类
class Student extends Person { }
const s1 = new Student("张三", 18);
console.log(s1); // Student {name: '张三', age: 18}
s1.sayHi(); // 你好, 我叫张三, 年龄18岁
console.log(s1 instanceof Student); // true
console.log(s1 instanceof Person); // true
super
在子类中可以使用super
关键字来访问和调用父类的构造函数
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
const { name, age } = this;
console.log(`你好, 我叫${name}, 年龄${age}岁`);
}
}
// Student 继承 Person 不写构造函数时, 默认传递接受的所有参数给父类
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
// 注意: 子类中使用了 super 后, this 只能在 super 之后才可以访问
this.grade = grade;
}
}
const s1 = new Student("张三", 18, "六年级");
console.log(s1); // Student {name: '张三', age: 18, grade: '六年级'}
注意: super
只能在子类的构造函数和静态方法中使用
class Parent {
constructor() {
console.log('Parent constructor');
}
static parentStaticFn() {
console.log('Parent parentStaticFn');
}
}
class child extends Parent {
constructor() {
super() // 可以在子类的构造函数里是可以调用 super 的
child.childStaticFn();
}
static childStaticFn() {
super.parentStaticFn(); // 也可以在子类的静态方法里是可以访问 super 的
}
}
new child();
重写方法
子类从父类中继承的方法是可以重写
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
const { name, age } = this;
console.log(`你好, 我叫${name}, 年龄${age}岁`);
}
}
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
this.grade = grade;
}
// 这里的名字同父类的方法同名即可
sayHi() {
console.log("我是子类中重写的 sayHi 方法");
}
}
const s1 = new Student("张三", 18, "六年级");
s1.sayHi(); // 我是子类中重写的 sayHi 方法
// 没有重写的子类还是会使用父类的方法
class Teacher extends Person { }
new Teacher("张麻子", 35).sayHi(); // 你好, 我叫张麻子, 年龄25岁