手摸手從0實現簡版Vue --- (數組劫持)

接上篇:手摸手從0實現簡版Vue --- (對象劫持)javascript

1. 數組原生方法的劫持

前面已經完成了對data數據裏對象的劫持,可是針對數組的變化,Object.defineProperty 不能很好的支持,因此在Vue 中,採起了對數組原生方法進行劫持的操做,來保證數據能夠被正常監測到。vue

因爲原來的方法不能正常對數組進行數據劫持,因此咱們要對data的數據類型進行區分,因此咱們須要改寫Observer類:java

class Observer {
  constructor(data) { // data === vm._data
    // 將用戶的數據使用 Object.defineProperty從新定義
    if (Array.isArray(data)) {
      // 對數組方法進行劫持, 讓數組經過鏈來查找咱們本身改寫的原型方法
      data.__proto__ = arrayMethods;
    } else {
      this.walk(data);
    }
  }
  
    // ...
}

經過使用data.__proto__將數組上的方法換成咱們改寫的原型方法 arrayMethods,咱們新建一個array.js來重寫咱們的數組方法。git

/**
 * 攔截用戶調用的push、shift、unshift、pop、reverse、sort、splice數組方法
 */

// 獲取老的數組方法
let oldArrayProtoMethods = Array.prototype;

// 拷貝新的對象,用來查找老的方法, 不修改原型上的方法
export let arrayMethods = Object.create(oldArrayProtoMethods);

let methods = [
  'push',
  'pop',
  'unshift',
  'shift',
  'sort',
  'splice'
];

methods.forEach(method => {
  arrayMethods[method] = function(...args) { // 函數劫持
    let result = oldArrayProtoMethods[method].apply(this, args);
    console.log('調用數組更新方法');
    return result;
  }
});

此時咱們去調用vm.arr.push(1, 2, 3)會打印調用數組更新方法,說明此時的push方法是調用的咱們重寫的方法。github

2. 數組新增對象進行監測

若是咱們調用vm.arr.push({a: 1}),那咱們也須要對數組新增的對象屬性進行監測,也就是須要添加observe觀測,因此還須要進一步改寫數組的方法:segmentfault

/**
 * 對數組新增的元素進行劫持
 * @param {*} inserted 
 */
+export function observerArray(inserted) {
+  for (let i = 0; i < inserted.length; i++){
+    observe(inserted[i]); // 還須要對數組裏面的內容進行監測
+  }
+}

methods.forEach(method => {
  arrayMethods[method] = function(...args) { // 函數劫持
    let result = oldArrayProtoMethods[method].apply(this, args);
    console.log('調用數組更新方法');
+    let inserted;
+    switch (method) {
+      case 'push':
+      case 'unshift':
+        inserted = args;
+        break;
+      case 'splice':
+        inserted = args.slice(2); // 獲取splice(start, deleteCount, [])新增的內容
+      default:
+        break;
+    }
+    if(inserted) observerArray(inserted);
    return result;
  }
});

3. 結果

此時若是咱們數組添加一個對象而且修改對象的值均可以正常被監測到:數組

console.log(vm.arr.push({a: 1}));
console.log(vm.arr[3].a = 100);

此時瀏覽器會打印:瀏覽器

image-20200307144823192

到這咱們的數組劫持也實現了,下一部分咱們去實現如何將數據經過模板的方式替換到頁面並同步改變。app

代碼部分可看本次提交commit 函數

但願各位大佬點個star,小弟跪謝~

相關文章
相關標籤/搜索