JavaScript で配列の変更を監視する方法

JavaScript で配列の変更を監視する方法

序文

以前、defineProperty を紹介したとき、オブジェクトの変更のみを監視でき、配列の変更は監視できないことを説明しました。

この記事では、配列の変更を監視する方法を説明します。

中心的なアイデア: 元の配列を変更する方法を見つけ、それらのメソッドを乗っ取る。

上記の文章は非常に重要です。先に進む前に必ず 3 回読んで覚えておいてください。

元の配列を変更するためによく使用されるメソッドには、push、pop、shift、unshift、reverse、sort、splice などがあります。

つまり、これらのメソッドは配列のエントリを変更します。

配列インスタンスと配列プロトタイプの間に新しいプロトタイプを追加する

Array.prototype を直接変更することは非常に危険です。

考え方を変えて、既存の配列プロトタイプをコピーし、その中のメソッドを変更しますが、ここではプロトタイプ上のメソッドは列挙可能ではないため、コピーできません。

では、考え方を変えてみましょう。配列と配列のプロトタイプの間にプロトタイプを挿入して、プロトタイプ チェーン (配列 => 新しいプロトタイプ => 配列のプロトタイプ) を形成します。次に、同じ名前のメソッドを新しいプロトタイプに追加できます。

まず擬似コードを使用して理解します。

// 疑似コード let arr = [];
arr.__proto__ = 新しいプロトタイプ;
newPrototype.__proto__ = Array.prototype;
// 次に、新しいプロトタイプに同じ名前のメソッドを追加できます。newPrototype.push = xxx;

実際のコードは以下のようになります。コアではObject.createを使用しています。

// Object.create は新しいオブジェクトを返します。新しいオブジェクトの __proto__ は渡されるパラメーターです。
newPrototype = Object.create(Array.prototype); を作成します。
// 次に、新しいプロトタイプに同じ名前のメソッドを追加できます。newPrototype.push = xxx;

// 監視する配列。新しいプロトタイプをバインドするだけです。let arr = [];
arr.__proto__ = 新しいプロトタイプ;

プッシュを例に挙げると、プッシュをハイジャックする

新しいプロトタイプのプッシュ メソッドを書き換えるだけで、古いプッシュを実行するだけでなく、他の操作も実行できます。

newPrototype = Object.create(Array.prototype); を作成します。

// 新しいプロトタイプに同じ名前のプッシュを追加します
newPrototype.push = function(...args) {
  // セマンティック this
  curArr = this とします。
  console.log("push が使用されています");
  //最後に、元のプッシュが実行されます
  Array.prototype.push.call(curArr, ...args) を返します。
};

// 監視する配列。新しいプロトタイプをバインドするだけです。let arr = [];
arr.__proto__ = 新しいプロトタイプ;

// pushが実行されると、arr.push(1)が出力されます。

他のメソッドも同様です。他のメソッドを書いてみてください

他の方法のハイジャック

他のメソッドもロジックが同じであり、直接走査できるため、一緒に記述されます。

newPrototype = Object.create(Array.prototype); を作成します。

メソッドを ["push"、"pop"、"shift"、"unshift"、"reverse"、"sort"、"splice"] とします。

メソッド.forEach(メソッド => {
  newPrototype[メソッド] = function(...args) {
    console.log(`${method} が使用されました`);
    Array.prototype[method].call(this, ...args) を返します。
  };
});

// 監視する配列。新しいプロトタイプをバインドするだけです。let arr = [];
arr.__proto__ = 新しいプロトタイプ;

// 実行するとarr.push(1)が出力されます。
arr.pop();

配列内に配列項目がある場合は、それらも監視する必要があります。

ここで配列内に配列が存在する場合、配列内の各項目を走査する必要があります。配列の場合でも、新しいプロトタイプを指す必要があります。

はい、再帰が使用されます。

newPrototype = Object.create(Array.prototype); を作成します。

メソッドを ["push"、"pop"、"shift"、"unshift"、"reverse"、"sort"、"splice"] とします。

メソッド.forEach(メソッド => {
  newPrototype[メソッド] = function(...args) {
    console.log(`${method} が使用されました`);
    Array.prototype[method].call(this, ...args) を返します。
  };
});

関数 observeArr(arr) {
  // これは条件付き制限と再帰終了条件の両方です if (!Array.isArray(arr)) {
    戻る;
  }
  // 配列全体が新しいプロトタイプを指します arr.__proto__ = newPrototype;
  // 配列内の各項目は、配列の場合は、新しいプロトタイプも指します。
  arr.forEach(Arr を観察します);
}
// 監視する配列。新しいプロトタイプをバインドするだけです。let arr = [[1, 2, 3]];
観測Arr(arr);

// 実行するとarr[0].push(1)が出力されます。
arr[1].pop();

配列に追加された新しい項目は、配列の場合は新しいプロトタイプを指す必要があります。

要素を追加できるメソッド: push unshift splice。

新しく追加された要素を見つけると、配列も新しいプロトタイプを指すようになります

newPrototype = Object.create(Array.prototype); を作成します。

メソッドを ["push"、"pop"、"shift"、"unshift"、"reverse"、"sort"、"splice"] とします。

メソッド.forEach(メソッド => {
  newPrototype[メソッド] = function(...args) {
    console.log(`${method} が使用されました`);
    挿入します。
    スイッチ(メソッド){
      ケース「プッシュ」:
      ケース「unshift」:
        挿入された = 引数;
        壊す;
      ケース「スプライス」:
        挿入 = args.slice(2);
        壊す;
      デフォルト:
        壊す;
    }
    挿入 && observeArr(挿入);
    Array.prototype[method].call(this, ...args); を返します。
  };
});

関数 observeArr(arr) {
  // これは条件付き制限と再帰終了条件の両方です if (!Array.isArray(arr)) {
    戻る;
  }
  // 配列全体が新しいプロトタイプを指します arr.__proto__ = newPrototype;
  // 配列内の各項目は、配列の場合は新しいプロトタイプも指します。
  arr.forEach(Arr を観察します);
}
// これをエクスポートすると、他のファイルでも使用しやすくなります export default observeArr;
// 監視する配列。新しいプロトタイプをバインドするだけです。let arr = [];
観測Arr(arr);
addItem = [1, 2, 3]とします。
arr.push(アイテムを追加);
// 実行するとaddItem.push(1)が出力されます。
アイテムを追加します。

オブジェクトと配列を監視するためのdefinePropertyの併用

これで、オブジェクトを監視するメソッドと配列を監視するメソッドができました。この 2 つを組み合わせることで、配列内のオブジェクトとオブジェクト内の配列を監視できます。

監視配列と監視オブジェクトは、後で使用するために別のファイルに書き込むことができます。

直接コード実行を容易にするために、これらはここにまとめられています。

/**
 * observeArr 部分**/
// 新しいプロトタイプを生成します。let newPrototype = Object.create(Array.prototype);

メソッドを ["push"、"pop"、"shift"、"unshift"、"reverse"、"sort"、"splice"] とします。
// 上記のメソッドを新しいプロトタイプに追加して、methods.forEach(method => {
  newPrototype[メソッド] = function(...args) {
    console.log(`${method} が使用されました`);
    挿入します。
    スイッチ(メソッド){
      ケース「プッシュ」:
      ケース「unshift」:
        挿入された = 引数;
        壊す;
      ケース「スプライス」:
        挿入 = args.slice(2);
        壊す;
      デフォルト:
        壊す;
    }
    挿入 && observeArr(挿入);
    Array.prototype[method].call(this, ...args); を返します。
  };
});

関数 observeArr(arr) {
  // 新しい! ! !オブジェクトの場合は、オブジェクトを使用する必要があります if (Object.prototype.toString.call(arr) === "[object Object]") {
    観測オブジェクト(arr)
    戻る;
  }

  Array.isArray(arr) の場合 {
    // 配列全体が新しいプロトタイプを指します arr.__proto__ = newPrototype;
    // 配列内の各項目は、配列の場合は新しいプロトタイプも指します。
    arr.forEach(Arr を観察します);
  }

  // オブジェクトでも配列でもない場合は何もしません}

/**
 * observeObj 部分**/
関数 observeObj(obj) {
  // パラメータ制限を追加します。オブジェクトのみがハイジャック可能で、これは再帰の終了条件でもあります。if (typeof obj !== "object" || obj == null) {
    戻る;
  }
  // 新しい! ! !配列は処理のために配列に渡されます if (Array.isArray(obj)) {
    観測Arr(obj);
    戻る;
  }
  // オブジェクトの場合のみ再帰を開始する for (let key in obj) {
    // obj.hasOwnProperty を直接使用すると非標準のプロンプトが表示されます if (Object.prototype.hasOwnProperty.call(obj, key)) {
      obj を observeKey します。
      // ここで、属性の属性値がハイジャックされます。オブジェクトでない場合は、observeObj(obj[key]) に影響を与えずに直接返されます。
    }
  }
  obj を返します。
}
関数 observeKey(obj, key) {
  値をobj[キー]とします。
  Object.defineProperty(obj, キー, {
    得る() {
      console.log("プロパティを読み取り", 値);
      戻り値;
    },
    set(新しい値) {
      console.log("プロパティを設定する", newValue);
      値 = 新しい値;
    }
  });
}

/**
 * デモを試す**/
データ = {a: 1、b: [1、2、{c: 2 }] }とします。
observeObj(データ);
データ.a = 2;
データ.b.push([2, 3]);

arr = [{ a: "配列内のオブジェクト" }, 3, 4]とします。
観測Arr(arr);
arr[0].a = 3;

欠陥

もちろん、メソッドを使わずに配列を変更することもできます。たとえば、length属性を使用して配列を削除したり、arr[0]=xxxを使用して配列を直接変更したりできます。

ただし、配列の変更は、「push」、「pop」、「shift」、「unshift」、「reverse」、「sort」、「splice」を使用する場合にのみ検出できます。

これも Vue の欠陥です。もちろん、新しいバージョンのプロキシではこの欠陥は解消されます。

なのでvueを使うときは上記の方法で配列を操作してみてください~~~

注: 配列のすべてのプロパティとメソッドを表示する

コンソールに dir([]) と入力すると、配列のすべてのプロパティとメソッドが表示されます。

具体的な使用方法については、mdnに直接アクセスし、サイドバーをクリックして対応する方法を確認してください。

要約する

JavaScript で配列の変更を監視する方法についての記事はこれで終わりです。JS による配列の変更の監視に関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript配列についてさらに詳しく知るのに役立つ記事
  • Javascript配列の重複排除のいくつかの方法の詳細な説明
  • JavaScript配列の組み込みメソッドの詳細な説明
  • よく使われるJavaScript配列メソッド
  • JavaScript 配列の詳細な概要
  • JavaScriptでよく使われる配列重複排除実戦ソースコード
  • JS オブジェクト配列の重複排除のための 3 つの方法の例と比較
  • JSは単純なフィルタリングから複数条件のフィルタリングまで配列フィルタリングを実装します
  • JavaScript 配列の Reduce() メソッドの構文と例の分析
  • JavaScript で 24 以上の配列メソッドを手動で実装する

<<:  Centos8 でローカル Web サーバーを構築するための実装手順

>>:  mysql 8.0.16 winx64 および Linux でルート ユーザーのパスワードを変更する方法

推薦する

MySQL インデックスの一般的な問題の概要

Q1: データベースにはどのようなインデックスがありますか?メリットとデメリットは何ですか? 1. ...

スタートアップ企業が丹念に作り上げた優れたウェブデザイン17選

スタートアップ企業は、型破りな仕事のやり方、ユニークなビジネスモデル、特徴的な製品やサービス、パーソ...

Linux (Ubuntu) での MySQL 5.6.28 のインストールと設定のチュートリアル

mysql5.6.28のインストールと設定方法1. 基本的なシステム情報を確認し、yumでインストー...

Webデザイン: タイトルが完全に表示できない場合

<br />今日、新しくなった ChinaUI.com の Web サイトを見たのですが...

nginx で複数の仮想ホストを設定する方法の例

nginx で仮想ホスト vhost を設定すると非常に便利です。 nginx設定ファイルnginx...

InnoDB ロック (レコード、ギャップ、Next-Key ロック) の詳細な説明

レコード ロックは、単一のインデックス レコードをロックします。レコード ロックは常にインデックスを...

キャンバスはスクラッチカード効果を描画します

この記事では、キャンバスでスクラッチカード効果を描画するための具体的なコードを参考までに共有します。...

W3C チュートリアル (14): W3C RDF および OWL アクティビティ

RDF と OWL は、2 つの重要なセマンティック ウェブ テクノロジーです。 RDF と OWL...

Docker ベースの ELK ログ システムを構築する方法

背景要件:ビジネスがどんどん大きくなると、サーバーの数も増え、さまざまなアクセスログ、アプリケーショ...

Docker データ ストレージ ボリュームの詳細な説明

デフォルトでは、コンテナ データの読み取りと書き込みはコンテナのストレージ レイヤーで行われます。コ...

Tomcat CentOS インストールプロセス図

Tomcat CentOS インストールこのインストール チュートリアルでは、次の内容について説明し...

TypeScript マッピング型の詳細

目次1. マップされた型2. マッピング修飾子3. キーの再マッピング4. さらなる探究序文: Ty...

vue2.x の徹底研究 - h 関数の説明

目次解決、要約: vue プロジェクト。 .vue ファイルのテンプレート内に記述されたコードは、w...

MySQL アーキテクチャのナレッジポイントの概要

1. データベースとデータベースインスタンスMySQL の研究では、データベースとデータベース イン...

VMware12 インストール centOS8 構成グラフィック チュートリアルの詳細説明 (vm 仮想マシン インストール centos8 チュートリアル)

数日前に CentOS8 がリリースされました。8 の最初のバージョンですが、今日は VM12 に ...