面试题:你知道如何完美的将 class 转换为 function 吗?

前言

在进行面试题的讲解之前,我们先简单了解一下ES6中的 class 类。

ES6 是 JavaScript 语言的一次重大更新,它引入了类(class)的概念,提供了一种新的基于原型的继承方式。类似 JavaScript 中实现面向对象编程(OOP)的一种方式,它使得代码更加模块化和易于理解。所以 class 其实是一个语法糖(指那些使得语言更加易于阅读、编写或理解的语言特性),其底层还是通过 构造函数 去创建的,它提供了一种更接近传统面向对象编程语言的语法来创建对象。

以下是ES6中类(class)的一些基本特性:

  1. 声明类 :使用class关键字来声明一个类。
js 复制代码
class Example {}
  1. 构造函数 :类中的constructor方法是一个特殊的方法,用于创建类的一个新实例时执行。如果没有显式定义,那么一个默认的constructor将被隐式添加。
js 复制代码
class Example {
    constructor(){
    
    }
}
  1. 实例属性 :类的属性通过在constructor中使用this关键字来定义。
js 复制代码
this.name = name;
  1. 方法:类可以包含方法,这些方法定义在类的主体中。
js 复制代码
class Example {
    constructor(name){
        this.name = name;
    }
    function() {
        console.log(this.name);
    }
}
  1. 继承 :使用extends关键字来实现类的继承。
js 复制代码
class Example2 extends Example {}
  1. 静态方法 :使用static关键字定义静态方法,直接通过类来调用的方法,其中的 this 指向类本身。
js 复制代码
class Example {
  static function() {
    return 'Ywis';
  }
}
  1. getter 和 setter:类提供了一种定义属性访问器和修改器的方法。
js 复制代码
class Example {
  constructor(name) {
    this.name = name;
  }
  get name() {
    return this.name;
  }
  set name(value) {
    this.name = value;
  }
}
  1. 类表达式:类也可以作为表达式使用,并且可以被赋值给变量。
js 复制代码
const Example = class {
  constructor(name) {
    this.name = name;
  }
};
  1. 私有方法和属性:尽管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 的实例对象,如果不是就是用了别的调用方式了,我们直接抛出错误就好了。

总结 🌸🌸🌸

看到这里,恭喜大家又解决一道面试题,我相信各位小伙伴已经能够完美的应对面试官了。最后祝你也祝我在今后日子里能够登高望远,心向彼岸。

相关推荐
一个处女座的程序猿O(∩_∩)O1 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink4 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
LCG元6 小时前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-6 小时前
验证码机制
前端·后端
燃先生._.7 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖8 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235248 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240259 小时前
前端如何检测用户登录状态是否过期
前端