JavaScript でプロパティハイジャックを実装する方法 defineProperty

JavaScript でプロパティハイジャックを実装する方法 defineProperty

序文

defineProperty は、データ ハイジャックを実現するための Vue の中核です。この記事では、defineProperty がどのようにプロパティ ハイジャックを実現するかを少しずつ説明します。

実際、通常、オブジェクトのプロパティはプロパティを追加または変更する方法で操作され、Object.defineProperty を使用できます。

obj = {} とします。
// 共通操作: 新しい属性を追加/変更 obj.a = 1;
// は次のものと同等です:
オブジェクト.defineProperty(o, "a", {
  値: 1,
  書き込み可能: true、
  設定可能: true、
  列挙可能: true
});

もちろん、通常の例では、長くなりすぎるため、このようには実行しません。

しかし、defineProperty を使用すると、オブジェクトのプロパティをより正確に追加または変更できます。

記述子

まず固有名詞「記述子」から始めましょう。

実際、これはオブジェクトである defineProperty の 3 番目のパラメータです。このオブジェクトには次のプロパティがあります。

  • 設定可能な属性: 記述子を変更できるかどうか、つまり記述子の他の属性を再度変更できるかどうか
  • 列挙可能なプロパティ: プロパティが列挙可能かどうか、つまり、プロパティが列挙可能かどうか
  • 書き込み可能属性: 属性値を変更できるかどうか、つまり、obj.a = 1 を次のように変更できるかどうか
  • 値属性: 属性の値
  • get attribute: 関数です。属性にアクセスすると関数が自動的に呼び出され、関数の戻り値が属性の値になります。
  • set property: は関数です。プロパティが変更されると、関数が自動的に呼び出されます。関数には、割り当てられる新しい値という 1 つのパラメーターのみがあります。

知らせ! ! !

  • 記述子内の値属性、書き込み可能属性、および取得属性、設定属性は相互に排他的です。存在できるのは 1 つだけです。
  • その他のプロパティのデフォルト値はすべて false です。 false にしたくない場合は、忘れずに設定してください。詳細については説明しません (主にあまり使用しないため)。

getとsetの詳細な説明

  • get attribute: 関数です。属性にアクセスすると関数が自動的に呼び出され、関数の戻り値が属性の値になります。
  • set property: は関数です。プロパティが変更されると、関数が自動的に呼び出されます。関数には、割り当てられる新しい値という 1 つのパラメーターのみがあります。

黙って3回繰り返して覚えましょう。

理解を助けるために get と set の例を書いてください。

この例をマスターする必要があります。これを理解すれば、基本的にデータハイジャックの本質を理解できるようになります。

obj = {} とします。

値を 1 にします。
オブジェクト.defineProperty(obj, "b", {
  得る() {
    console.log("b 属性を読み取りました", 値);
    戻り値;
  },
  set(新しい値) {
    console.log("b プロパティを設定", newValue);
    値 = 新しい値;
  }
});
// get 関数をトリガーします。get の戻り値は属性値です // 1
コンソールにログ出力します。
// 設定関数をトリガーすると、値が 2 になります。注意してください。 ! !このとき、メモリ内の属性値は変更されていません。obj.b = 2;
// ただし、プロパティ値を読み取りたい場合、必然的に get 関数がトリガーされ、プロパティ値が自然に変化します。このアイデアは本当に優れています console.log(obj.b);

ここで落とし穴があります。get では読み取り操作はできません。そうしないと無限ループになります。そのため、get set が使用される場所では、常に変数が必要になります。

したがって、ここでは、変数 value の値が属性の値になります。属性を変更する場合は、value の値を変更するだけです。

この例を理解すれば、get と set の本質を理解するのに十分だと思います。

オブジェクトの属性の乗っ取り

前の例を基に、ハイジャックされたオブジェクトの任意のプロパティを記述してみます。

関数 observeKey(obj, key) {
  値をobj[キー]とします。
  Object.defineProperty(obj, キー, {
    得る() {
      console.log("プロパティを読み取り", 値);
      戻り値;
    },
    set(新しい値) {
      console.log("プロパティを設定する", newValue);
      値 = 新しい値;
    }
  });
}
obj = {a: 1} とします。
観測キー(obj, "a");
// a を読み取り、get 関数をトリガーします console.log(obj.a);
// a を設定し、set 関数をトリガーします。obj.a = 1;

オブジェクトのすべてのプロパティをハイジャックする

オブジェクトのすべてのプロパティを乗っ取ろうとする

実際、それはトラバーサルです:

関数 observeObj(obj) {
  for (let key in obj) {
    // obj.hasOwnProperty を直接使用すると非標準のプロンプトが表示されます if (Object.prototype.hasOwnProperty.call(obj, key)) {
      obj を observeKey します。
    }
  }
  obj を返します。
}
関数 observeKey(obj, key) {
  値をobj[キー]とします。
  Object.defineProperty(obj, キー, {
    得る() {
      console.log("プロパティを読み取り", 値);
      戻り値;
    },
    set(新しい値) {
      console.log("プロパティを設定する", newValue);
      値 = 新しい値;
    }
  });
}

obj = {a: 1, b: 2} とします。
obj を観察します。
コンソールにログ出力します。
// a を読み取り、get 関数をトリガーします console.log(obj.a);
// a を設定し、set 関数をトリガーします。obj.a = 1;

オブジェクトのすべてのプロパティをハイジャックする - オブジェクトタイプのプロパティ値を含む

上記には欠陥があり、属性値もオブジェクトである場合、{a:1,c:{b:1}}のように属性値をハイジャックすることはできません。

シンプル、再帰、記入するだけ。

関数 observeObj(obj) {
  // パラメータ制限を追加します。オブジェクトのみがハイジャック可能で、これは再帰の終了条件でもあります。if (typeof obj !== "object" || obj == null) {
    戻る;
  }
  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);
      値 = 新しい値;
    }
  });
}

obj = { a: 1, b: 2, c: { name: "c" } } とします。
obj を観察します。
コンソールにログ出力します。
// a を読み取り、get 関数をトリガーします console.log(obj.a);
// a を設定し、set 関数をトリガーします。obj.a = 1;
// トリガー設定 function obj.c.name = "d";

observeObj 関数はオブジェクトの新しいプロパティをハイジャックすることはできず、オブジェクトの既存のプロパティのみをハイジャックできることに注意してください。

definePropertyの欠点

  • 属性を追加するためにオブジェクトを監視できません
  • オブジェクトの削除属性を監視できません
  • 配列の変更をハイジャックできない

もちろん、配列の変更は他の方法で監視できます。これは、配列変更メソッドをハイジャックすることによって実現されます。

上記の欠陥は​​、Vue に $set/$delete が存在する理由であり、配列が特定の方法でのみ検出できる理由でもあります。

obj = { a: 1, b: [1, 2] } とします。
obj を観察します。
// 新しい属性を追加します。obj.c = 3;
// get 関数はトリガーされません console.log(obj.c);
// 設定関数はトリガーされません obj.b.push(3);

definePropertyはプロパティをマウントすることもできます

実際、これはoptions.data.name(専門用語ではoptions.nameと略される)にアクセスして、データの属性をオプションにマウントすることです。

これは、defineProperty を使用してオプションに新しいプロパティを追加することと同じです。

// 最初に単一の属性をマウントします // options.data はソースと同等です options はターゲットと同等です
関数 proxyKey(ターゲット, ソース, キー) {
  Object.defineProperty(ターゲット、キー、{
    // ここでsource[key]は変数値と同等なので、最も単純な例はコアのget() {
      ソース[キー]を返します。
    },
    set(新しい値) {
      if (newValue === source[key]) {
        戻る;
      }
      ソース[キー] = 新しい値;
    }
  });
}
// 属性をトラバースしてマウントする function proxyObj(target, source) {
  for (let key in source) {
    // obj.hasOwnProperty を直接使用すると非標準のプロンプトが表示されます if (Object.prototype.hasOwnProperty.call(source, key)) {
      proxyKey(ターゲット、ソース、キー);
    }
  }
}
オプション = {
  データ: { 名前: 1 }
};
proxyObj(オプション、オプションデータ);
// 1
console.log(オプション名);

ちなみに、Vue の属性ハイジャックと属性のマウントの基本原則は、上記とほぼ同じです。

definePropertyはログも書き込むことができます

たとえば、obj には値が頻繁に変更される属性があり、変更された値をすべて記録してログを形成したいとします。

obj = {a: 1} とします。

log = [obj.a]; とします。

値を obj.a とします。
オブジェクト.defineProperty(obj, "a", {
  得る() {
    戻り値;
  },
  set(新しい値) {
    (新しい値 === 値) の場合 {
      戻る;
    }
    値 = 新しい値;
    log.push(新しい値);
  }
});

2 番目の引数は 0 です。
3 を 0 に する
4 を 0 にします。
// [1,2,3,4]
コンソールにログを出力します。

一般的なクラスを抽出して、特定の値の変化を記録することができます。

クラスアーカイバ{
  コンストラクタ() {
    値を null にします。
    this.archive = [];
    Object.defineProperty(this, "a", {
      得る() {
        戻り値;
      },
      set(新しい値) {
        (新しい値 === 値) の場合 {
          戻る;
        }
        値 = 新しい値;
        this.archive.push(新しい値);
      }
    });
  }
}
アーカイバを新しいアーカイバ()にします。
アーカイバ.a = 1;
アーカイバ.a = 2;
// [1,2]
console.log(アーカイバ.アーカイブ);

参考文献

MDN の defineProperty

要約する

JavaScript の defineProperty でプロパティ ハイジャックを実装する方法についての記事はこれで終わりです。defineProperty プロパティ ハイジャックに関するその他の関連コンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

<<:  VMware での Ubuntu Docker のインストール (コンテナ構築)

>>:  MySQLはデータベースのN+1クエリ問題を解決します

推薦する

Tcl言語に基づくシンプルなネットワーク環境を構成するプロセスの分析

1. Tclスクリプトファイルcircle.tclコードコメント #シミュレーションに必要なプロパテ...

CSS でよく発生する問題の整理 (ロゴのハッキング/コンテナの固定/画像の垂直方向の中央揃え)

1. IEブラウザモードハックロゴ1. CSSハックロゴコードをコピーコードは次のとおりです。 ie...

JavaScript イベント キャプチャ バブリングとキャプチャの詳細

目次1. イベントの流れ1. コンセプト2. DOMイベントフロー2. イベントの委任1. イベント...

Vueプロジェクトはログインと登録の効果を実現します

この記事の例では、ログインと登録の効果を実現するためのvueプロジェクトの具体的なコードを共有してい...

Windows サービス 2012 Alibaba Cloud サーバーで MySQL をビルドするときに msvcr100.dll ファイルが見つからないという問題を解決します

解決策1: msvcr100.dll ファイルをダウンロードし (インターネットからソース ファイル...

Linux/CentOS システムでネットワーク時間を同期する 2 つの方法の詳細な説明

ハードウェア上の理由により、機械は標準時間にある程度追いつけない場合があり、その誤差は 1 か月で数...

MySQL マルチバージョン同時実行制御 MVCC の基本原理の分析

目次1 トランザクションの同時実行で発生する問題1.1 ダーティリード1.2 繰り返し不可能な読み取...

音声キューイングシステムを実装するためのJavaScript

目次導入主な特徴エフェクト表示キーコード導入音声キューイングシステムは、銀行、レストラン、病院などの...

HTML テーブル マークアップ チュートリアル (43): テーブル ヘッダーの VALIGN 属性

垂直方向では、ヘッダーの配置を上、中央、下に設定できます。基本的な構文構文Top は上、Middle...

MySQL イベント スケジューラに関するよくある話 (必読)

概要MySQL には独自のイベント スケジューラもあり、これは Linux の crontab ジョ...

HTML マークアップ言語 - リファレンス

123WORDPRESS.COM HTML チュートリアル セクションに戻るには、ここをクリックして...

MySQLを安全にシャットダウンする方法

MySQL サーバーをシャットダウンする場合、シャットダウン方法に応じてさまざまな問題が発生する可能...

MySQLはカバーインデックスを使用してテーブルリターンを回避し、クエリを最適化します。

序文カバーリング インデックスについて説明する前に、まずそのデータ構造である B+ ツリーを理解する...

Vue コンポーネント ライブラリ ElementUI はテーブル読み込みツリー データのチュートリアルを実装します

ElementUIは、参考のためにテーブルツリーリストの読み込みチュートリアルを実装しています。具体...

Vue3でelement-plusを使用する方法の詳細な説明

目次1. インストール2. main.jsにインポートする3. 使用Vue3がリリースされてからしば...