javascript中的类--实现定义一个链表

340 阅读3分钟

1.ES5中创建一个自定义类型(仿类结构):

{YW4@{X}KF{VKJ0_G@N.png

此代码中的 PersonType 是一个构造器函数,并创建了单个属性 name 。

sayName() 方法被指派到原型上,因此在 PersonType 对象的所有实例上都共享了此方法。接下来,使用 new 运算符创建了 PersonType 的一个新实例 person ,此对象会被认为是一个通过原型继承了PersonType 与 Object 的实例。

2.ES6中的类声明:

XR2BV5DSN(MF$JHRQE~INXW.png

这个 PersonClass 类声明的行为非常类似上个例子中的 PersonType 。类声明允许你在其中使用特殊的constructor 方法名称直接定义一个构造器,而不需要先定义一个函数再把它当作构造器使用。由于类的方法使用了简写语法,于是就不再需要使用 function 关键字。

constructor 之外的方法名称则没有特别的含义,因此可以自由添加方法。

注意:constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

3.类与自定义类型之间的区别:

  1. 类声明不会被提升,这与函数定义不同。类声明的行为与 let 相似,因此在程序的执行到达声明处之前,类会存在于暂时性死区内。

  2. 类声明中的所有代码会自动运行在严格模式下,并且也无法退出严格模式。

  3. 类的所有方法都是不可枚举的,这是对于自定义类型的显著变化,后者必须用 Object.defineProperty() 才能将方法改变为不可枚举。

  4. 类的所有方法内部都没有 [[Construct]] ,因此使用 new 来调用它们会抛出错误。

  5. 调用类构造器时不使用 new ,会抛出错误。

  6. 试图在类的方法内部重写类名,会抛出错误。

4.链表的定义及其相关方法的实现

class ListNode {
    // 默认函数
    constructor(val, next) {
        this.val = val
        this.next = next
    }
}


// 初始化一个链表
var MyLinkedList = function () {
    this.head = null
    // 尾节点
    this.tail = null
    this.size = 0
};

// 获取链表第index个节点
MyLinkedList.prototype.getNode = function(index) {
    if(index < 0 || index >= this.size) return null;
    // 创建虚拟头节点
    let cur = new ListNode(0this.head);
    // 0 -> head
    while(index-- >= 0) {
        cur = cur.next;
    }
    return cur;
};

// 获取链表第index个节点的val
MyLinkedList.prototype.get = function (index) {
    if (index < 0 || index >= this.size) {
        return -1
    }
    return this.getNode(index).val
};

// 添加节点到链表头部
MyLinkedList.prototype.addAtHead = function (val) {
    const node = new ListNode(valthis.head)
    // 更新头节点
    this.head = node
    // 链表长度增加
    this.size++
    // 若尾节点为空
    // 否则尾节点还是尾节点不变
    if (!this.tail) {
        // 新增的node作为尾节点
        this.tail = node
    }
};

// 添加节点到链表尾部的函数
MyLinkedList.prototype.addAtTail = function (val) {
    const node = new ListNode(valnull)
    this.size++
    // 尾节点若不为空
    if (this.tail) {
        // 将新节点挂在旧尾节点后面
        this.tail.next = node
        // 新增节点成为新的尾节点
        this.tail = node
        return
    }
    // 尾节点为空的情况,说明头节点也为空
    // 新增节点既是新的头节点,也是新的尾节点
    this.head = node
    this.tail = node
};

// 将值为val的节点插入到链表第index个节点
MyLinkedList.prototype.addAtIndex = function (index, val) {
    // 如果 index 大于链表长度,则不会插入节点
    if (index > this.size) {
        return
    }
    // 如果index小于0,则在头部插入节点
    if (index <= 0) {
        this.addAtHead(val)
        return
    }
    // 插入尾节点的情况
    if (index === this.size) {
        this.addAtTail(val)
        return 
    }
    // 获取目标节点的上一个的节点
    const node = this.getNode(index - 1)
    // 更新目标节点
    node.next = new ListNode(val, node.next)
    this.size++
};

// 删除链表第index个节点
MyLinkedList.prototype.deleteAtIndex = function (index) {
    // 索引无效
    if (index < 0 || index >= this.size) {
        return
    }
    // 若删除的节点为头节点
    if (index === 0) {
        this.head = this.head.next
        // 如果删除的这个节点同时是尾节点,要处理尾节点
        // 其实就是size为1的情况
        if (index === this.size - 1) {
            this.tail = this.head
        }
        this.size--

        return
    }

    // 获取目标节点的上一个的节点
    const node = this.getNode(index - 1)
    node.next = node.next.next
    // 若删除的目标节点为尾节点
    if(index === this.size - 1) {
        this.tail = node;

    }
    this.size--
};

参考文献:

1.<<深入理解ES6>>
2.<<代码随想录>>