ES6 面试题及详细答案 80题 (55-61)-- 类与继承
《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。
文章目录
- 一、本文面试题目录
- 55. ES6中的class与ES5的构造函数有何区别?
- 56. 如何定义一个类?类的构造函数(constructor)有什么作用?
- 定义类的方式
- 构造函数(constructor)的作用
- 57. 类的静态方法(static)与实例方法有何区别?
- 58. 类的继承如何实现?extends和super关键字的作用是什么?
- 类的继承实现
- `extends`关键字的作用
- `super`关键字的作用
- 59. 子类如何重写父类的方法?
- 重写规则
- 调用父类方法
- 60. 类的getter和setter方法有什么作用?如何定义?
- 作用
- 定义方式
- 61. 类的私有属性和私有方法如何声明?
- 私有属性声明
- 私有方法声明
- 特性
- 二、80道ES6 面试题目录列表
一、本文面试题目录
55. ES6中的class与ES5的构造函数有何区别?
ES6的class
是基于ES5构造函数的语法糖,但在语法、功能和行为上存在显著区别:
特性 | ES6 class | ES5 构造函数 |
---|---|---|
定义方式 | 使用class 关键字声明(class A {} ) | 使用function 声明(function A() {} ) |
原型方法定义 | 方法直接写在类体中(method() {} ),自动挂载到原型 | 需手动挂载到prototype (A.prototype.method = function() {} ) |
静态方法 | 通过static 关键字定义(static method() {} ) | 直接挂载到构造函数上(A.method = function() {} ) |
继承 | 使用extends 关键字(class B extends A {} ) | 通过Object.create() 和call 实现(B.prototype = Object.create(A.prototype); B.call(this) ) |
构造函数 | 必须在constructor 中显式调用super() (子类) | 需手动调用父类构造函数(A.call(this) ) |
提升 | 不存在函数提升(需先定义后使用) | 存在函数提升(可先调用后定义) |
私有成员 | 支持通过# 声明私有属性/方法(ES2022) | 无原生支持,需通过闭包模拟 |
语法约束 | 类体中不能有同名方法(会报错) | 允许覆盖原型方法(后定义的生效) |
示例:
// ES5 构造函数
function PersonES5(name) {this.name = name;
}
PersonES5.prototype.sayHi = function() {console.log(`Hi, ${this.name}`);
};// ES6 class
class PersonES6 {constructor(name) {this.name = name;}sayHi() {console.log(`Hi, ${this.name}`);}
}
56. 如何定义一个类?类的构造函数(constructor)有什么作用?
定义类的方式
ES6通过class
关键字定义类,类体中可包含构造函数、实例方法、静态方法等。
基本语法:
class ClassName {// 构造函数constructor(参数) {// 初始化实例属性}// 实例方法method1() {}// 静态方法static staticMethod() {}
}
示例:
class Student {// 构造函数constructor(name, age) {this.name = name; // 实例属性this.age = age;}// 实例方法introduce() {return `我是${this.name},今年${this.age}岁`;}// 静态方法static getSchool() {return "阳光小学";}
}// 使用类创建实例
const student = new Student("小明", 12);
console.log(student.introduce()); // 输出:我是小明,今年12岁
console.log(Student.getSchool()); // 输出:阳光小学
构造函数(constructor)的作用
- 初始化实例属性:在实例创建时,为实例绑定属性(如
this.name = name
)。 - 返回实例对象:默认返回
this
(即新创建的实例),也可手动返回其他对象(但通常不建议)。 - 处理实例创建逻辑:如参数验证、默认值设置等。
注意:
- 一个类只能有一个
constructor
方法,若定义多个会报错。 - 若未显式定义
constructor
,默认会生成一个空构造函数(constructor() {}
)。 - 通过
new
调用类时,会自动执行constructor
。
57. 类的静态方法(static)与实例方法有何区别?
静态方法和实例方法是类中两种不同类型的方法,核心区别如下:
特性 | 静态方法(static) | 实例方法 |
---|---|---|
定义方式 | 用static 关键字修饰 | 直接定义在类体中,无static |
调用方式 | 通过类名调用(ClassName.method() ) | 通过实例调用(instance.method() ) |
this 指向 | 指向类本身(构造函数) | 指向调用该方法的实例 |
作用 | 处理与类相关的通用逻辑(如工具方法、工厂方法) | 处理实例的具体逻辑(依赖实例属性) |
继承关系 | 可被子类继承,子类调用时this 指向子类 | 可被实例继承,子类实例可重写 |
示例:
class MathUtil {// 静态方法(工具函数)static add(a, b) {return a + b; // this指向MathUtil类}// 实例方法(依赖实例属性)constructor(base) {this.base = base;}multiply(num) {return this.base * num; // this指向实例}
}// 调用静态方法(类名调用)
console.log(MathUtil.add(2, 3)); // 输出:5// 调用实例方法(实例调用)
const util = new MathUtil(10);
console.log(util.multiply(5)); // 输出:50
应用场景:
- 静态方法:工具类(如
MathUtil
)、工厂方法(如create()
创建实例)。 - 实例方法:操作实例状态的方法(如
user.updateName()
)。
58. 类的继承如何实现?extends和super关键字的作用是什么?
类的继承实现
ES6通过extends
关键字实现类的继承,让子类继承父类的属性和方法。
基本语法:
class Parent {// 父类代码
}class Child extends Parent {// 子类代码
}
extends
关键字的作用
用于声明子类继承自父类,让子类拥有父类的所有实例方法、静态方法和原型属性。
示例:
class Animal {constructor(name) {this.name = name;}eat() {console.log(`${this.name}在吃东西`);}static getType() {return "动物";}
}// 继承Animal
class Dog extends Animal {bark() {console.log(`${this.name}在汪汪叫`);}
}const dog = new Dog("旺财");
dog.eat(); // 继承父类方法:输出“旺财在吃东西”
console.log(Dog.getType()); // 继承父类静态方法:输出“动物”
super
关键字的作用
-
在子类构造函数中:
super(参数)
用于调用父类的构造函数,必须在访问this
之前调用。class Dog extends Animal {constructor(name, age) {super(name); // 调用父类constructor(name)this.age = age; // 必须在super之后访问this} }
-
在子类方法中:
super.method()
用于调用父类的同名方法。class Dog extends Animal {eat() {super.eat(); // 调用父类的eat()console.log(`${this.name}爱吃骨头`);} } const dog = new Dog("旺财"); dog.eat(); // 输出: // 旺财在吃东西 // 旺财爱吃骨头
注意:
- 子类若显式定义
constructor
,必须先调用super()
,否则报错。 - 若子类未定义
constructor
,会默认生成包含super(...arguments)
的构造函数。
59. 子类如何重写父类的方法?
子类重写父类方法是指在子类中定义与父类同名的方法,覆盖父类的实现,实现多态特性。
重写规则
- 子类方法名与父类完全一致。
- 可通过
super.方法名()
调用父类的原方法。 - 重写后,实例调用该方法时会执行子类的实现。
示例:
class Shape {getArea() {return 0; // 父类默认实现}
}// 子类重写getArea()
class Circle extends Shape {constructor(radius) {super();this.radius = radius;}// 重写父类的getArea()getArea() {return Math.PI * this.radius **2; // 圆的面积公式}
}// 另一个子类重写getArea()
class Rectangle extends Shape {constructor(width, height) {super();this.width = width;this.height = height;}// 重写父类的getArea()getArea() {return this.width * this.height; // 矩形的面积公式}
}// 多态:同一方法在不同子类中有不同实现
const circle = new Circle(5);
console.log(circle.getArea()); // 输出:78.5398...const rectangle = new Rectangle(4, 5);
console.log(rectangle.getArea()); // 输出:20
调用父类方法
重写时若需保留父类逻辑,可通过super
调用:
class Parent {greet() {return "Hello";}
}class Child extends Parent {greet(name) {// 调用父类greet(),再添加子类逻辑return `${super.greet()}, ${name}!`;}
}const child = new Child();
console.log(child.greet("小明")); // 输出:Hello, 小明!
60. 类的getter和setter方法有什么作用?如何定义?
getter
和setter
是类中用于控制属性访问的特殊方法,分别用于获取和设置属性值。
作用
1.** 封装属性访问 :隐藏属性的实际存储方式,对外提供统一的访问接口。
2. 数据验证 :在setter
中验证输入值的合法性。
3. 计算属性 **:getter
可返回动态计算的结果(如基于其他属性的值)。
定义方式
通过get
和set
关键字定义,语法如下:
class ClassName {constructor() {// 通常用下划线开头的变量存储实际值(约定)this._property = value;}// getter:获取属性值get property() {return this._property;}// setter:设置属性值set property(value) {// 可添加验证逻辑this._property = value;}
}
示例:
class User {constructor(name) {this._name = name; // 实际存储的属性this._age = 0;}// 姓名的getter(直接返回)get name() {return this._name;}// 年龄的getter和setter(带验证)get age() {return this._age;}set age(value) {if (value < 0 || value > 150) {throw new Error("年龄必须在0-150之间");}this._age = value;}// 计算属性:获取全名(假设_name是名,这里简化)get fullName() {return `用户:${this._name}`; // 动态计算}
}const user = new User("张三");
console.log(user.name); // 调用getter:输出“张三”user.age = 25; // 调用setter
console.log(user.age); // 调用getter:输出25// user.age = 200; // 报错:年龄必须在0-150之间console.log(user.fullName); // 调用计算属性的getter:输出“用户:张三”
注意:
getter
不能有参数,setter
只能有一个参数。- 访问属性时直接用
user.age
(而非user.age()
),语法上像访问普通属性。
61. 类的私有属性和私有方法如何声明?
ES2022(ES13)正式引入了类的私有成员(属性和方法),通过井号(#) 前缀声明,只能在类内部访问。
私有属性声明
在属性名前加#
,只能在类的构造函数或方法中访问。
示例:
class BankAccount {#balance; // 私有属性(余额)constructor(initialAmount) {this.#balance = initialAmount; // 类内部可访问}deposit(amount) {if (amount > 0) {this.#balance += amount; // 类内部修改}}getBalance() {return this.#balance; // 类内部读取,对外提供接口}
}const account = new BankAccount(1000);
console.log(account.getBalance()); // 输出:1000// 外部无法直接访问私有属性
console.log(account.#balance); // 报错:Private field '#balance' must be declared in an enclosing class
私有方法声明
在方法名前加#
,只能在类内部调用。
示例:
class Calculator {#validateNumber(num) { // 私有方法(验证数字合法性)return typeof num === "number" && !isNaN(num);}add(a, b) {if (!this.#validateNumber(a) || !this.#validateNumber(b)) {throw new Error("参数必须是数字");}return a + b;}
}const calc = new Calculator();
console.log(calc.add(2, 3)); // 输出:5// 外部无法调用私有方法
calc.#validateNumber(5); // 报错:Private method '#validateNumber' must be declared in an enclosing class
特性
1.** 严格私有 :外部(包括子类)无法访问,只能在当前类内部使用。
2. 语法约束 :私有成员名必须以#
开头,且不能与公共成员同名。
3. 无继承 **:子类无法继承父类的私有成员。
示例(子类无法访问父类私有成员):
class Parent {#privateField = "父类私有属性";getParentPrivate() {return this.#privateField;}
}class Child extends Parent {getChildPrivate() {return this.#privateField; // 报错:父类私有属性在子类中不可见}
}
通过私有成员可实现真正的封装,避免外部修改类的内部状态。
二、80道ES6 面试题目录列表
文章序号 | ES6 80道 |
---|---|
1 | ES6面试题及详细答案80道(01-05) |
2 | ES6面试题及详细答案80道(06-12) |
3 | ES6面试题及详细答案80道(13-21) |
4 | ES6面试题及详细答案80道(22-32) |
5 | ES6面试题及详细答案80道(33-40) |
6 | ES6面试题及详细答案80道(41-54) |
7 | ES6面试题及详细答案80道(55-61) |
8 | ES6面试题及详细答案80道(62-80) |