【JavaScript】Reflectの使い方「Reflect | Metaprogramming in ES6: Part 2 – Reflect」の記事概要

【JavaScript】Reflectの使い方「Reflect | Metaprogramming in ES6: Part 2 - Reflect」の記事概要

【JavaScript】Reflectの使い方「Reflect | Metaprogramming in ES6: Part 2 - Reflect」の記事概要

【JavaScript】Reflectの使い方「Reflect | Metaprogramming in ES6: Part 2 - Reflect」の記事概要

以下ここの記事の概要

------------
ReflectはJSONやMathのようなグローバルオブジェクトです。

introspection methods(内観メソッドというのは Object.keys, Object.getOwnPropertyNamesなどと同じ)

なぜこれらが新しいAPIとして追加される必要があったのでしょうか

Internal Methods

すべてのJavaScript仕様で、一連の内部メソッドは付属しています

それらの内部メソッドはあなたのコード周りの望みとしてオブジェクト上の不可欠なJavaScriptプラットフォームを効果的に操作します。
もしあなたが仕様を読み通したら、あなたはいたるところに
[[Get]],[[Set]], [[HasOwnProperty]]
のような物を見つけるでしょう

これらの多くの内部メソッドはJavaScriptコードから隠れていて、
多くのメソッドによって部分的に適用されています。
もし内部メソッドが利用可能なら、それらはさまざまな内部的な隙間に隠されています。
例えばObject.prototype.hasOwnPropertyは[[HasOwnProperty]]の実装です。
全ての「オブジェクト」がObject継承されているのではないので、
その場合、複雑な「おまじない」をかけて実行しなければなりません。

[code language="javascript"]
var myObject = Object.create(null);
// Happens more often than you might think (especially with new ES6 classes)
//思ったより頻繁に発生します。(特にES6 classで)
assert(myObject.hasOwnProperty === undefined);
// If you want to use hasOwnProperty on `myObject`:
もしmyObject上でhasOwnPropertyを使いたかったら、、
Object.prototype.hasOwnProperty.call(myObject, 'foo');
[/code]

違う例で、
[[OwnPropertyKeys]]は
全てのObjectの文字列keyとシンボルkeyを配列で受け取ります。
これらを受け取る唯一の方法は(Reflect以外で)
Object.getOwnPropertyNamesとObject.getOwnPropertySymbolsの
結果を結合させることです。

[code language="javascript"]
var s = Symbol('foo');
var k = 'bar';
var o = { [s]: 1, [k]: 1 };
// Simulate [[OwnPropertyKeys]]//擬似内部メソッド
var keys = Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o));
//唯一の方法
assert.deepEqual(keys, [k, s]);
[/code]

Reflect methods

ReflectはそれらのJavaScriptの内部エンジンを通して利用可能な内部メソッドの全てのコレクションに効果的です。
あなたは「でもなぜ
Object.keys、Object.getOwnPropertyNamesなど
のようなObjectへそれらを追加調整したなったの?」と思っているかもしれません。

なぜか

1.Reflectはオブジェクトに対してだけの意味ではなく例えばReflect.applyのような関数をターゲットにしてメソッドを持ちます。
2 .それらの単一オブジェクトをもつことはこれらのメソッドを収容することは
残りのJSを綺麗に保つ良い方法です

3.typeof , instance, and delete はすでにReflectionのnew キーワードとして追加で
存在している
開発者に対して面倒なものだけではなく
互換性とそれらの予約語の数を送るものとしてではなく

Reflect.apply (target, thisArgument [, argumentLst])

Reflect.applyはかなりFunction#applyに合っています
それは関数をとり、コンテキストでそれを呼び、
配列の引数をとります

この重要な形式はFunction#call/Fuction#applyバージョンは非奨励として考慮することができます

これは下のような考えではなく、それをセンスよくさせるように
どのように使うか示します

[code language="javascript"]
var ages = [11, 33, 12, 54, 18, 96];

// Function.prototype style:
var youngest = Math.min.apply(Math, ages);
var oldest = Math.max.apply(Math, ages);
var type = Object.prototype.toString.call(youngest);

// Reflect style:
var youngest = Reflect.apply(Math.min, Math, ages);
var oldest = Reflect.apply(Math.max, Math, ages);
var type = Reflect.apply(Object.prototype.toString, youngest);
[/code]

Function.prototype.applyに対してのReflect applyを実際の恩恵は
どのコードも callやapplyメソッドのつまらない変更でき、
壊れたコードやそのまわりの恐ろしいコードから離れることができます
これは本当に現実の世界では実際に大きな問題になってしまうかもしれませんが
以下のコードは確かに存在します

[code language="javascript"]
function totalNumbers() {
return Array.prototype.reduce.call(arguments, function (total, next) {
return total + next;
}, 0);
}
totalNumbers.apply = function () {
throw new Error('Aha got you!');
}

totalNumbers.apply(null, [1, 2, 3, 4]);
// throws Error('Aha got you!');

// The only way to defensively do this in ES5 code is horrible:
Function.prototype.apply.call(totalNumbers, null, [1, 2, 3, 4]) === 10;

//You could also do this, which is still not much cleaner:
Function.apply.call(totalNumbers, null, [1, 2, 3, 4]) === 10;

// Reflect.apply to the rescue!
Reflect.apply(totalNumbers, null, [1, 2, 3, 4]) === 10;
[/code]

Reflect.construct (target, argumentsList [, constructorToCreateThis])

Reflect.applyに似ています
これはあなたにコンストラクタを引数と一緒に呼ぶことができます。
Reflect.applyはClassで動き、
コンストラクタがもつprorotypeでのthisオブジェクトのような「正確なオブジェクト」をセットアップするため
に動きます。

ES5ではObject.create(Constructor.prototype)使い、Constructor.callやConstructor.applyにthisを渡していました
Reflect.constructとの違いは、
オブジェクトを渡すことよりもさらに、
ただコンストラクタをわたして、
Reflect.constructは全てをハンドリングできることです。

古いスタイルではこれはかなり効率が悪いことで、
この新しいスタイルはより簡潔に、一行で可能にします。

[code language="javascript"]
class Greeting {

constructor(name) {
this.name = name;
}

greet() {
return `Hello ${name}`;
}

}

// ES5 style factory:
function greetingFactory(name) {
var instance = Object.create(Greeting.prototype);
Greeting.call(instance, name);
return instance;
}

// ES6 style factory
function greetingFactory(name) {
return Reflect.construct(Greeting, [name], Greeting);
}

// Or, omit the third argument, and it will default to the first argument.
function greetingFactory(name) {
return Reflect.construct(Greeting, [name]);
}

// Super slick ES6 one liner factory function!
const greetingFactory = (name) => Reflect.construct(Greeting, [name]);

[/code]

Reflect.defineProperty(target, propertyKey, attributes)

Reflect.defineProperty は かなりObject.definePropertyから引き継いだものに合います
Reflect.definePropertyはあなたにプロパティについてのメタデータを定義させます。

ObjectがReflectを実行していることを鮮明にするため、Reflect.definePropertyがある間、
あなたがしていることはReflectionということをよりほのめかし、意味的にします。

大事なことはReflect.definePropertyは 拒否されたtarget(数値型か文字列のプリミティブ達のような)に対してTypeErrorを投げます。
Reflect.defineProperty(1, ‘foo')
これは良いことです。
なぜなら悪い引数の型に対してエラーを投げることは黙って失敗するよりもより問題を
あなたに気づかせてくれるからです。

もう一度言います
あなたはObject.definePropertyについてここから非奨励とされることをかなり考慮することになります
代わりにReflect.definePropertyを使ってください。

[code language="javascript"]
function MyDate() {
/*…*/
}

// Old Style, weird because we're using Object.defineProperty to define
// a property on Function (why isn't there a Function.defineProperty?)
Object.defineProperty(MyDate, 'now', {
value: () => currentms
});

// New Style, not weird because Reflect does Reflection.
Reflect.defineProperty(MyDate, 'now', {
value: () => currentms
});
[/code]

Reflect.getOwnPropertyDescripter(target, propertyKey)

これはもう一度、
Object.getOwnPropertyDescriptorに置き換わります。
プロパティのディスクリプターメタデータになります。
この違いは
Object.getOwnPropertyDescriptor(1, ‘foo’)は静かに失敗し、
undefinedを返していて、Reflect.getOwnPropertyDescripotr(1, ‘foo’)は
TypeErrorを投げます

Reflect.definePropertyと同じように引数が拒否されたことに対して投げます

[code language="javascript"]
var myObject = {};
Object.defineProperty(myObject, 'hidden', {
value: true,
enumerable: false,
});
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');
assert.deepEqual(theDescriptor, { value: true, enumerable: true });

// Old style
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden');
assert.deepEqual(theDescriptor, { value: true, enumerable: true });

assert(Object.getOwnPropertyDescriptor(1, 'foo') === undefined)
Reflect.getOwnPropertyDescriptor(1, 'foo');
// throws TypeError
[/code]

Reflect.deleteProperty (target, propertyKey)

Reflect.deletePropertyは驚き驚きで、
targetオブジェクトのプロパティを削除します
ES6以前は
あなたは典型的にdelete obj.fooと書いて、
今になってはあなたはReflect.deleteProperty(obj, ‘foo’)と書くことができます。

これは僅かにより冗長で、キーワードを削除することの意味は若干違い、
でも基本的にオブジェクトに対して同じ影響を与えます

両方とも内部[target[Delete]](propertyKey)メソッドを呼びますが
delete操作は非オブジェクト参照に対しても実行します。

それはより渡されたオペランドをよりチェックします。

[code language="javascript"]
var myObj = { foo: 'bar' };
delete myObj.foo;
assert(myObj.hasOwnProperty('foo') === false);

myObj = { foo: 'bar' };
Reflect.deleteProperty(myObj, 'foo');
assert(myObj.hasOwnProperty('foo') === false);
[/code]

あなたが削除したい場合、
プロパティを削除するための新しい方法になります。
これはより削除することの意思をはっきりさせます。

Reflect.getPrototypeOf (target)

この置換するかオブジェクトメソッドを続けるかのテーマは
Object.getPrototypeOfの番です。

それ自体兄弟のようで、
新しいReflect.getPrototypeOfメソッドはtargetに数値型か文字リテラル、null、undefined
のようなものを与えるとTypeErrorを投げます。

Object.getPrototypeOfはtargetに’a’のようなオブジェクトにObject(‘a’)になるように強制させます
そうではない構文は全く同じです。

[code language="javascript"]
var myObj = new FancyThing();
assert(Reflect.getPrototypeOf(myObj) === FancyThing.prototype);

// Old style
assert(Object.getPrototypeOf(myObj) === FancyThing.prototype);

Object.getPrototypeOf(1); // undefined
Reflect.getPrototypeOf(1); // TypeError
[/code]

Reflect.setPrototypeOf (target, proto)

もちろんあなたはgetPrototypeOfをsetPrototypeOfなしで持つことはできません。
今Object.setPrototypeOfは非オブジェクトに対して投げ、
非オブジェクトは強制的にObjectの中に与えられた引数にさせ、。//Object(‘a')

もし[[SetPrototype]]内部操作が失敗しても
TypeErrorを投げ、targetの引数を返すように続けます。

Reflect.setPropertyOfはより基本的に
もし非オブジェクトが渡されたらTypeErrorを投げますが、
それ以上は他にありません
それは操作が成功したかの指し示しているBooleanとしての[[SetPrototypeOf]]の結果を返し、
これは役立ちます。
なぜなら
成果を他のどのTypeErrorをキャッチもできるtry/catchを使うことより管理できるからです

[code language="javascript"]
var myObj = new FancyThing();
assert(Reflect.setPrototypeOf(myObj, OtherThing.prototype) === true);
assert(Reflect.getPrototypeOf(myObj) === OtherThing.prototype);

// Old style
assert(Object.setPrototypeOf(myObj, OtherThing.prototype) === myObj);
assert(Object.getPrototypeOf(myObj) === FancyThing.prototype);

Object.setPrototypeOf(1);
// TypeError
Reflect.setPrototypeOf(1);
// TypeError

var myFrozenObj = new FancyThing();
Object.freeze(myFrozenObj);

Object.setPrototypeOf(myFrozenObj);
// TypeError
assert(Reflect.setPrototypeOf(myFrozenObj) === false);
[/code]

Reflect.isExtensible(target)

じゃあ、
もいいちど、
これはObject.isExtensibleの代わりの一つです。

でもそれ自体ちょっとES6以前より複雑化されています。

Object.isExtensibleはTypeErrorを投げます。
もし非オブジェクト(typeof target !== ‘object’)のようなら

ES6構文は非オブジェクトの中で渡しているこれを変えました。
Object.isExtensibleはfalesを返すでしょう。
なぜなら非オブジェクトは全て拡張不可だからです

このように
Object.isExtensible(1) === false は投げます。
一方ES6はこのあなたがtrueと評価すると期待したような構文を実行します

このポイントは
Reflect.isExtensibleは古い振る舞いを使い、
非オブジェクトを通すことです

どうしてそうするのか確かではないが
これは技術的に
Reflect.isExtensibleはObject.isExtensibleに対して構文を変えますが、
Object.isExtensibleはとにかく変わりました。

ここに示します

[code language="javascript"]
var myObject = {};
var myNonExtensibleObject = Object.preventExtensions({});

assert(Reflect.isExtensible(myObject) === true);
assert(Reflect.isExtensible(myNonExtensibleObject) === false);
Reflect.isExtensible(1);
// throws TypeError
Reflect.isExtensible(false);
// throws TypeError

// Using Object.isExtensible
assert(Object.isExtensible(myObject) === true);
assert(Object.isExtensible(myNonExtensibleObject) === false);

// ES5 Object.isExtensible semantics
Object.isExtensible(1);
// throws TypeError on older browsers
Object.isExtensible(false);
// throws TypeError on older browsers

// ES6 Object.isExtensible semantics
assert(Object.isExtensible(1) === false);
// only on newer browsers
assert(Object.isExtensible(false) === false);
// only on newer
[/code]

Reflect.preventExtensions (target)

これはObjectから借りているReflection Objectの中で最後のメソッドです。
preventExtensionsはReflect.isExtensibleとして同じストーリーに従います
ES5のObject.preventExtensionsは非オブジェクト上でthrowするために使います。
でもES6では値が返されます
Reflect.preventExtensionsは単にtrueかfalseを返し、オペレーションの成果に依存していて、
失敗時のシナリオをハンドルすることを可能にします。

[code language="javascript"]
var myObject = {};
var myObjectWhichCantPreventExtensions = magicalVoodooProxyCode({});

assert(Reflect.preventExtensions(myObject) === true);
assert(Reflect.preventExtensions(myObjectWhichCantPreventExtensions) === false);
Reflect.preventExtensions(1);
// throws TypeError
Reflect.preventExtensions(false);
// throws TypeError

// Using Object.isExtensible
assert(Object.isExtensible(myObject) === true);
Object.isExtensible(myObjectWhichCantPreventExtensions); // throws TypeError

// ES5 Object.isExtensible semantics
Object.isExtensible(1);
// throws TypeError
Object.isExtensible(false);
// throws TypeError

// ES6 Object.isExtensible semantics
assert(Object.isExtensible(1) === false);
assert(Object.isExtensible(false) === false);
[/code]

Reflect.enumerate (target)

更新: これはES7で削除されました。
myObject[Symbol.iterator]()は現在Object.keysかvalueを列挙する唯一の方法です。

[code language="javascript"]
var myArray = [1, 2, 3];
myArray[Symbol.enumerate] = function () {
throw new Error('Nope!');
}
for (let item of myArray) {
// error thrown: Nope!
}
for (let item of Reflect.enumerate(myArray)) {
// 1 then 2 then 3
}
[/code]

Reflect.get(target, propertyKey [, receiver])

Reflect.getは完全に新しいメソッドです。
これはかなりシンプルなメソッドで
target[propertyKey]を有効的に呼びます。
もしtargetが非オブジェクトなら
関数コールはあなたが暗黙的にundefinedを返す1[‘foo’]のような何かをしたらそれはthrowされます
Reflect.get(1,’foo’)はTypeErrorをthrowし、
Reflect.getの興味深い一つはもしtarget[propertyKey]がゲッター関数ならthis引数として実行可能に不可欠な引数を受け取ります

[code language="javascript"]
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
}

assert(Reflect.get(myObject, 'foo') === 1);
assert(Reflect.get(myObject, 'bar') === 2);
assert(Reflect.get(myObject, 'baz') === 3);
assert(Reflect.get(myObject, 'baz', myObject) === 3);

var myReceiverObject = {
foo: 4,
bar: 4,
};
assert(Reflect.get(myObject, 'baz', myReceiverObject) === 8);

// Non-objects throw:
Reflect.get(1, 'foo');
// throws TypeError
Reflect.get(false, 'foo');
// throws TypeError

// These old styles don't throw:
assert(1['foo'] === undefined);
assert(false['foo'] === undefined);
[/code]

Reflect.set (target, propertyKey, V [, receiver])

あなたは多分このメソッドが何をするか推測できるはずです
これはReflect.getの兄弟であり、
これはsetするための値、一つ引数をとります、
Reflect.get, Reflect.setは非オブジェクト上でthrowし、もしtarget[propertyKey]がsetter関数ならthis値として動く特別な引数を受け取ります
必須のコードは例えば、、

[code language="javascript"]
var myObject = {
foo: 1,
set bar(value) {
return this.foo = value;
},
}

assert(myObject.foo === 1);
assert(Reflect.set(myObject, 'foo', 2));
assert(myObject.foo === 2);
assert(Reflect.set(myObject, 'bar', 3));
assert(myObject.foo === 3);
assert(Reflect.set(myObject, 'bar', myObject) === 4);
assert(myObject.foo === 4);

var myReceiverObject = {
foo: 0,
};
assert(Reflect.set(myObject, 'bar', 1, myReceiverObject));
assert(myObject.foo === 4);
assert(myReceiverObject.foo === 1);

// Non-objects throw:
Reflect.set(1, 'foo', {});
// throws TypeError
Reflect.set(false, 'foo', {});
// throws TypeError

// These old styles don't throw:
1['foo'] = {};
false['foo'] = {};
assert(1['foo'] === undefined);
assert(false['foo'] === undefined);

[/code]

Reflect.has ( target, propertyKey )

Reflect.has は興味深い一つです
なぜならこれは機能性としてinオペレーターと同じで不可欠だからです
両方とも[[HasProperty]]内部メソッドを使い、両方とももしtargetがオブジェクトでなかったらthrowします
inを関数呼び出しのスタイルを好まないということ以外でReflect.hasを使うポイントがあります

それは言語の他の部分の中で使う重要なものをもっています
次の投稿で明らかになるでしょう
とにかく
使い方を示します

[code language="javascript"]
myObject = {
foo: 1,
};
Object.setPrototypeOf(myObject, {
get bar() {
return 2;
},
baz: 3,
});

// Without Reflect.has
assert(('foo' in myObject) === true);
assert(('bar' in myObject) === true);
assert(('baz' in myObject) === true);
assert(('bing' in myObject) === false);

// With Reflect.has:
assert(Reflect.has(myObject, 'foo') === true);
assert(Reflect.has(myObject, 'bar') === true);
assert(Reflect.has(myObject, 'baz') === true);
assert(Reflect.has(myObject, 'bing') === false);
[/code]

Reflect.ownKeys (target)

これはすでに議論されてきました
Reflect.ownKeysはObject.getOwnPropertyNamesとObject.getOwnPropertySymbolsの結合です。
もしあなたが再呼び出しする[[OwnPropertyKeys]]場合。
これはReflect.ownKeysを独自に有効にします。

[code language="javascript"]
var myObject = {
foo: 1,
bar: 2,
[Symbol.for('baz')]: 3,
[Symbol.for('bing')]: 4,
};

assert.deepEqual(Object.getOwnPropertyNames(myObject), ['foo', 'bar']);
assert.deepEqual(Object.getOwnPropertySymbols(myObject), [Symbol.for('baz'), Symbol.for('bing')]);

// Without Reflect.ownKeys:
var keys = Object.getOwnPropertyNames(myObject).concat(Object.getOwnPropertySymbols(myObject));
assert.deepEqual(keys, ['foo', 'bar', Symbol.for('baz'), Symbol.for('bing')]);

// With Reflect.ownKeys:
assert.deepEqual(Reflect.ownKeys(myObject), ['foo', 'bar', Symbol.for('baz'), Symbol.for('bing')]);
[/code]

結び

私たちはReflectメソッドについてかなり徹底的に行ってきました
見てきた多くのものは共通で存在するメソッドの新しいバージョンです
時々少し調整、時々全く新しいメソッドでした
JavaScript内でのReflectの新しいレベルを可能にします
もしあなたがObject.*/ Function.*メソッドそれにかわる新しいReflectを使うことが完全にできたら
もう何もいらないでしょう
何も悪いことは起こらないでしょう。
--------------------------------------------------

【その他参考になるReflect記事】
es6-reflection-in-depth

Mozilla | Reflect

他の海外参照記事