Getter/setter в конструкторе
Недавно я прочитал о том, что есть возможность определять геттеры/сеттеры в JavaScript. Это кажется чрезвычайно полезным - сеттер - это своего рода "помощник", который может анализировать значение, которое нужно установить первым, прежде чем устанавливать его.
Например, у меня есть этот код:
var obj = function(value) {
var test = !!value; // 'test' has to be a boolean
return {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new obj(true);
Этот код всегда преобразует value
в логическое. Поэтому, если вы код instance.test = 0
, то instance.test === false
.
Однако для этого вам нужно фактически вернуть объект, что означает, что новый экземпляр не имеет тип obj
, а просто является простым объектом. Это означает, что изменение прототипа obj
не влияет на экземпляры. Например, это работает не - instance.func
есть undefined:
obj.prototype.func = function() { console.log(this.value); };
потому что instance
не имеет типа obj
. Чтобы заставить функции прототипа работать, я предполагаю, что я не должен возвращать простой объект, но не возвращать ничего, поэтому instance
будет иметь тип obj
, как работает обычный конструктор.
Проблема в том, как реализовать геттеры/сеттеры? Я могу найти статьи, описывающие, как добавлять их к объекту, а не как часть конструктора настраиваемого типа.
Итак, как мне реализовать getters/setters в конструкторе, чтобы иметь возможность использовать геттеры/сеттеры и расширять прототип?
Ответы
Ответ 1
Вы не можете этого сделать.
Однако вы можете установить setter/getters для свойств объектов. Я советую вам использовать ES5 Object.defineProperties
. конечно, это работает только в современных браузерах.
var obj = function() {
...
Object.defineProperties(this, {
"test": {
"get": function() { ... },
"set": function() { ... }
}
});
}
obj.prototype.func = function() { ... }
var o = new obj;
o.test;
o.func();
Ответ 2
Обновление для ES6 - посмотрите раздел 19.3.1 книги Алекса Раушмайера Изучение ES6 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data, в котором показано, как использовать WeakMaps с геттерами и сеттерами для хранения частных данных. Объединение с разделом 16.2.2.3 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters приведет к чему-то вроде
# module test_WeakMap_getter.js
var _MyClassProp = new WeakMap();
class MyClass {
get prop() {
return _MyClassProp.get( this );
}
set prop(value) {
_MyClassProp.set( this, value );
}
}
var mc = new MyClass();
mc.prop = 5 ;
console.log( 'My value is', mc.prop );
$ node --use_strict test_WeakMap_getter.js
My value is 5
Ответ 3
Я знаю, что это может быть очень поздно, но я понял, что нужно сделать то, что вы хотите, и ради людей, таких как я, googling для ответа на это здесь.
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
var test = new Constructor(5);
alert(test.value) // 10
Я тестировал это в хроме, сафари, мобильном сафари, firefox, и все они работают (последние версии, конечно)
Ответ 4
function Obj(value){
this.value = !!value;
}
Obj.prototype = {
get test () {
return this.value;``
},
set test (value) {
this.value = !!this.value;
}
};
var obj = new Obj(true);
Ответ 5
Обычно вам нужны методы класса. Ответ @Raynos от 7 мая 2011 года выполняет задание, но он определяет метод экземпляра, а не метод класса.
Ниже показано определение класса, в котором геттер и сеттер являются частью класса. Это определение очень похоже на ответ @Raynos, но с двумя отличиями в коде: (1) Действие "defineProperties()" было перенесено из конструктора. (2) Аргумент "defineProperties()", который был изменен из объекта экземпляра "this", на объект прототипа конструктора.
function TheConstructor(side) {
this.side = side;
}
Object.defineProperties(TheConstructor.prototype, {
area: {
get: function() { return this.side * this.side; }
,set: function(val) { this.side = Math.sqrt(val); }
}
});
// Test code:
var anInstance = new TheConstructor(2);
console.log("initial Area:"+anInstance.area);
anInstance.area = 9;
console.log("modified Area:"+anInstance.area);
Что дает эти результаты:
initial Area:4
modified Area:9
Хотя обычно различие между классом и экземпляром
определение - это только вопрос стиля, есть цель
хороший стиль, и есть случай, когда различие имеет значение:
мемуаризованный геттер. Целью мемуаризованного геттера является
описанный здесь: Smart/self-overwording/lazy getters
Определите геттер на уровне класса, когда значение memoized равно
относятся ко всему классу. Например, файл конфигурации
следует читать только один раз; затем следует применить полученные значения
на протяжении всей программы. Следующий пример кода
определяет memoised getter на уровне класса.
function configureMe() {
return 42;
}
Object.defineProperties(TheConstructor.prototype, {
memoizedConfigParam: {
get: function() {
delete TheConstructor.prototype.memoizedConfigParam;
return TheConstructor.prototype.memoizedConfigParam = configureMe();
}
,configurable: true
}
});
// Test code:
console.log("memoizedConfigParam:"+anInstance.memoizedConfigParam);
Выдает:
memoizedConfigParam:42
Как видно из примера, memoised getters имеют
что функция геттера удаляет себя,
затем заменяет собой простое значение, которое
(предположительно) никогда не изменится.
Обратите внимание, что "настраиваемый" должен быть установлен в "true".
Определите getter на уровне экземпляра, когда memoized value
зависит от содержимого экземпляра. Определение движется
внутри конструктора, а объектом внимания является 'this'.
function TheConstructorI(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
return this.memoizedCalculation = this.expensiveOperation();
}
,configurable: true
}
});
}
TheConstructorI.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instance2 = new TheConstructorI(2);
var instance3 = new TheConstructorI(3);
console.log("memoizedCalculation 2:"+instance2.memoizedCalculation);
console.log("memoizedCalculation 3:"+instance3.memoizedCalculation);
Выдает:
memoizedCalculation 2:8
memoizedCalculation 3:27
Если вы хотите гарантировать (а не предполагать), что memoized
значение никогда не будет изменено, атрибут "записываемый" должен
быть изменен. Это делает код немного сложнее.
function TheConstructorJ(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
Object.defineProperty( this, 'memoizedCalculation'
,{ value : this.expensiveOperation()
,writable : false
});
return this.memoizedCalculation;
}
,configurable: true
}
});
}
TheConstructorJ.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instanceJ = new TheConstructorJ(2);
console.log("memoizedCalculation:"+instanceJ.memoizedCalculation);
instanceJ.memoizedCalculation = 42; // results in error
Выдает:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
Первоначальный вопрос OP, с 7 марта 2011 года, представил основные
getter и setter, отметил, что он работал над объектом, но
не на 'this', и спросил, как определить геттеры и сеттеры внутри
конструктор. В дополнение ко всем приведенным выше примерам
также "дешевый" способ сделать это: создать новый объект внутри
конструктор, как и OP, но затем назначить объект
быть членом в 'this'. Итак, исходный код будет выглядеть так:
это:
var MyClass = function(value) {
var test = !!value; // 'test' has to be a boolean
this.data = {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new MyClass(true);
// But now 'data' is part of the access path
instance.data.test = 0;
console.log(instance.data.test);
Выдает:
false
Верьте или нет, я действительно сталкиваюсь с ситуациями, когда
этот "дешевый выстрел" - лучшее решение. В частности, я использовал это
когда у меня были записи из нескольких таблиц, инкапсулированных внутри
один класс, и хотел представить единое представление, как будто
они были одной записью, называемой "данными".
Удачи.
IAM_AL_X
Ответ 6
@Alex Я вижу, что это больше возможностей и больше энергии, программирование - это искусство, @Поделитесь своим делом с нами, и за это я благодарю его. Может быть, кто-то хочет это сделать.
Я уверен, что версия setter такая же, но просто изменила ее на s.
i.g:
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
Object.__defineSetter__.call(Constructor.prototype, "bar", function(foo){
return this.input *= foo;
});
var test = new Constructor(5);
console.log(test.value); // 10
test.bar = 5;
console.log(test.input); //25
С учетом сказанного, эта функция устарела, советы не использовать в производственной кодировке.