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クエリ問題を解決します

推薦する

mysql5.7.14 解凍版インストールと設定方法 グラフィックチュートリアル (win10)

Win10はmysql5.7の解凍版をインストールします。参考までに、具体的な内容は次のとおりです...

MySQL 8.0.12 winx64 詳細なインストールチュートリアル

この記事では、MySQL 8.0.12のインストールチュートリアルを参考までに紹介します。具体的な内...

Linuxカーネルとデバイスツリーのコンパイルと書き込みを分析する

目次1. 材料を準備する2. Linuxカーネルファイルをダウンロードする3. コンパイル4. TF...

WAMPにインストールするとMySQLが起動できるが、再起動後に起動できなくなる問題の解決方法

初めてwampをインストールした後、すべてのサービスが正常に使用できますが、再起動するとwampのア...

MySQL ストアド プロシージャのクエリ コマンドの概要

以下のように表示されます。 mysql.proc から名前を選択します (db='データベー...

Jenkins の紹介と Docker で Jenkins をデプロイする方法

1. 関連概念1.1 Jenkins の概念: Jenkins は、使用されるプラットフォームに関係...

MySQL ルート パスワードをリセットするときに発生する「不明な列 'password'」問題を解決する方法

夜にMACの電源を入れたところ、突然ルートアカウントがMySQLに正常にログインできなくなったため、...

Windows Server 2016 で Flash を有効にする方法

最近、VMware Horizo​​n を導入してテストしましたが、そのコンソールにはデフォルトで ...

CSS カウンターとコンテンツの概要

コンテンツ プロパティは CSS 2.1 で導入され、:before および :after 疑似要素...

9999px に別れを告げる新しい CSS 画像置換テクニック (背景表示と画面外へのテキストの移動)

-9999 ピクセルの画像置換技術は、ここ 10 年近く人気があります。テキスト要素を画像に置き換え...

Linux で SpringBoot jar プログラム デプロイメント シェル スクリプトを起動および停止する方法

では早速、コードをお見せしましょう。具体的なコードは次のとおりです。 #!/bin/bash cd ...

Ubuntu 16.04 mysql5.7.17 リモートポート 3306 を開く

MySQLへのリモートアクセスを有効にするデフォルトでは、MySQL ユーザーにはリモート アクセス...

Vueのwatch、computed、methodsの違いのまとめ

目次1 はじめに2 基本的な使い方2.1 方法2.2 計算プロパティ2.3 リスナーを見る3 3つの...

W3Cチュートリアル(1):W3Cを理解する

1994 年に設立された組織である W3C は、共通プロトコルの開発を促進し、それらの相互運用性を確...

MySQLはテーブル内のフィールドを別のテーブル内のフィールドの値と等しくなるように更新します

以下のように表示されます。 table1 を z として更新し、table2 を zb として結合し...