中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript怎么監測數組的變化

發布時間:2021-07-19 17:54:17 來源:億速云 閱讀:148 作者:chen 欄目:開發技術

這篇文章主要講解了“JavaScript怎么監測數組的變化”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JavaScript怎么監測數組的變化”吧!

前言

之前介紹defineProperty的時候說到,其只能監測對象的變化,并不能監測數組的變化。

本文致力于說清楚怎么實現監測數組的變化。

核心思路:找到改變原數組的方法,然后對這些方法進行劫持處理。

上面這句話,是重中之重,務必讀三遍,記住了,再往下走。

改變原數組,常用到的方法有push pop shift unshift reverse sort splice。

換言之,這些方法是改變數組的入口。

在數組實例和數組原型之間,加一個新的原型

直接修改Array.prototype,是極其危險的。

換一個思路,復制已有的數組原型,然后修改其中的方法,但這里因為原型上的方法不可枚舉,自然也就沒法復制。

于是再換一個思路,在數組和數組的原型之間,插入一個原型,形成原型鏈,數組 => 新原型 => 數組的原型,可以在新原型上增加同名的方法就可以了。

先借助偽代碼理解:

// 偽代碼
let arr = [];
arr.__proto__ = newPrototype;
newPrototype.__proto__ = Array.prototype;
// 然后可以在新原型上添加同名的方法就可以了
newPrototype.push = xxx;

轉換成真實的代碼如下,核心使用了Object.create,

// Object.create返回一個新對象,而來新對象的__proto__就是傳進去的參數。
let newPrototype = Object.create(Array.prototype);
// 然后可以在新原型上添加同名的方法就可以了
newPrototype.push = xxx;

// 需要監測的數組,綁定新的原型就可以了
let arr = [];
arr.__proto__ = newPrototype;

以 push 為例,劫持 push

就是在新原型上重新寫一個 push 的方法,里面執行老的 push,但除此之外還可以做點別的事。

let newPrototype = Object.create(Array.prototype);

// 在新原型上添加同名push
newPrototype.push = function(...args) {
  // 語義化this
  let curArr = this;
  console.log("使用了push");
  // 最后還是會執行原始的push
  return Array.prototype.push.call(curArr, ...args);
};

// 需要監測的數組,綁定新的原型就可以了
let arr = [];
arr.__proto__ = newPrototype;

// 執行push的時候,就會打印了
arr.push(1);

然后其他的方法也是類似的,試著寫寫其他的方法

劫持其他的方法

將其他方法也一起寫了,因為邏輯一樣,直接遍歷即可。

let newPrototype = Object.create(Array.prototype);

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

methods.forEach(method => {
  newPrototype[method] = function(...args) {
    console.log(`使用了${method}`);
    return Array.prototype[method].call(this, ...args);
  };
});

// 需要監測的數組,綁定新的原型就可以了
let arr = [];
arr.__proto__ = newPrototype;

// 執行的時候,就會打印了
arr.push(1);
arr.pop();

數組里有數組項的話,也是需要監測的

這里數組里可能也是有數組的,需要遍歷數組的每項,如果是數組的話依然需要指向新的原型。

嗯,對,用到遞歸了。

let newPrototype = Object.create(Array.prototype);

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

methods.forEach(method => {
  newPrototype[method] = function(...args) {
    console.log(`使用了${method}`);
    return Array.prototype[method].call(this, ...args);
  };
});

function observeArr(arr) {
  // 既是條件限制,也是遞歸的終止條件
  if (!Array.isArray(arr)) {
    return;
  }
  // 整個數組指向新的原型
  arr.__proto__ = newPrototype;
  // 數組的每項,如果是數組,也指向新的原型。
  arr.forEach(observeArr);
}
// 需要監測的數組,綁定新的原型就可以了
let arr = [[1, 2, 3]];
observeArr(arr);

// 執行的時候,就會打印了
arr[0].push(1);
arr[1].pop();

數組新添加的項,如果是數組也需要指向新的原型

能添加元素的方法:push unshift splice。

找到新加的元素,然后是數組的也同樣指向新的原型

let newPrototype = Object.create(Array.prototype);

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

methods.forEach(method => {
  newPrototype[method] = function(...args) {
    console.log(`使用了${method}`);
    let inserted;
    switch (method) {
      case "push":
      case "unshift":
        inserted = args;
        break;
      case "splice":
        inserted = args.slice(2);
        break;
      default:
        break;
    }
    inserted && observeArr(inserted);
    return Array.prototype[method].call(this, ...args);
  };
});

function observeArr(arr) {
  // 即是條件限制,也是遞歸的終止條件
  if (!Array.isArray(arr)) {
    return;
  }
  // 整個數組指向新的原型
  arr.__proto__ = newPrototype;
  // 數組的每項,如果是數組,也指向新的原型。
  arr.forEach(observeArr);
}
// 這里可以導出去,方便別的文件使用
export default observeArr;
// 需要監測的數組,綁定新的原型就可以了
let arr = [];
observeArr(arr);
let addItem = [1, 2, 3];
arr.push(addItem);
// 執行的時候,就會打印了
addItem.push(1);
addItem.pop();

綜合使用 defineProperty 監測對象和數組

現在已經有了監測對象的方法,也有了監測數組的方法,將兩個綜合起來,就能監測數組里面的對象,對象里面的數組了。

將監測數組和監測對象的的可以單獨寫成一個文件,方便之后使用。

這里為了方便直接運行代碼,直接放在一塊了。

/**
 * observeArr的部分
 **/
// 生成新的原型
let newPrototype = Object.create(Array.prototype);

let methods = ["push", "pop", "shift", "unshift", "reverse", "sort", "splice"];
// 在新原型上面添加以上方法,實現劫持
methods.forEach(method => {
  newPrototype[method] = function(...args) {
    console.log(`使用了${method}`);
    let inserted;
    switch (method) {
      case "push":
      case "unshift":
        inserted = args;
        break;
      case "splice":
        inserted = args.slice(2);
        break;
      default:
        break;
    }
    inserted && observeArr(inserted);
    return Array.prototype[method].call(this, ...args);
  };
});

function observeArr(arr) {
  // 新加!!!是對象的話,需要用對象
  if (Object.prototype.toString.call(arr) === "[object Object]") {
    observeObj(arr);
    return;
  }

  if (Array.isArray(arr)) {
    // 整個數組指向新的原型
    arr.__proto__ = newPrototype;
    // 數組的每項,如果是數組,也指向新的原型。
    arr.forEach(observeArr);
  }

  // 不是對象或者數組的,什么都不做
}

/**
 * observeObj的部分
 **/
function observeObj(obj) {
  // 加上參數限制,必須是對象才有劫持,也是遞歸的終止條件
  if (typeof obj !== "object" || obj == null) {
    return;
  }
  // 新加!!!數組交給數組處理
  if (Array.isArray(obj)) {
    observeArr(obj);
    return;
  }
  // 是對象的話 才開始遞歸
  for (let key in obj) {
    // 直接使用 obj.hasOwnProperty會提示不規范
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      observeKey(obj, key);
      // 這里劫持該屬性的屬性值,如果不是對象直接返回,不影響
      observeObj(obj[key]);
    }
  }
  return obj;
}
function observeKey(obj, key) {
  let value = obj[key];
  Object.defineProperty(obj, key, {
    get() {
      console.log("讀取屬性", value);
      return value;
    },
    set(newValue) {
      console.log("設置屬性", newValue);
      value = newValue;
    }
  });
}

/**
 * demo試試
 **/
let data = { a: 1, b: [1, 2, { c: 2 }] };
observeObj(data);
data.a = 2;
data.b.push([2, 3]);

let arr = [{ a: "數組里的對象" }, 3, 4];
observeArr(arr);
arr[0].a = 3;

缺陷

當然數組其實可以不通過方法改變,比如直接刪除數組可以直接使用 length 屬性,或者直接arr[0]=xxx改變數組。

但只有當使用"push","pop", "shift","unshift","reverse","sort","splice"才能檢測到數組變化。

這也是 vue 的缺陷,當然新版的 proxy 將干掉這個缺陷。

所以在使用 vue 的過程中,要盡量使用以上方法操作數組~~~

附注:查看數組的所有屬性和方法

在控制臺可以輸入dir([]),然后能看到數組所有的屬性和方法。

具體用法,可以直接到到mdn 上細看,點擊側邊欄看對應的方法

JavaScript怎么監測數組的變化

感謝各位的閱讀,以上就是“JavaScript怎么監測數組的變化”的內容了,經過本文的學習后,相信大家對JavaScript怎么監測數組的變化這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新余市| 靖西县| 蓬莱市| 修文县| 陈巴尔虎旗| 巧家县| 海口市| 兴化市| 兴义市| 安远县| 寻甸| 平舆县| 调兵山市| 钟山县| 凤庆县| 隆尧县| 莲花县| 兴业县| 芦山县| 芮城县| 黑河市| 东山县| 武鸣县| 炉霍县| 谢通门县| 广河县| 屏东县| 闻喜县| 宁晋县| 长丰县| 库伦旗| 介休市| 刚察县| 贵港市| 上虞市| 塔城市| 靖边县| 仙游县| 太仆寺旗| 祁连县| 顺平县|