笔试

104 阅读5分钟

- TS语言中静态函数的作用?是被类中的静态对象调用吗?

- 在TypeScript(TS)中,静态函数是定义在类内部,但不需要通过类的实例来调用的函数。它们属于类本身,而不是类的任何特定实例。静态函数通常用于以下场景:
  1. 工具函数:当函数不需要访问类的实例属性或方法时,可以定义为静态函数。例如,一个计算日期的函数可能不需要任何实例特定的数据。
  2. 工厂方法:静态函数可以用来创建类的实例,这在设计模式中很常见,尤其是在工厂模式中。
  3. 常量:虽然TypeScript不支持真正的静态常量,但可以通过静态属性来模拟。
  4. 类级别的操作:有时候,你可能需要对类本身进行操作,而不是对类的实例进行操作,这时静态方法就非常有用。
  5. 单例模式:在实现单例模式时,静态函数可以用来确保类只有一个实例,并提供一个全局访问点。

静态函数不是由类中的静态对象调用的,而是由类本身调用。你可以在不创建类实例的情况下直接通过类名来调用静态函数。例如:

typescript
class MyClass {
    static myStaticMethod() {
        console.log('这是一个静态方法');
    }
}

MyClass.myStaticMethod(); // 直接通过类名调用

在上述代码中,myStaticMethod 是一个静态方法,你可以直接通过 MyClass.myStaticMethod() 来调用它,而不需要创建 MyClass 的实例。

- 实现一个类,实现一个定时器功能,并且可以暂停定时器、关掉定时器(主要看一下类的写法和实现)

  • 类的私有属性:
  1. 访问限制private 成员只能在其所属类的内部被访问。
  2. 封装性private 成员的值和行为对类的使用者是隐藏的,这有助于保护类的内部状态。
  3. 继承限制:即使是派生类(子类)也无法直接访问基类中的 private 成员。
class Timer {
   private intervalId: NodeJS.Timeout | null = null;
   private remainingTime: number = 0;
   private isPaused: boolean = false;

   constructor(private duration: number) {
       this.remainingTime = duration;
   }

   start() {
       if (this.intervalId !== null) {
           console.log('Timer is already running.');
           return;
       }

       this.isPaused = false;
       this.intervalId = setInterval(() => {
           if (this.remainingTime <= 0) {
               this.stop();
               console.log('Timer finished.');
               return;
           }

           this.remainingTime -= 1000; // Decrease the remaining time by 1 second
           console.log(`Timer remaining: ${this.remainingTime / 1000} seconds`);
       }, 1000);
   }

   pause() {
       if (this.intervalId === null) {
           console.log('Timer is not running.');
           return;
       }

       clearInterval(this.intervalId);
       this.intervalId = null;
       this.isPaused = true;
       console.log('Timer paused.');
   }

   resume() {
       if (this.intervalId !== null || this.isPaused === false) {
           console.log('Timer is not paused or is already running.');
           return;
       }

       this.start();
       this.isPaused = false;
   }

   stop() {
       if (this.intervalId === null) {
           console.log('Timer is not running.');
           return;
       }

       clearInterval(this.intervalId);
       this.intervalId = null;
       this.remainingTime = this.duration; // Reset the remaining time
       console.log('Timer stopped.');
   }
}

// Usage
const timer = new Timer(5 * 60 * 1000); // 5 minutes
timer.start();
// After some time, pause the timer
setTimeout(() => {
   timer.pause();
}, 1000 * 30); // Pause after 30 seconds
// Resume the timer after some time
setTimeout(() => {
   timer.resume();
}, 1000 * 60); // Resume after 1 minute
// Stop the timer
setTimeout(() => {
   timer.stop();
}, 1000 * 120); // Stop after 2 minutes
  • LRU缓存策略? LRU(Least Recently Used)缓存策略是一种常见的缓存淘汰算法,用于管理缓存中的数据项,以便在缓存空间不足时能够淘汰最长时间未被使用的数据项。这种策略的核心思想是“最近最少使用”,即认为最近被访问的数据项在未来被访问的可能性更高,因此应该保留在缓存中。

工作原理

  1. 访问缓存:当一个数据项被访问时,如果它已经在缓存中,则将其移动到缓存的最近使用的位置。
  2. 添加数据:如果缓存未满,直接将数据项添加到缓存的最近使用位置。
  3. 缓存满时:如果缓存已满,需要移除最长时间未被使用的数据项,通常是缓存中的第一个数据项,然后将新数据项添加到缓存的最近使用位置。

实现方式

LRU缓存可以通过多种数据结构实现,常见的有:

  1. 哈希表 + 双向链表

    • 哈希表用于存储键和对应节点的指针,以实现快速查找。
    • 双向链表用于维护数据项的使用顺序,链表的头部是最近使用的数据项,尾部是最久未使用的数据项。
  2. 哈希表 + 队列

    • 哈希表同样用于存储键和对应节点的指针。
    • 队列用于维护数据项的使用顺序,每次访问数据项时,将其移动到队列的末尾。
  3. 哈希表 + 堆

    • 哈希表用于存储键和对应节点的指针。
    • 用于维护数据项的使用顺序,每次访问数据项时,将其在堆中的位置更新。

应用场景

LRU缓存策略广泛应用于操作系统的页面置换算法、数据库的查询缓存、Web服务器的内容缓存等领域。它帮助系统高效地管理有限的资源,通过淘汰最不常使用的数据来优化性能。

示例代码(Python)

下面是一个使用哈希表和双向链表实现的LRU缓存的简单示例:

python
class LRUCache:

    class ListNode:
        def __init__(self, key=None, value=None):
            self.key = key
            self.value = value
            self.prev = None
            self.next = None

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}
        self.head = self.ListNode()
        self.tail = self.ListNode()
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key in self.cache:
            node = self.cache[key]
            self._remove(node)
            self._add(node)
            return node.value
        return -1

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self._remove(self.cache[key])
        node = self.ListNode(key, value)
        self._add(node)
        self.cache[key] = node
        if len(self.cache) > self.capacity:
            lru = self.head.next
            self._remove(lru)
            del self.cache[lru.key]

    def _remove(self, node):
        prev, next = node.prev, node.next
        prev.next, next.prev = next, prev

    def _add(self, node):
        prev = self.tail.prev
        prev.next = node
        node.prev = prev
        node.next = self.tail
        self.tail.prev = node

# 使用示例
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
print(cache.get(1))       # 返回  1
cache.put(3, 3)           # 淘汰 key 2
print(cache.get(2))       # 返回 -1 (未找到)
cache.put(4, 4)           # 淘汰 key 1
print(cache.get(1))       # 返回 -1 (未找到)
print(cache.get(3))       # 返回  3
print(cache.get(4))       # 返回  4

这个示例中,LRUCache 类使用了一个内部的双向链表和一个哈希表来实现LRU缓存策略。每次访问或添加数据时,都会更新数据在链表中的位置,以保持最近使用的数据在链表头部。