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 でルート ユーザーのパスワードを変更する方法

推薦する

WeChatアプレットのスケルトン画面の実装例

目次スケルトンスクリーンとはアプレットでスケルトン画面を生成する方法導入方法表示と非表示ユーザーエク...

MySQL パラメータ関連の概念とクエリ変更方法

序文:以前の記事では、特定のパラメータの機能についてよく紹介してきました。しかし、MySQL パラメ...

Navicat for MySQL 11 登録コード\アクティベーションコードの概要

おすすめの読み物: Navicat12.1シリーズのクラッキングとアクティベーションのチュートリアル...

WebページのレイアウトではIE6の互換性の問題を考慮する必要があります

下の図は、当社のウェブサイト統計システムの訪問者詳細におけるブラウザ閲覧率を示しており、IE6 が ...

衝突検出を実装するためのjs

この記事の例では、衝突検出を実装するためのjsの具体的なコードを参考までに共有しています。具体的な内...

Docker で SVN サーバーを構築するチュートリアル

SVN は Subversion の略称で、ブランチ管理システムを使用して効率的に管理するオープンソ...

MySQL トリガー: 複数のトリガー操作の作成例の分析

この記事では、例を使用して、MySQL で複数のトリガー操作を作成する方法について説明します。ご参考...

Azure Container Registry を使用してイメージを保存する際の問題

Azure Container Registry は、Docker Registry 2.0 仕様に...

シンプルなログインページを実装するための HTML+jQuery

目次導入公開コード(バックエンドインターフェース)例 1: 最もシンプル (純粋な HTML)コード...

MySQL 8.0.15 のインストールと設定方法のグラフィックチュートリアル

この記事ではMySQL 8.0.15のインストールと設定方法を参考までに記録します。具体的な内容は以...

Jenkinsはマイクロサービスをパッケージ化してDockerイメージを構築し、実行します。

目次環境の準備始める1. GitLabリモートリポジトリがマイクロサービスプロジェクトを作成する2....

Dockerコンテナの接続と通信の実装

ポート マッピングは、Docker を別のコンテナーに接続する唯一の方法ではありません。 Docke...

強くお勧めします! Vue 3.2 でシンタックスシュガーを設定する

目次前の1. セットアップ構文シュガーとは何か2. セットアップコンポーネントを使用して自動的に登録...

docker に php-fpm サービス/拡張機能/構成をインストールする詳細なチュートリアル

macにbrewを使ってphp56をインストールしたところ、 opensslがバージョン1.1だった...

判定条件を使用してCSSファイルをインポートする

解決策 1: HEAD に次のコードを挿入するなど、HTML ドキュメントで条件付きインポートを使用...