反映する

概要

Reflect オブジェクトは、Proxy オブジェクトと同様に、オブジェクトを操作するために ES6 によって提供される新しい API です。 「Reflect」オブジェクトは、いくつかの目的のために設計されています。

(1) 明らかに言語の内部にある Object オブジェクトのいくつかのメソッド (Object.defineProperty など) を Reflect オブジェクトに配置します。この段階では、一部のメソッドは ObjectReflect オブジェクトの両方にデプロイされており、将来の新しいメソッドは Reflect オブジェクトにのみデプロイされます。言い換えれば、言語の内部メソッドは Reflect オブジェクトから取得できます。

(2) いくつかの Object メソッドの戻り結果を変更して、より合理的なものにします。たとえば、Object.defineProperty(obj, name, desc) はプロパティを定義できない場合にエラーをスローしますが、Reflect.defineProperty(obj, name, desc)false を返します。

//古い書き方
試す {
  Object.defineProperty(ターゲット、プロパティ、属性);
  //成功
} キャッチ (e) {
  // 失敗
}

//新しい書き方
if (Reflect.defineProperty(ターゲット, プロパティ, 属性)) {
  //成功
} それ以外 {
  // 失敗
}

(3) 「オブジェクト」の操作を関数的な動作にします。 「name in obj」や「delete obj[name]」など、一部の「Object」操作は必須ですが、「Reflect.has(obj, name)」や「Reflect.deleteProperty(obj, name)」によって機能するようになりました。行動。

//古い書き方
'assign' in Object // true

//新しい書き方
Reflect.has(Object, 'assign') // true

(4) Reflect オブジェクトのメソッドと Proxy オブジェクトのメソッドは 1 対 1 に対応します。 Proxy オブジェクトのメソッドであれば、対応するメソッドが 上に見つかります。 Reflect オブジェクト。これにより、「Proxy」オブジェクトは、対応する「Reflect」メソッドを簡単に呼び出してデフォルトの動作を完了し、動作を変更するための基礎として機能することができます。言い換えれば、「Proxy」がデフォルトの動作をどのように変更しても、「Reflect」では常にデフォルトの動作を取得できます。

プロキシ(ターゲット, {
  set: function(ターゲット、名前、値、受信者) {
    var success = Reflect.set(ターゲット、名前、値、受信者);
    if (成功) {
      console.log('プロパティ ' + 名前 + ' 上の ' + ターゲット + ' に設定 + 値);
    }
    成功を返します。
  }
});

上記のコードでは、「Proxy」メソッドが「target」オブジェクトの属性割り当て動作をインターセプトします。 Reflect.set メソッドを使用してオブジェクトのプロパティに値を割り当て、元の動作が完了していることを確認してから、追加の機能をデプロイします。

別の例を示します。

var loggedObj = 新しいプロキシ(obj, {
  get(ターゲット, 名前) {
    console.log('get', ターゲット, 名前);
    return Reflect.get(ターゲット, 名前);
  }、
  deleteProperty(ターゲット, 名前) {
    console.log('削除' + 名前);
    return Reflect.deleteProperty(ターゲット, 名前);
  }、
  has(ターゲット, 名前) {
    console.log('が' + 名前);
    return Reflect.has(ターゲット, 名前);
  }
});

上記のコードでは、「Proxy」オブジェクトの各インターセプト操作 (「get」、「delete」、「has」) は、対応する「Reflect」メソッドを内部で呼び出して、ネイティブの動作が正常に実行できることを確認します。追加された作業は、操作ごとに 1 行のログを出力することです。

「Reflect」オブジェクトを使用すると、多くの操作が読みやすくなります。

//古い書き方
Function.prototype.apply.call(Math.floor, 未定義, [1.75]) // 1

//新しい書き方
Reflect.apply(Math.floor, 未定義, [1.75]) // 1

静的メソッド

「Reflect」オブジェクトには合計 13 個の静的メソッドがあります。

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(ターゲット, 引数)
  • Reflect.get(ターゲット、名前、受信者)
  • Reflect.set(ターゲット、名前、値、受信者)
  • Reflect.defineProperty(ターゲット、名前、説明)
  • Reflect.deleteProperty(ターゲット, 名前)
  • Reflect.has(ターゲット, 名前)
  • Reflect.ownKeys(ターゲット)
  • Reflect.isExtensible(ターゲット)
  • Reflect.preventExtensions(ターゲット)
  • Reflect.getOwnPropertyDescriptor(ターゲット, 名前)
  • Reflect.getPrototypeOf(ターゲット)
  • Reflect.setPrototypeOf(ターゲット, プロトタイプ)

上記のメソッドの機能のほとんどは、Object オブジェクト上の同名のメソッドと同じであり、Proxy オブジェクトのメソッドと 1 対 1 に対応します。以下にそれらについて説明します。

Reflect.get(ターゲット、名前、受信者)

Reflect.get メソッドは、target オブジェクトの name プロパティを検索して返します。そのようなプロパティが存在しない場合は unknown を返します。

var myObject = {
  フー: 1、
  バー: 2get baz() {
    this.foo + this.bar を返します。
  }、
}

Reflect.get(myObject, 'foo') // 1
Reflect.get(myObject, 'bar') // 2
Reflect.get(myObject, 'baz') // 3

name 属性が読み取り関数 (ゲッター) を展開する場合、読み取り関数の thisreceiver にバインドされます。

var myObject = {
  フー: 1、
  バー: 2get baz() {
    this.foo + this.bar を返します。
  }、
};

var myReceiverObject = {
  フー: 4、
  バー: 4、
};

Reflect.get(myObject, 'baz', myReceiverObject) // 8

最初のパラメータがオブジェクトでない場合、「Reflect.get」メソッドはエラーを報告します。

Reflect.get(1, 'foo') // エラーを報告する
Reflect.get(false, 'foo') // エラーを報告する

Reflect.set(ターゲット、名前、値、受信者)

Reflect.set メソッドは、target オブジェクトの name プロパティを value に設定します。

var myObject = {
  フー: 1、
  セットバー(値) {
    this.foo = 値を返します。
  }、
}

myObject.foo // 1

Reflect.set(myObject, 'foo', 2);
myObject.foo // 2

Reflect.set(myObject, 'bar', 3)
myObject.foo // 3

name 属性が代入関数を設定する場合、代入関数の thisreceiver にバインドされます。

var myObject = {
  フー: 4、
  セットバー(値) {
    this.foo = 値を返します。
  }、
};

var myReceiverObject = {
  フー: 0、
};

Reflect.set(myObject, 'bar', 1, myReceiverObject);
myObject.foo // 4
myReceiverObject.foo // 1

Proxy オブジェクトと Reflect オブジェクトが一緒に使用される場合、前者は代入操作をインターセプトし、後者は代入のデフォルト動作を完了し、receiver が渡されてから Reflect.set が渡されることに注意してください。 Proxy.defineProperty インターセプトをトリガーします。

p = { とします
  a:「あ」
};

let ハンドラー = {
  set(ターゲット、キー、値、受信者) {
    console.log('セット');
    Reflect.set(ターゲット、キー、値、レシーバー)
  }、
  defineProperty(ターゲット、キー、属性) {
    console.log('定義プロパティ');
    Reflect.defineProperty(ターゲット、キー、属性);
  }
};

let obj = 新しいプロキシ(p, ハンドラー);
obj.a = 'A';
// セット
//プロパティを定義する

上記のコードでは、「Proxy.set」インターセプトで「Reflect.set」が使用され、「receiver」が渡されて、「Proxy.defineProperty」インターセプトがトリガーされます。これは、Proxy.setreceiver パラメータが常に現在の Proxy インスタンス (つまり、上記の例では obj) を指しており、Reflect.setreceiver に渡されると、属性には receiver (つまり obj) が割り当てられ、defineProperty インターセプトがトリガーされます。 Reflect.setreceiver に渡されない場合、defineProperty インターセプトはトリガーされません。

p = { とします
  a:「あ」
};

let ハンドラー = {
  set(ターゲット、キー、値、受信者) {
    console.log('セット');
    Reflect.set(ターゲット、キー、値)
  }、
  defineProperty(ターゲット、キー、属性) {
    console.log('定義プロパティ');
    Reflect.defineProperty(ターゲット、キー、属性);
  }
};

let obj = 新しいプロキシ(p, ハンドラー);
obj.a = 'A';
//セット

最初のパラメータがオブジェクトでない場合、Reflect.set はエラーを報告します。

Reflect.set(1, 'foo', {}) // エラーを報告する
Reflect.set(false, 'foo', {}) // エラーを報告する

Reflect.has(obj, 名前)

Reflect.has メソッドは、name in objin 演算子に対応します。

var myObject = {
  フー: 1、
};

//古い書き方
myObject の 'foo' // true

//新しい書き方
Reflect.has(myObject, 'foo') // true

Reflect.has() メソッドの最初のパラメータがオブジェクトでない場合、エラーが報告されます。

Reflect.deleteProperty(obj, 名前)

Reflect.deleteProperty メソッドは delete obj[name] と同等であり、オブジェクトのプロパティを削除するために使用されます。

const myObj = { foo: 'bar' };

//古い書き方
myObj.fooを削除します。

//新しい書き方
Reflect.deleteProperty(myObj, 'foo');

このメソッドはブール値を返します。削除が成功した場合、または削除された属性が存在しない場合は、「true」を返します。削除が失敗し、削除された属性がまだ存在する場合は、「false」を返します。

Reflect.deleteProperty() メソッドの最初のパラメータがオブジェクトでない場合、エラーが報告されます。

Reflect.construct(ターゲット, 引数)

Reflect.construct メソッドは new target(...args) と同等であり、new を使用せずにコンストラクターを呼び出す方法を提供します。

関数 挨拶(名前) {
  this.name = 名前;
}

// 新規の書き方
const インスタンス = new Greeting('張三');

// Reflect.constructの書き方
const インスタンス = Reflect.construct(挨拶, ['張三']);

Reflect.construct() メソッドの最初のパラメータが関数でない場合、エラーが報告されます。

Reflect.getPrototypeOf(obj)

Reflect.getPrototypeOf メソッドは、Object.getPrototypeOf(obj) に対応するオブジェクトの __proto__ 属性を読み取るために使用されます。

const myObj = 新しい FancyThing();

//古い書き方
Object.getPrototypeOf(myObj) === FancyThing.prototype;

//新しい書き方
Reflect.getPrototypeOf(myObj) === FancyThing.prototype;

「Reflect.getPrototypeOf」と「Object.getPrototypeOf」の違いの 1 つは、パラメータがオブジェクトでない場合、「Object.getPrototypeOf」はパラメータをオブジェクトに変換して実行するのに対し、「Reflect.getPrototypeOf」はエラー。

Object.getPrototypeOf(1) // 数値 {[[PrimitiveValue]]: 0}
Reflect.getPrototypeOf(1) // エラーレポート

Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOf メソッドは、Object.setPrototypeOf(obj, newProto) メソッドに対応する、ターゲット オブジェクトのプロトタイプを設定するために使用されます。設定が成功したかどうかを示すブール値を返します。

const myObj = {};

//古い書き方
Object.setPrototypeOf(myObj, Array.prototype);

//新しい書き方
Reflect.setPrototypeOf(myObj, Array.prototype);

myObj.長さ // 0

対象オブジェクトのプロトタイプを設定できない場合(対象オブジェクトが拡張を禁止している場合など)、Reflect.setPrototypeOfメソッドはfalseを返します。

Reflect.setPrototypeOf({}, null)
// 真実
Reflect.setPrototypeOf(Object.freeze({}), null)
// 間違い

最初のパラメータがオブジェクトでない場合、Object.setPrototypeOf は最初のパラメータそのものを返し、Reflect.setPrototypeOf はエラーを報告します。

Object.setPrototypeOf(1, {})
// 1

Reflect.setPrototypeOf(1, {})
// TypeError: 非オブジェクトで呼び出された Reflect.setPrototypeOf

最初のパラメータが「unknown」または「null」の場合、「Object.setPrototypeOf」と「Reflect.setPrototypeOf」の両方がエラーを報告します。

Object.setPrototypeOf(null, {})
// TypeError: null または未定義で呼び出された Object.setPrototypeOf

Reflect.setPrototypeOf(null, {})
// TypeError: 非オブジェクトで呼び出された Reflect.setPrototypeOf

Reflect.apply(func, thisArg, args)

Reflect.apply メソッドは、Function.prototype.apply.call(func, thisArg, args) と同等であり、this オブジェクトをバインドした後に指定された関数を実行するために使用されます。

一般的に、関数の this オブジェクトをバインドしたい場合は、次のように fn.apply(obj, args) を記述できますが、関数が独自の apply メソッドを定義している場合は、それを記述することしかできません。 Function.prototype .apply.call(fn, obj, args) のように、Reflect オブジェクトを使用すると、この操作を簡素化できます。

定数 = [11, 33, 12, 54, 18, 96];

//古い書き方
const youngest = Math.min.apply(Math, 年齢);
const 最も古い = Math.max.apply(Math, 年齢);
const type = Object.prototype.toString.call(末っ子);

//新しい書き方
const youngest = Reflect.apply(Math.min, Math, 年齢);
const 最も古い = Reflect.apply(Math.max, Math, 年齢);
const type = Reflect.apply(Object.prototype.toString, youngest, []);

Reflect.defineProperty(ターゲット、プロパティキー、属性)

Reflect.defineProperty メソッドは基本的に Object.defineProperty と同等であり、オブジェクトのプロパティを定義するために使用されます。将来的には、後者は徐々に非推奨になる予定です。今後は代わりに Reflect.defineProperty を使用してください。

関数 MyDate() {
  /*…*/
}

//古い書き方
Object.defineProperty(MyDate, 'now', {
  値: () => Date.now()
});

//新しい書き方
Reflect.defineProperty(MyDate, 'now', {
  値: () => Date.now()
});

Reflect.defineProperty の最初のパラメータがオブジェクトでない場合、Reflect.defineProperty(1, 'foo') などのエラーがスローされます。

このメソッドは「Proxy.defineProperty」と組み合わせて使用​​できます。

const p = 新しいプロキシ({}, {
  defineProperty(ターゲット、プロップ、記述子) {
    console.log(記述子);
    return Reflect.defineProperty(ターゲット、プロップ、記述子);
  }
});

p.foo = 'バー';
// {値: "bar"、書き込み可能: true、列挙可能: true、構成可能: true}

p.foo // "バー"

上記のコードでは、Proxy.defineProperty がプロパティ割り当てのインターセプトを設定し、Reflect.defineProperty を使用して割り当てを完了します。

Reflect.getOwnPropertyDescriptor(ターゲット, propertyKey)

Reflect.getOwnPropertyDescriptor は基本的に Object.getOwnPropertyDescriptor と同等で、指定されたプロパティの説明オブジェクトを取得するために使用され、将来的には後者に置き換わります。

var myObject = {};
Object.defineProperty(myObject, 'hidden', {
  値: true、
  列挙可能: false、
});

//古い書き方
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden');

//新しい書き方
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');

Reflect.getOwnPropertyDescriptorObject.getOwnPropertyDescriptor の違いの 1 つは、最初のパラメータがオブジェクトでない場合、Object.getOwnPropertyDescriptor(1, 'foo') はエラーを報告せず、unknown を返すのに対し、 Reflect.getOwnPropertyDescriptor( 1, 'foo') は、パラメーターが不正であることを示すエラーをスローします。

Reflect.isExtensible (ターゲット)

「Reflect.isExtensible」メソッドは「Object.isExtensible」に対応し、現在のオブジェクトが拡張可能かどうかを示すブール値を返します。

const myObject = {};

//古い書き方
Object.isExtensible(myObject) // true

//新しい書き方
Reflect.isExtensible(myObject) // true

パラメータがオブジェクトでない場合、非オブジェクトは本質的に拡張不可能であるため、Object.isExtensiblefalse を返し、Reflect.isExtensible はエラーを報告します。

Object.isExtensible(1) // false
Reflect.isExtensible(1) // エラーを報告する

Reflect.preventExtensions(ターゲット)

Reflect.preventExtensions は、オブジェクトを拡張不可能にするために使用される Object.preventExtensions メソッドに対応します。操作が成功したかどうかを示すブール値を返します。

var myObject = {};

//古い書き方
Object.preventExtensions(myObject) // オブジェクト {}

//新しい書き方
Reflect.preventExtensions(myObject) // true

パラメータがオブジェクトでない場合、ES5 環境では Object.preventExtensions がエラーを報告し、ES6 環境では渡されたパラメータを返し、Reflect.preventExtensions がエラーを報告します。

// ES5環境
Object.preventExtensions(1) // エラーレポート

// ES6環境
Object.preventExtensions(1) // 1

//新しい書き方
Reflect.preventExtensions(1) // エラーレポート

Reflect.ownKeys (ターゲット)

Reflect.ownKeys メソッドはオブジェクトのすべてのプロパティを返すために使用されます。これは基本的に Object.getOwnPropertyNamesObject.getOwnPropertySymbols の合計と同等です。

var myObject = {
  フー: 1、
  バー: 2、
  [Symbol.for('baz')]: 3、
  [Symbol.for('bing')]: 4、
};

//古い書き方
Object.getOwnPropertyNames(myObject)
// ['foo', 'bar']

Object.getOwnPropertySymbols(myObject)
//[シンボル(baz), シンボル(bing)]

//新しい書き方
Reflect.ownKeys(myObject)
// ['foo', 'bar', Symbol(baz), Symbol(bing)]

Reflect.ownKeys() メソッドの最初のパラメータがオブジェクトでない場合、エラーが報告されます。

例: プロキシを使用してオブザーバー パターンを実装する

オブザーバー モードは、データ オブジェクトが変更されると自動的に実行されるデータ オブジェクトを自動的に監視する機能を指します。

const person = 観察可能({
  名前:「チャン・サン」、
  年齢: 20歳
});

関数 print() {
  console.log(`${人名}, ${人.年齢}`)
}

観察(印刷);
person.name = '李思';
//出力
// ジョン・ドゥ、20歳

上記のコードでは、データオブジェクト「person」が観測対象、関数「print」が観測者となっています。データオブジェクトが変更されると、p​​rint が自動的に実行されます。

次に、Proxy を使用してオブザーバー パターンの最も単純な実装、つまり 2 つの関数 observableobserve を実装します。その考え方は、「observable」関数が元のオブジェクトの Proxy プロキシを返し、代入操作をインターセプトし、オブザーバーとして機能するさまざまな関数をトリガーするというものです。

const queuedObservers = new Set();

constobserver = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});

関数セット(ターゲット、キー、値、受信者) {
  const result = Reflect.set(ターゲット、キー、値、受信者);
  queuedObservers.forEach(observer => observer());
  結果を返します。
}

上記のコードでは、最初に Set コレクションが定義され、すべてのオブザーバー関数がこのセットに入れられます。次に、「observable」関数は元のオブジェクトのプロキシを返し、割り当てをインターセプトします。インターセプト関数 set では、すべてのオブザーバーが自動的に実行されます。


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0