Reflect
Обзор
Reflectобъект сProxyКак и объекты, это также новый API, предоставляемый ES6 для управления объектами.ReflectОбъекты предназначены для нескольких целей.
(1) будетObjectНекоторые методы, принадлежащие к чистоту внутреннего языка объекта (например,Object.defineProperty), вставитьReflectна объекте. На этом этапе некоторые методыObjectа такжеReflectобъект, будущие новые методы будут развернуты только наReflectна объекте. То есть изReflectОбъекты имеют доступ к внутренним методам языка.
(2) Изменить некоторыеObjectВозвращаемый результат метода делает его более разумным. Например,Object.defineProperty(obj, name, desc)Ошибка выдается, когда свойство не может быть определено, иReflect.defineProperty(obj, name, desc)вернусьfalse.
// 老写法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// failure
}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
(3) пустьObjectОперации становятся функциональным поведением. немногоObjectОперации обязательны, напримерname in objа такжеdelete obj[name],а такжеReflect.has(obj, name)а такжеReflect.deleteProperty(obj, name)Сделайте их функциональным поведением.
// 老写法
'assign' in Object // true
// 新写法
Reflect.has(Object, 'assign') // true
(4)Reflectметоды объекта иProxyМетоды объекта соответствуют один к одному, пока онProxyметоды объекта, вы можетеReflectНайдите соответствующий метод на объекте. Это делаетProxyОбъект может легко вызвать соответствующийReflectметод, который завершает поведение по умолчанию, в качестве основы для изменения поведения. То есть независимо отProxyКак изменить поведение по умолчанию, вы всегда можетеReflectчтобы получить поведение по умолчанию.
Proxy(target, {
set: function(target, name, value, receiver) {
var success = Reflect.set(target, name, value, receiver);
if (success) {
console.log('property ' + name + ' on ' + target + ' set to ' + value);
}
return success;
}
});
В приведенном выше кодеProxyперехват методаtarget对象的属性赋值行为。 он принимаетReflect.setМетоды присваивают значения свойствам объекта, гарантируя, что исходное поведение выполняется до развертывания дополнительных функций.
Ниже приведен еще один пример.
var loggedObj = new Proxy(obj, {
get(target, name) {
console.log('get', target, name);
return Reflect.get(target, name);
},
deleteProperty(target, name) {
console.log('delete' + name);
return Reflect.deleteProperty(target, name);
},
has(target, name) {
console.log('has' + name);
return Reflect.has(target, name);
}
});
В приведенном выше коде каждыйProxyОперация перехвата объекта (get,delete,has), внутренне вызывает соответствующийReflectметод, чтобы убедиться, что собственное поведение может выполняться нормально. Дополнительная работа заключается в выводе строки журнала для каждой операции.
имеютReflectОбъекты позже, многие операции будут более читаемыми.
// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1
// 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1
статический метод
ReflectОбъект имеет в общей сложности 13 статических методов.
- Reflect.apply(target, thisArg, args)
- Reflect.construct(target, args)
- Reflect.get(target, name, receiver)
- Reflect.set(target, name, value, receiver)
- Reflect.defineProperty(target, name, desc)
- Reflect.deleteProperty(target, name)
- Reflect.has(target, name)
- Reflect.ownKeys(target)
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target)
- Reflect.setPrototypeOf(target, prototype)
Эффекты вышеперечисленных методов в основном связаны сObjectМетод объекта с тем же именем имеет тот же эффект, и он такой же, какProxyСуществует однозначное соответствие между методами объекта. Ниже приводится их объяснение.
Reflect.get(target, name, receiver)
Reflect.getметод найти и вернутьtargetобъектnameатрибут или, если такого атрибута нет, вернутьundefined.
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
}
Reflect.get(myObject, 'foo') // 1
Reflect.get(myObject, 'bar') // 2
Reflect.get(myObject, 'baz') // 3
еслиnameСвойство развертывает функцию чтения (геттер), функция чтенияthisсвязыватьreceiver.
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
};
var myReceiverObject = {
foo: 4,
bar: 4,
};
Reflect.get(myObject, 'baz', myReceiverObject) // 8
Если первый аргумент не является объектом,Reflect.getметод сообщит об ошибке.
Reflect.get(1, 'foo') // 报错
Reflect.get(false, 'foo') // 报错
Reflect.set(target, name, value, receiver)
Reflect.setнастройки методаtargetобъектnameсвойство равноvalue.
var myObject = {
foo: 1,
set bar(value) {
return this.foo = value;
},
}
myObject.foo // 1
Reflect.set(myObject, 'foo', 2);
myObject.foo // 2
Reflect.set(myObject, 'bar', 3)
myObject.foo // 3
еслиnameСвойство задает функцию присваивания, значение присваиваетсяthisсвязыватьreceiver.
var myObject = {
foo: 4,
set bar(value) {
return this.foo = value;
},
};
var myReceiverObject = {
foo: 0,
};
Reflect.set(myObject, 'bar', 1, myReceiverObject);
myObject.foo // 4
myReceiverObject.foo // 1
Обратите внимание, что еслиProxyобъект иReflectОбъекты используются в комбинации, первый перехватывает операцию присваивания, последний завершает поведение присваивания по умолчанию и передает вreceiver,ТакReflect.setвызоветProxy.definePropertyперехват.
let p = {
a: 'a'
};
let handler = {
set(target, key, value, receiver) {
console.log('set');
Reflect.set(target, key, value, receiver)
},
defineProperty(target, key, attribute) {
console.log('defineProperty');
Reflect.defineProperty(target, key, attribute);
}
};
let obj = new Proxy(p, handler);
obj.a = 'A';
// set
// defineProperty
В приведенном выше кодеProxy.setиспользуется для перехватаReflect.set, и прошел вreceiver, вызывая триггерProxy.definePropertyперехват. Это потому чтоProxy.setизreceiverПараметр всегда указывает на текущийProxyэкземпляр (то естьobj),а такжеReflect.setОднажды прошел вreceiver, свойство будет присвоеноreceiverвыше (т.е.obj), вызывая триггерdefinePropertyперехват. еслиReflect.setнет входящихreceiver, то не сработаетdefinePropertyперехват.
let p = {
a: 'a'
};
let handler = {
set(target, key, value, receiver) {
console.log('set');
Reflect.set(target, key, value)
},
defineProperty(target, key, attribute) {
console.log('defineProperty');
Reflect.defineProperty(target, key, attribute);
}
};
let obj = new Proxy(p, handler);
obj.a = 'A';
// set
Если первый аргумент не является объектом,Reflect.setсообщит об ошибке.
Reflect.set(1, 'foo', {}) // 报错
Reflect.set(false, 'foo', {}) // 报错
Reflect.has(obj, name)
Reflect.hasметод соответствияname in objвнутриinоператор.
var myObject = {
foo: 1,
};
// 旧写法
'foo' in myObject // true
// 新写法
Reflect.has(myObject, 'foo') // true
еслиReflect.has()Первый параметр метода не является объектом, и будет сообщено об ошибке.
Reflect.deleteProperty(obj, name)
Reflect.deletePropertyметод эквивалентенdelete obj[name], который используется для удаления свойства объекта.
const myObj = { foo: 'bar' };
// 旧写法
delete myObj.foo;
// 新写法
Reflect.deleteProperty(myObj, 'foo');
Этот метод возвращает логическое значение. Если удаление прошло успешно или удаленное свойство не существует, вернутьtrue;удаление не удалось, удаленное свойство все еще существует, вернутьfalse.
еслиReflect.deleteProperty()Первый параметр метода не является объектом, и будет сообщено об ошибке.
Reflect.construct(target, args)
Reflect.constructметод эквивалентенnew target(...args), что дает возможность не использоватьnew, чтобы вызвать метод конструктора.
function Greeting(name) {
this.name = name;
}
// new 的写法
const instance = new Greeting('张三');
// Reflect.construct 的写法
const instance = Reflect.construct(Greeting, ['张三']);
еслиReflect.construct()Первый параметр метода не является функцией, и будет сообщено об ошибке.
Reflect.getPrototypeOf(obj)
Reflect.getPrototypeOfметод для чтения объекта__proto__атрибут, соответствующийObject.getPrototypeOf(obj).
const myObj = new FancyThing();
// 旧写法
Object.getPrototypeOf(myObj) === FancyThing.prototype;
// 新写法
Reflect.getPrototypeOf(myObj) === FancyThing.prototype;
Reflect.getPrototypeOfа такжеObject.getPrototypeOfОдно отличие состоит в том, что если параметр не является объектом,Object.getPrototypeOfпреобразует этот параметр в объект, а затем запустит, иReflect.getPrototypeOfсообщит об ошибке.
Object.getPrototypeOf(1) // Number {[[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.length // 0
Если прототип целевого объекта не может быть установлен (например, целевой объект не должен расширяться),Reflect.setPrototypeOfметод возвращаетfalse.
Reflect.setPrototypeOf({}, null)
// true
Reflect.setPrototypeOf(Object.freeze({}), null)
// false
Если первый аргумент не является объектом,Object.setPrototypeOfвозвращает сам первый параметр, аReflect.setPrototypeOfсообщит об ошибке.
Object.setPrototypeOf(1, {})
// 1
Reflect.setPrototypeOf(1, {})
// TypeError: Reflect.setPrototypeOf called on non-object
если первый аргументundefinedилиnull,Object.setPrototypeOfа такжеReflect.setPrototypeOfсообщит об ошибках.
Object.setPrototypeOf(null, {})
// TypeError: Object.setPrototypeOf called on null or undefined
Reflect.setPrototypeOf(null, {})
// TypeError: Reflect.setPrototypeOf called on non-object
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Объекты могут упростить эту операцию.
const ages = [11, 33, 12, 54, 18, 96];
// 旧写法
const youngest = Math.min.apply(Math, ages);
const oldest = Math.max.apply(Math, ages);
const type = Object.prototype.toString.call(youngest);
// 新写法
const youngest = Reflect.apply(Math.min, Math, ages);
const oldest = Reflect.apply(Math.max, Math, ages);
const type = Reflect.apply(Object.prototype.toString, youngest, []);
Reflect.defineProperty(target, propertyKey, attributes)
Reflect.definePropertyметод в основном эквивалентенObject.defineProperty, используемый для определения свойств объекта. В будущем последний будет постепенно упраздняться, пожалуйста, используйте его с этого момента.Reflect.definePropertyзамени это.
function MyDate() {
/*…*/
}
// 旧写法
Object.defineProperty(MyDate, 'now', {
value: () => Date.now()
});
// 新写法
Reflect.defineProperty(MyDate, 'now', {
value: () => Date.now()
});
еслиReflect.definePropertyПервый параметр не является объектом, будет выдана ошибка, напримерReflect.defineProperty(1, 'foo').
Этот метод можно использовать сProxy.definePropertyС использованием.
const p = new Proxy({}, {
defineProperty(target, prop, descriptor) {
console.log(descriptor);
return Reflect.defineProperty(target, prop, descriptor);
}
});
p.foo = 'bar';
// {value: "bar", writable: true, enumerable: true, configurable: true}
p.foo // "bar"
В приведенном выше кодеProxy.definePropertyНастройте перехват при присвоении атрибутов, затем используйтеReflect.definePropertyЗадание завершено.
Reflect.getOwnPropertyDescriptor(target, propertyKey)
Reflect.getOwnPropertyDescriptorв основном эквивалентенObject.getOwnPropertyDescriptor, который используется для получения объекта описания указанного свойства, которое в дальнейшем будет заменено.
var myObject = {};
Object.defineProperty(myObject, 'hidden', {
value: true,
enumerable: false,
});
// 旧写法
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden');
// 新写法
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');
Reflect.getOwnPropertyDescriptorа такжеObject.getOwnPropertyDescriptorОдно отличие состоит в том, что если первый аргумент не является объектом,Object.getOwnPropertyDescriptor(1, 'foo')нет ошибки, верниundefined,а такжеReflect.getOwnPropertyDescriptor(1, 'foo')Будет выброшена ошибка, указывающая, что параметр недействителен.
Reflect.isExtensible (target)
Reflect.isExtensibleметод соответствияObject.isExtensible, который возвращает логическое значение, указывающее, является ли текущий объект расширяемым.
const myObject = {};
// 旧写法
Object.isExtensible(myObject) // true
// 新写法
Reflect.isExtensible(myObject) // true
Если параметр не является объектом,Object.isExtensibleвернусьfalse, потому что не-объекты по своей сути не расширяемы, иReflect.isExtensibleсообщит об ошибке.
Object.isExtensible(1) // false
Reflect.isExtensible(1) // 报错
Reflect.preventExtensions(target)
Reflect.preventExtensionsвести перепискуObject.preventExtensionsспособ сделать объект нерасширяемым. Он возвращает логическое значение, указывающее, была ли операция успешной.
var myObject = {};
// 旧写法
Object.preventExtensions(myObject) // Object {}
// 新写法
Reflect.preventExtensions(myObject) // true
Если параметр не является объектом,Object.preventExtensionsВ среде ES5 сообщается об ошибке, переданные параметры возвращаются в среде ES6 иReflect.preventExtensionsсообщит об ошибке.
// ES5 环境
Object.preventExtensions(1) // 报错
// ES6 环境
Object.preventExtensions(1) // 1
// 新写法
Reflect.preventExtensions(1) // 报错
Reflect.ownKeys (target)
Reflect.ownKeysМетод используется для возврата всех свойств объекта, в основном эквивалентныхObject.getOwnPropertyNamesа такжеObject.getOwnPropertySymbolsСумма.
var myObject = {
foo: 1,
bar: 2,
[Symbol.for('baz')]: 3,
[Symbol.for('bing')]: 4,
};
// 旧写法
Object.getOwnPropertyNames(myObject)
// ['foo', 'bar']
Object.getOwnPropertySymbols(myObject)
//[Symbol(baz), Symbol(bing)]
// 新写法
Reflect.ownKeys(myObject)
// ['foo', 'bar', Symbol(baz), Symbol(bing)]
еслиReflect.ownKeys()Первый параметр метода не является объектом, и будет сообщено об ошибке.
Пример: реализация шаблона Observer с прокси
Режим наблюдателя (режим наблюдателя) относится к функции автоматического наблюдения за объектом данных, как только объект изменится, функция будет выполняться автоматически.
const person = observable({
name: '张三',
age: 20
});
function print() {
console.log(`${person.name}, ${person.age}`)
}
observe(print);
person.name = '李四';
// 输出
// 李四, 20
В приведенном выше коде объект данныхperson– цель наблюдения, функцияprintявляется наблюдателем. Как только объект данных изменится,printбудет выполняться автоматически.
Ниже с помощью Proxy напишите простейшую реализацию паттерна Observer, то есть реализуйтеobservableа такжеobserveэти две функции. Идея в том,observableФункция возвращает прокси исходного объекта, перехватывает операции присваивания и запускает различные функции, выступающие в роли наблюдателей.
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}
В приведенном выше коде определитеSetКоллекция, в которую помещаются все функции наблюдателя. Потом,observableФункция возвращает прокси исходного объекта, перехватывая операции присваивания. Функция перехватаsetСреди них все наблюдатели автоматически казнены.