vue 的里 observer、dep、watcher

简单梳理一下 vue 源码里,observer、dep、watcher 这三者的作用,捋清了基本上就知道 vue 的响应式操作了。看一张图:

vue-observer-dep-watcher.jpg

总结一下就是,我们在 vue 文件定义数据 data 属性,会生成一个 _data 的对象,遍历这个 _data 对象的属性,会通过 defineProperty 挂载到 vue 实例上。以图里 src 为例:

  • vm.src,被 defineProperty 处理
  • => 读取和设置都是 vm._data.src
  • => vm._data.src 被 defineProperty 处理
  • => 在其 set get 函数进行依赖收集即更新通知
再细述两个小点
  1. 原来 dom 的更新是在 watcher 的 getter 里。watcher 有两种,一种是在实例上声明的 watch 对象或者主动调用 vm.$watch 方法的,这种都是属性 user=== true 的 watcher,其 getter 都是直接获取 vm 上的值。一种是生命周期由框架生成的 watcher,这个的 getter 是 updateComponent,里面包含了对 dom 的更新。当然接下来就是 diff 算法更新 dom 的事了,不属于依赖更新的内容。

  2. vue 文件的 template 代码会被解析成 render 函数,对,就是那个说直接写会效率更快的 render 函数。原过程是:template => ast => render,这一步还是比较消耗。直接写 render 函数相当于省去前面两步。然后 render 执行后会生成虚拟 dom,也就是 VNode。接下来就是 vm.patch 里通过双指针的 diff 算法,来对比新旧 VNode 的差别,算出变更的地方,然后执行 js 原生的元素操作语句进行 dom 的修改。

二叉树前序中序后序遍历

二叉树的遍历,详细概念还是看搜索引擎总结吧,二叉树遍历

二叉树,前序、中序、后序,遍历,都是深度优先遍历
前中后指的是根节点的访问顺序
对最底层的节点而言,前中后就是三个节点
对于非最底层的节点而言,左右两个节点就是分支,不仅仅是三个节点

const tree = {
  value: 1,
  left: {
    value: 2,
    left: {
      value: 4,
      left: {
        value: 8,
      },
      right: {
        value: 9,
      },
    },
    right: {
      value: 5,
      left: {
        value: 10,
      },
      right: {
        value: 11,
      },
    },
  },
  right: {
    value: 3,
    left: {
      value: 6,
      left: {
        value: 12,
      },
      right: {
        value: 13,
      },
    },
    right: {
      value: 7,
      left: {
        value: 14,
      },
      right: {
        value: 15,
      },
    },
  },
};

Read More

深广度优先遍历

之前其实有写过一篇类似的文章《深度优先遍历(DFS)-栈和广度优先遍历(BFS)-队列的理解》
再次复习一下,写了不一样的注释。而且之前是用 dom 做的遍历,这次还是回到数据来。

// 深广度优先遍历

const tree = {
  value: 1,
  children: [
    {
      value: 2,
      children: [
        {
          value: 4,
          children: [
            {
              value: 8
            },
            {
              value: 9
            },
          ],
        },
        {
          value: 5,
          children: [
            {
              value: 10
            },
            {
              value: 11
            },
          ],
        },
      ],
    },
    {
      value: 3,
      children: [
        {
          value: 6,
          children: [
            {
              value: 12
            },
            {
              value: 13
            },
          ],
        },
        {
          value: 7,
          children: [
            {
              value: 14
            },
            {
              value: 15
            },
          ],
        },
      ],
    }
  ],
};

Read More

链表

链表的概念这里就不赘述了,直接上代码吧。
其他都没什么问题,主要是反转的做法。

class Node {
  constructor(element) {
    this.element = element;
    this.next = null;
  }
}

class LinkedList {
  constructor() {
    this.head = new Node('head');
    this.length = 0;
  }
  insert(newElement, element = null) { // 加在某个元素后面,如果该元素不在,则加在 head 后面
    let currNode = this.find(element);
    if (!currNode) {
      currNode = this.head;
    }
    const newNode = new Node(newElement);
    newNode.next = currNode.next;
    currNode.next = newNode;
    this.length++;
    return this;
  }
  remove(element) {
    const prevNode = this.prev(element);
    if (prevNode && prevNode.next) {
      const currNode = prevNode.next;
      prevNode.next = currNode.next;
      this.length--;
    }
    return this;
  }
  find(element) {
    let node = this.head;
    while(node) {
      if (node.element === element) {
        break;
      }
      node = node.next;
    }
    return node;
  }
  getAll() {
    const result = [];
    let node = this.head;
    while(node && node.next) {
      node = node.next;
      result.push(node.element);
    }
    return result;
  }
  prev(element) {
    let node = this.head;
    while(node) {
      if (node.next && node.next.element === element) {
        break;
      }
      node = node.next;
    }
    return node;
  }
  next(element) {
    const currNode = this.find(element);
    if (currNode) {
      return currNode.next;
    }
    return null;
  }
  reverse(element) {
    // 直接倒序重新生成的不正规解法
    // let node = this.head;
    // this.getAll().reverse().forEach((element) => {
    // 	node.next = new Node(element);
    // 	node = node.next;
    // });
    // node.next = null;

    // 只需要循环一次的正规解法
    let head = this.prev(element); // 这个后面的元素要反转,所以也有可能是链表的 head
    let first = this.find(element); // 一直指向要反转的链表第一个
    if (!first) {
      head = this.head;
      first = this.head.next;
    }
    if (!first) return this;

    const node = first; // node 是不需要变的,一直都是一开始的第一个,因为每次循环 node 都会变位置,它的 next 指向也会一直变
    while(node && node.next) {
      const nextNode = node.next;
      node.next = nextNode.next;
      nextNode.next = first;
      first = nextNode;
    }
    head.next = first;
    // 每次都把 1 的 next 挪到 first 的前面,同时 1 也会相应的变位置
    // 12345:first = 1,node = 1,把 1 的 next 指向 3,2 的 next 指向 1,即把 2 放到 1(frist) 前面
    // 21345:first = 2,node = 1,把 1 的 next 指向 4,3 的 next 指向 2,即把 3 放到 2(frist) 前面
    // 32145:first = 3,node = 1,把 1 的 next 指向 5,4 的 next 指向 3,即把 4 放到 3(frist) 前面
    // 43215
    // 54321
    return this;
  }
}

下面是运行结果

Read More