Отслеживание изменений переменных в JavaScript
Существует ли возможность создать событие в JavaScript, которое будет срабатывать при изменении значения определенной переменной? Также допустимо использование jQuery.
5 ответ(ов)
Да, это теперь вполне возможно!
Я знаю, что это старая тема, но сейчас этот эффект можно реализовать с помощью аксессоров (геттеров и сеттеров): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters
Вы можете определить объект следующим образом, где aInternal
представляет поле a
:
x = {
aInternal: 10,
aListener: function(val) {},
set a(val) {
this.aInternal = val;
this.aListener(val);
},
get a() {
return this.aInternal;
},
registerListener: function(listener) {
this.aListener = listener;
}
}
Теперь вы можете зарегистрировать слушателя с помощью следующего кода:
x.registerListener(function(val) {
alert("Кто-то изменил значение x.a на " + val);
});
Таким образом, всякий раз, когда что-то изменяет значение x.a
, функция слушателя будет вызвана. Запуск следующей строки вызовет всплывающее окно с предупреждением:
x.a = 42;
Посмотрите пример здесь: https://jsfiddle.net/5o1wf1bn/1/
Вы также можете использовать массив слушателей вместо одного слота для слушателя, но я хотел показать вам самый простой возможный пример.
Ответ на вопрос о Proxy и Prototype в JavaScript
В JavaScript есть две интересные концепции: Proxy и Prototype, которые могут существенно упростить управление состоянием и поведением объектов.
Proxy
Proxy — это объект, который обрабатывает операции, выполняемые над другим объектом (называемым target). Он позволяет вам определять поведение для базовых операций, таких как чтение и запись свойств. Вот пример:
const target = {
message1: "hello",
message2: "everyone",
};
const proxy = new Proxy(target, {
get(target, prop, receiver) {
if (prop === "message2") {
return "world";
}
return Reflect.get(...arguments);
},
});
console.log(proxy.message1); // hello
console.log(proxy.message2); // world
В этом коде, когда вы обращаетесь к message2
через proxy, он возвращает "world", заменяя тем самым исходное значение "everyone".
Prototype
Прототипное наследование позволяет вам добавлять свойства и методы к объектам во время выполнения программы. Вы можете создать «наблюдаемую» переменную с помощью Object.defineProperty
, чтобы отслеживать изменения её значения:
// Консоль
function print(t) {
var c = document.getElementById('console');
c.innerHTML = c.innerHTML + '<br />' + t;
}
// Демонстрация
var myVar = 123;
Object.defineProperty(this, 'varWatch', {
get: function () { return myVar; },
set: function (v) {
myVar = v;
print('Value changed! New value: ' + v);
}
});
print(varWatch);
varWatch = 456;
print(varWatch);
В этом примере, когда вы изменяете varWatch
, происходит вызов метода set, который выводит сообщение о том, что значение изменилось.
Другой пример
Можно создать функцию, которая будет объявлять переменные и отслеживать их изменения:
// Консоль
function print(t) {
var c = document.getElementById('console');
c.innerHTML = c.innerHTML + '<br />' + t;
}
// Демонстрация
var varw = (function (context) {
return function (varName, varValue) {
var value = varValue;
Object.defineProperty(context, varName, {
get: function () { return value; },
set: function (v) {
value = v;
print('Value changed! New value: ' + value);
}
});
};
})(window);
varw('varWatch'); // Объявление без начального значения
print(varWatch);
varWatch = 456;
print(varWatch);
print('---');
varw('otherVarWatch', 123); // Объявление с начальным значением
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
Таким образом, вы можете легко создавать управляемые переменные с помощью Object.defineProperty
, что делает ваш код более удобным и интуитивно понятным.
Если у вас есть дополнительные вопросы по Proxy или Prototype в JavaScript, не стесняйтесь спрашивать!
Нет.
Но если это действительно так важно, у вас есть 2 варианта (первый протестирован, второй — нет):
Первый вариант — используйте сеттеры и геттеры, например так:
var myobj = {a : 1};
function create_gets_sets(obj) { // сделайте это глобальной функцией
var proxy = {};
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
var k = i;
proxy["set_" + i] = function(val) { this[k] = val; };
proxy["get_" + i] = function() { return this[k]; };
}
}
for (var i in proxy) {
if (proxy.hasOwnProperty(i)) {
obj[i] = proxy[i];
}
}
}
create_gets_sets(myobj);
Теперь вы можете сделать что-то вроде этого:
function listen_to(obj, prop, handler) {
var current_setter = obj["set_" + prop];
var old_val = obj["get_" + prop]();
obj["set_" + prop] = function(val) {
current_setter.apply(obj, [val]);
handler(old_val, val);
old_val = val; // обновляем старое значение
};
}
Затем установите слушатель так:
listen_to(myobj, "a", function(oldval, newval) {
alert("старое : " + oldval + " новое : " + newval);
});
Второй вариант — наблюдать за значением:
Учитывая myobj
выше, с его свойством 'a':
function watch(obj, prop, handler) { // сделайте это глобальной функцией
var currval = obj[prop];
function callback() {
if (obj[prop] != currval) {
var temp = currval;
currval = obj[prop];
handler(temp, currval);
}
}
return callback;
}
var myhandler = function(oldval, newval) {
// выполните что-то
};
var intervalH = setInterval(watch(myobj, "a", myhandler), 100);
myobj.set_a(2);
Оба подхода имеют свои плюсы и минусы, и выбор зависит от ваших потребностей.
Извините, что поднимаю старую тему, но вот небольшая инструкция для тех, кто (как и я!) не понимает, как работает пример Эли Грея:
var test = new Object();
test.watch("elem", function(prop, oldval, newval) {
// Ваш код
return newval;
});
Надеюсь, это поможет кому-то.
Недавно столкнулся с аналогичной проблемой. Мне нужно было отслеживать изменение переменной и выполнять определенные действия, когда переменная изменялась.
Кто-то предложил простое решение — использовать сеттер для установки значения.
Вот как я объявил простой объект, который хранит значение моей переменной:
var variableObject = {
value: false,
set: function (value) {
this.value = value;
this.getOnChange();
}
}
Этот объект содержит метод set
, с помощью которого я могу изменить значение. Но также он вызывает метод getOnChange()
. Определю его сейчас.
variableObject.getOnChange = function() {
if(this.value) {
// выполняем некоторые действия
}
}
Теперь, когда я вызываю variableObject.set(true)
, метод getOnChange
срабатывает, и если значение установлено как нужно (в моем случае: true
), выполняется блок if
.
Это самый простой способ, который я нашел для решения этой задачи.
event.preventDefault() против return false: в чем разница?
Получение ID элемента, вызвавшего событие
Как перенаправить на другую веб-страницу?
Прокрутка к элементу с использованием jQuery
Как определить нажатие клавиши Esc?