前言
在进行面试题的讲解之前,我们先简单了解一下ES6中的 class
类。
ES6 是 JavaScript 语言的一次重大更新,它引入了类(class)的概念,提供了一种新的基于原型的继承方式。类似 JavaScript 中实现面向对象编程(OOP)的一种方式,它使得代码更加模块化和易于理解。所以 class 其实是一个语法糖(指那些使得语言更加易于阅读、编写或理解的语言特性),其底层还是通过 构造函数
去创建的,它提供了一种更接近传统面向对象编程语言的语法来创建对象。
以下是ES6中类(class)的一些基本特性:
- 声明类 :使用
class
关键字来声明一个类。
js
class Example {}
- 构造函数 :类中的
constructor
方法是一个特殊的方法,用于创建类的一个新实例时执行。如果没有显式定义,那么一个默认的constructor
将被隐式添加。
js
class Example {
constructor(){
}
}
- 实例属性 :类的属性通过在
constructor
中使用this
关键字来定义。
js
this.name = name;
- 方法:类可以包含方法,这些方法定义在类的主体中。
js
class Example {
constructor(name){
this.name = name;
}
function() {
console.log(this.name);
}
}
- 继承 :使用
extends
关键字来实现类的继承。
js
class Example2 extends Example {}
- 静态方法 :使用
static
关键字定义静态方法,直接通过类来调用的方法,其中的this
指向类本身。
js
class Example {
static function() {
return 'Ywis';
}
}
- getter 和 setter:类提供了一种定义属性访问器和修改器的方法。
js
class Example {
constructor(name) {
this.name = name;
}
get name() {
return this.name;
}
set name(value) {
this.name = value;
}
}
- 类表达式:类也可以作为表达式使用,并且可以被赋值给变量。
js
const Example = class {
constructor(name) {
this.name = name;
}
};
- 私有方法和属性:尽管ES6没有直接支持私有方法和属性,但是可以使用闭包的特性来模拟。
面试题 🔥🔥🔥
在了解完 class
类之后就可以开始我们的面试题讲解了,先上题目:
js
class Example {
constructor(name){
this.name = name;
}
function() {
console.log(this.name);
}
}
我们知道 class
类其实就是一个语法糖,本质上还是个普通的构造函数,所以这道题本质就是来考察我们对于类的理解是否准确。我相信绝大多数的小伙伴们都能回答出这个面试题,给出这样一份答卷:
js
function Example(name){
this.name = name;
}
Example.prototype.function = function () {
console.log(this.name);
}
这样子做虽然算是完成了这道面试题,但却达不到让面试官满意的程度,小问题还是有很多的。接下来,我们来看看如何才能让面试官心满意足。
第一步
我们清楚在类和模块的内部,默认是严格模式,所以不需要使用 use strict
指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。所以我们在转换成普通构造函数时,我们也要在第一行加上 use strict
,即:
js
`use strict`;
function Example(name){
this.name = name;
}
Example.prototype.function = function () {
console.log(this.name);
}
第二步
在 ES6 中类虽然本质上是一个函数,但它也有它自己的特点,我们只能通过 new
来调用,直接调用会出现这样的一个错误:
Example('Ywis')
TypeError: Class constructor Example cannot be invoked without 'new'
所以我们在转换时,也应该将这个错误给加上,应该怎么做呢?
我们知道类只能通过 new
来调用,那么它的 this
就默认指向类的实例。然后在构造函数中,当它是通过 new
来调用的时候,它的 this
就一定是指向这构造函数的实例,也就是一定满足 this instanceof Example
,所以我们可以通过验证 this
的指向,来判断函数的调用方式是不是通过 new
来调用的,解决代码如下:
js
`use strict`;
function Example(name){
// 验证 this 的指向
if(!(this instanceof Example)){
throw new TypeError(
`Class constructor Example cannot be invoked without 'new'`
)
}
this.name = name;
}
Example.prototype.function = function () {
console.log(this.name);
}
第三步
在 class
类中,它有自己的方法,这些方法定义在类的主体中,也肯定是在原型上的,不过有一点需要注意的是:这些方法是不可以被枚举的。我们通过一段代码来理解:
js
class Example {
constructor(name){
this.name = name;
}
function() {
console.log(this.name);
}
}
let example = new Example('Ywis')
for(let key in example){
console.log(key); // name
}
我们先使用 new
关键字创建了 Example
类的一个新实例,然后使用 for...in 循环 遍历example
对象的可枚举属性。由于Example
类的实例中只有一个可枚举属性name
,所以将打印出'name'
。
可是将一样的思路用于转换后的代码,我们会发现将打印出 'name' 'function'
。所以我们就不能像这样 Example.prototype.function = ...
直接赋值了,我们可以这样改:
js
Object.defineProperty(Example.prototype, 'function', {
value: function(){
console.log(this.name);
},
enumerable: false, // 不可枚举
})
这一步完成后,大伙们是不是觉得这个面试题已经近乎完美了?其实这里还有一个非常隐秘的加分点,让我们来看看。
第四步
在类自己的方法里面有一个这样的特点,这个方法本身不能使用 new
关键字来调用,如果这样做的话会报错:
new example.function()
example.function is not a constructor
它会告诉你,这个 example.function
它不是一个构造器,所以我们在转换后的代码中,也要进行一个和第二步 类似的操作,一样的通过判断 this
的指向来让它不能通过 new
调用。
js
`use strict`;
function Example(name){
// 不可通过 new 调用(验证 this 的指向)
if(!(this instanceof Example)){
throw new TypeError(
`Class constructor Example cannot be invoked without 'new'`
)
}
this.name = name;
}
Object.defineProperty(Example.prototype, 'function', {
value: function(){
// 不可通过 new 调用(验证 this 的指向)
if(!(this instanceof Example)){
throw new TypeError(
`example.function is not a constructor`
)
}
console.log(this.name);
},
enumerable: false, // 不可枚举
})
let example = new Example('Ywis')
new example.function()
正常情况下,这里的 this
应该指向 Example
的实例对象,如果不是就是用了别的调用方式了,我们直接抛出错误就好了。
总结 🌸🌸🌸
看到这里,恭喜大家又解决一道面试题,我相信各位小伙伴已经能够完美的应对面试官了。最后祝你也祝我在今后日子里能够登高望远,心向彼岸。