Доступ к получателю / установщику по умолчанию для объекта JavaScript
Получатели и установщики JavaScript могут быть переопределены для определенных свойств с помощью Object.defineProperty
, Есть ли какой-нибудь способ получить доступ к получателю / установщику по умолчанию (т.е. к функции, используемой, если получатель / установщик не был перезаписан)? В моем пользовательском сеттере я хочу иметь специальную обработку в некоторых случаях, но использую обработку по умолчанию в других. Я старался:
Object.defineProperty(obj, 'foo',
'set': function(val) {
if (useCustom) {
doCustomProcessing(obj, 'foo', val);
}
else {
obj['foo'] = val;
}
}
);
К сожалению, это приводит к переполнению стека, так как obj['foo'] = val;
вызывает пользовательский установщик для вызова. Поэтому я хотел бы найти способ установить foo
собственностью obj
используя установщик по умолчанию. Это возможно?
5 ответов
Насколько я знаю, свойство либо имеет значение (свойство data), либо имеет getter/setter (свойство accessor): оно не может иметь обоих. Используйте локальную переменную при закрытии или другое свойство в качестве основного хранилища для свойств, для которых вы определили метод получения / установки.
Например,
(function() {
var value;
Object.defineProperty(obj, 'foo', {
set: function(val) {
if (useCustom) {
value = doCustomProcessing(obj, 'foo', val);
} else {
value = val;
}
},
get: function() { return value; }
});
})();
В ES6 вы можете использовать объект Proxy:
var myObject = {};
var myProxyObject = new Proxy(myObject, {
set: function(ob, prop, value) {
if (prop === 'counter' && value === 10) {
// Some specialised behaviour
ob.counter = 0;
}
else {
// Default setter behaviour.
ob[prop] = value;
}
// Return true otherwise you will get a TypeError in strict mode.
return true;
}
});
>>> myProxyObject.counter = 5;
>>> console.log(myProxyObject.counter);
5
>>> myProxyObject.counter = 10;
>>> console.log(myProxyObject.counter);
0
Единственный способ, которым я знаю, как это сделать, - сделать переменную не приватной, а двумя примерами, второй - более кратким:
(function testInheritance(global, doc) {
"use strict";
var MyFunc = Object.create({}, {
_foo: {
value: "Some Default Value",
writable: true,
enumerable: true
},
foo: {
get: function() {
return this._foo;
},
set: function(value) {
this._foo = value;
}
}
}),
testFunc = Object.create(MyFunc);
console.log(testFunc.foo); // "Some default value"
testFunc.foo = "boo";
console.log(testFunc.foo); // "boo";
testFunc._foo = "Not a private variable anymore";
console.log(testFunc.foo); // "Not a private variable anymore"
}(window, document));
(function testInheritanceTwo(global, doc) {
"use strict";
var MyFunc = Object.create({}, {
foo: {
get: function() {
if (!this._foo) {
return "Some default value set by the getter.";
}
return this._foo;
},
set: function(value) {
this._foo = value;
}
}
}),
testFunc = Object.create(MyFunc);
console.log(testFunc.foo); // "Some default value set by the getter."
testFunc.foo = "Whomp";
console.log(testFunc.foo); // "Whomp";
testFunc._foo = "Not a private variable anymore, unfortunately.";
console.log(testFunc.foo); // "Not a private variable anymore"
}(window, document));
Насколько я могу сказать:
Вы не можете ссылаться на значение с тем же именем, что и то, что вы используете в set: function(value), или вы в конечном итоге получаете бесконечный цикл, в котором установка значения вызывает заданное значение и вызывает его снова, и так далее. Отсюда твоя проблема.
Если вы попытаетесь сделать переменную _foo приватной, то установщик не будет работать. С этим синтаксисом кажется, что вы можете скрыть переменную, но вы не можете сделать ее приватной.
Я полагаю, вы пытаетесь найти механизм наследования для методов получения и установки...
var useCustom;
var obj1 = {
set foo(val){
console.log(val + " : foo : obj1");
}
};
var obj2 = {
set foo(val){
if (useCustom) {
console.log(val + " : foo : obj2");
}
else {
Object.getPrototypeOf(obj2).foo = val;
}
}
};
Object.setPrototypeOf(obj2,obj1);
useCustom = true;
obj2.foo = "test1"; //test1 : foo : obj2
useCustom = false;
obj2.foo = "test2"; // test2 : foo : obj1