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

推薦する

MySQL XA で分散トランザクションを実装する方法

目次序文XA プロトコルMySQL XA で分散トランザクションを実装する方法序文MySQL が単一...

Dockerコンテナ終了エラーコードの手順

実行後、一部の Docker コンテナが終了することがあります。原因を調べるには、Docker コン...

画像のフェードインとフェードアウト効果を実現する js

この記事では、画像のフェードインとフェードアウトを実現するためのjsの具体的なコードを参考までに紹介...

現在のブラウザが JavaScript でヘッドレス ブラウザであるかどうかを検出する方法

目次ヘッドレスブラウザとは何ですか?なぜ「ヘッドレス」ブラウザと呼ばれるのでしょうか?ヘッドレスブラ...

MySQLで行を列に変換する方法

MySQL の行から列への操作いわゆる行から列への操作は、テーブルの行情報を列情報に変換することです...

CSSはリモコンのボタンを模倣する

注: このデモはミニプログラム環境でテストされており、他の h5 および pc Web ページにも適...

Nginx 正規表現の詳細な説明

Nginx (エンジン x) は、高性能な HTTP およびリバース プロキシ サーバーであり、IM...

Docker、Nginx、Jenkins をベースにした自動フロントエンド デプロイメント

目次事前準備展開ターゲットDocker環境構築クラウドサーバーに接続Docker環境をインストールす...

Vueのインストール方法の紹介

目次1. グローバルに登録されたコンポーネント2. グローバルカスタム指示vue 、新しいプラグイン...

JavaScript は詳細なコードで星座クエリ機能を実装します

目次1. タイトル2. コード3. 結果IV. 結論1. タイトルテキスト ボックスに誕生日の値を入...

Vue+canvas は、ウォーターフォール チャートを上から下までリアルタイムに更新する効果を実現します (QT と同様)

早速ですが、デモ画像をご紹介します。実装されている機能は、左側に凡例、右側にウォーターフォール チャ...

フロントエンドJavaScriptのクラス

目次1. クラス1.1 コンストラクタ() 1.2 ゲッターとセッター1.3 これ1.4 静的プロパ...

JavaScript 配列の詳細な概要

目次1. 配列誘導1. 文字列を配列に分割する2. コレクションとマップを新しい配列に変換する3. ...

基本構造、ドキュメント タイプ、ヘッダー、本文などの一般的な HTML 要素の概要。

1. 基本構造:コードをコピーコードは次のとおりです。 <!DOCTYPE html PUBL...

垂直方向の中央揃えをエレガントに実現する方法を教えます(推奨)

序文CSS で水平方向と垂直方向に中央揃えする方法はたくさんあります。この記事で紹介する方法は非常に...