6

Отслеживание изменений переменных в JavaScript

1

Существует ли возможность создать событие в JavaScript, которое будет срабатывать при изменении значения определенной переменной? Также допустимо использование jQuery.

5 ответ(ов)

1

Да, это теперь вполне возможно!

Я знаю, что это старая тема, но сейчас этот эффект можно реализовать с помощью аксессоров (геттеров и сеттеров): 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/

Вы также можете использовать массив слушателей вместо одного слота для слушателя, но я хотел показать вам самый простой возможный пример.

0

Ответ на вопрос о 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, не стесняйтесь спрашивать!

0

Нет.

Но если это действительно так важно, у вас есть 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);

Оба подхода имеют свои плюсы и минусы, и выбор зависит от ваших потребностей.

0

Извините, что поднимаю старую тему, но вот небольшая инструкция для тех, кто (как и я!) не понимает, как работает пример Эли Грея:

var test = new Object();
test.watch("elem", function(prop, oldval, newval) {
    // Ваш код
    return newval;
});

Надеюсь, это поможет кому-то.

0

Недавно столкнулся с аналогичной проблемой. Мне нужно было отслеживать изменение переменной и выполнять определенные действия, когда переменная изменялась.

Кто-то предложил простое решение — использовать сеттер для установки значения.

Вот как я объявил простой объект, который хранит значение моей переменной:

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.

Это самый простой способ, который я нашел для решения этой задачи.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь