Вопрос по jquery, javascript-events, javascript – Прослушивание изменений переменных в JavaScript

373

Возможно ли иметь событие в JS, которое срабатывает при изменении значения определенной переменной? JQuery принимается.

@ HellBaby, этот вопрос о переменных, а не о DOM. Benjamin Gruenbaum
@BenjaminGruenbaum, вероятно, вы хотите сказать MutableObserver (для DOM). Объект только для объектов JS из того, что я помню. HellBaby
@ BenjaminGruenbaum в соответствии сdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/... Object.observe устарел или устарел. Рекомендуемой заменой (для этой же страницы) является объект Proxy. stannius
есть ли ответ на это? Я имею в виду, чтобы выбрать один Braian Mellor
Вопрос только оvariableТем не менее, все ответы здесь относятся кproperty, Интересно, можем ли мы послушатьlocal variable хотя меняется. Thariq Nugrohotomo

Ваш Ответ

17   ответов
0

Однако это можно сделать с помощью CustomEvent:https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent

Приведенный ниже метод принимает массив имен переменных в качестве входных данных и добавляет прослушиватель событий для каждой переменной и запускает событие для любых изменений значения переменных.

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

function watchVariable(varsToWatch) {
    let timeout = 1000;
    let localCopyForVars = {};
    let pollForChange = function () {
        for (let varToWatch of varsToWatch) {
            if (localCopyForVars[varToWatch] !== window[varToWatch]) {
                let event = new CustomEvent('onVar_' + varToWatch + 'Change', {
                    detail: {
                        name: varToWatch,
                        oldValue: localCopyForVars[varToWatch],
                        newValue: window[varToWatch]
                    }
                });
                document.dispatchEvent(event);
                localCopyForVars[varToWatch] = window[varToWatch];
            }
        }
        setTimeout(pollForChange, timeout);
    };
    let respondToNewValue = function (varData) {
        console.log("The value of the variable " + varData.name + " has been Changed from " + varData.oldValue + " to " + varData.newValue + "!!!"); 
    }
    for (let varToWatch of varsToWatch) {
        localCopyForVars[varToWatch] = window[varToWatch];
        document.addEventListener('onVar_' + varToWatch + 'Change', function (e) {
            respondToNewValue(e.detail);
        });
    }
    setTimeout(pollForChange, timeout);
}

Вызывая метод:

watchVariables(['username', 'userid']);

Он обнаружит изменения в переменных username и userid.

0

но я наткнулся на второй по величине ответ (пользовательские слушатели), когда искал решение с использованием Angular. В то время как решение работает, Angular имеет лучший встроенный способ решения этой проблемы с помощью@Output и источники событий. Исходя из примера в пользовательском ответе слушателя:

ChildComponent.html

<button (click)="increment(1)">Increment</button>

ChildComponent.ts

import {EventEmitter, Output } from '@angular/core';

@Output() myEmitter: EventEmitter<number> = new EventEmitter<number>();

private myValue: number = 0;

public increment(n: number){
  this.myValue += n;

  // Send a change event to the emitter
  this.myEmitter.emit(this.myValue);
}

ParentComponent.html

<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>

ParentComponent.ts

public n: number = 0;

public monitorChanges(n: number){
  this.n = n;
  console.log(n);
}

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

6

AngularJS (Я знаю это неJQuery, но это может помочь. [Чистый JS хорош только в теории]):

$scope...

где «данные» - это имя вашей переменной в области видимости.

Существуетссылка на док.

он запускается только тогда, когда$scope.$apply() работает iamawebgeek
К сожалению, это заставляет вас связывать переменную с областью действия ruX
2

которую вы ищете, может быть достигнута с помощью метода defineProperty (), который доступен только для современных браузеров:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Я написал расширение jQuery, которое имеет некоторые аналогичные функциональные возможности, если вам нужна дополнительная кросс-браузерная поддержка

https://github.com/jarederaj/jQueue

Небольшое расширение jQuery, которое обрабатывает обратные вызовы в очереди на существование переменной, объекта или ключа. Вы можете назначить любое количество обратных вызовов любому количеству точек данных, на которые могут повлиять процессы, выполняющиеся в фоновом режиме. jQueue прослушивает и ожидает появления указанных вами данных, а затем запускает правильный обратный вызов со своими аргументами.

34

Но, если это действительно так важно, у вас есть 2 варианта (первый проверен, второй нет):

Во-первых, используйте сеттеры и геттеры, вот так:

var myobj = {a : 1};

function create_gets_sets(obj) { // make this a framework/global function
    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, [old_val, val]); handler(val));
}

затем установите слушателя как:

listen_to(myobj, "a", function(oldval, newval) {
    alert("old : " + oldval + " new : " + newval);
}

Во-вторых, я действительно забыл, я подам, пока я думаю об этом :)

РЕДАКТИРОВАТЬ: О, я помню :) Вы могли бы поставить часы на значение:

Учитывая myobj выше, с 'a' на нем:

function watch(obj, prop, handler) { // make this a framework/global function
    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) {
    //do something
};

var intervalH = setInterval(watch(myobj, "a", myhandler), 100);

myobj.set_a(2);
хе-хе, я запомню в конце концов, сейчас я исправляю код Luke Schafer
второе хорошо :) Sinan
исправил код, добавил мою вторую идею Luke Schafer
«наблюдение» на самом деле не обнаруживает изменения. Это не основано на событиях, и, конечно, очень скоро замедлит работу всего приложения. Эти подходы ИМХО никогда не должны быть частью реального проекта. Muhammad bin Yusrat
55

Я знаю, что это старый поток, но теперь этот эффект возможен с использованием методов доступа (геттеры и сеттеры):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("Someone changed the value of x.a to " + val);
});

Поэтому всякий раз, когда что-либо меняет значениеx.a, функция слушателя будет запущена. Выполнение следующей строки вызовет всплывающее окно с предупреждением:

x.a = 42;

Смотрите пример здесь:https://jsfiddle.net/5o1wf1bn/1/

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

Где твой код? Вы начали новый вопрос с этого? Akira
Вам просто нужно иметь массив слушателей вместо одного, а затем вместо простого вызоваthis.aListener(val), вам придется пройти через все функции слушателя и вызывать каждуюval, Как правило, метод называетсяaddListener вместоregisterListener. Akira
Как насчет способа обнаружить, когда новое свойство было добавлено к объекту или было удалено? Michael
Это тонна кода на объект, есть ли способ сделать это более пригодным для повторного использования? Vael Victus
Привет Акира! Я сделал нечто подобное - не могли бы вы рассмотреть, почему это не работает для меня? Rick Sanchez
1
//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
    console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};

var x1 = {
    eventCurrentStatus:undefined,
    get currentStatus(){
        return this.eventCurrentStatus;
    },
    set currentStatus(val){
        this.eventCurrentStatus=val;
      //your function();
    }
};

или же

/*  var x1 = {
eventCurrentStatus:undefined,
currentStatus : {
    get : function(){
        return Events.eventCurrentStatus
        },
    set : function(status){
        Events.eventCurrentStatus=status;

    },
}*/
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);

или же

/* global variable ku*/
    var jsVarEvents={};
    Object.defineProperty(window, "globalvar1", {//no i18n
        get: function() { return window.jsVarEvents.globalvarTemp},
        set: function(value) { window.window.jsVarEvents.globalvarTemp = value; }
    });
    console.log(globalvar1);
    globalvar1=1;
    console.log(globalvar1);
5

Доступно решение для большинства браузеров (и IE6 +), которое использует событие onpropertychange и более новую спецификацию defineProperty. Небольшая проблема в том, что вам нужно сделать вашу переменную объектом dom.

Полная информация:

http://johndyer.name/native-browser-get-set-properties-in-javascript/

22

Prototype: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

// Console
function print(t) {
  var c = document.getElementById('console');
  c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
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);
<pre id="console">
</pre>

Другой пример

// Console
function print(t) {
  var c = document.getElementById('console');
  c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
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'); // Declare
print(varWatch);
varWatch = 456;
print(varWatch);

print('---');

varw('otherVarWatch', 123); // Declare with initial value
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
<pre id="console">
</pre>

Второй пример немного вводит в заблуждение,varw требует 2 аргумента, но часть вашего примера показывает, что функция вызывается только с параметром value. Hlawuleka MAS
2

вам нужна пара getter / setter с каким-то интерфейсом «addListener / removeListener» ... или плагин NPAPI (но это совсем другая история).

18

либо неэффективны, либо требуют включения больших раздутых библиотек:

Object.watch а такжеObject.observe оба устарели и не должны использоваться.onPropertyChange обработчик события элемента DOM, который работает только в некоторых версиях IEObject.defineProperty позволяет сделать свойство объекта неизменным, что позволит вам обнаруживать предпринятые изменения, но также будет блокировать любые изменения.Определение сеттеров и геттеров работает, но требует много кода настройки и не работает, когда вам нужно удалить или создать новые свойства.

Сегодня,Теперь вы можете использоватьполномочие объект отслеживать (и перехватывать) изменения, внесенные в объект. Он специально создан для того, что пытается сделать ОП. Вот основной пример:

var targetObj = {};
var targetProxy = new Proxy(targetObj, {
  set: function (target, key, value) {
      console.log(`${key} set to ${value}`);
      target[key] = value;
      return true;
  }
});

targetProxy.hello_world = "test"; // console: 'hello_world set to test'

Единственные недостаткиProxy Объектом являются:

Proxy объект недоступен в старых браузерах (таких как IE11) иpolyfill не может полностью воспроизвестиProxy функциональность.Прокси-объекты не всегда ведут себя должным образом со специальными объектами (например,Date) -Proxy Объект лучше всего сочетать с простыми объектами или массивами.

Если вам нужно наблюдать изменения, внесенные ввложенный объект, то вам нужно использовать специализированную библиотеку, такую какНаблюдаемый Тонкий (который я опубликовал) который работает так:

var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
    console.log(JSON.stringify(changes));
});

p.testing.blah = 42; // console:  [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
Это не раскрывает вашу принадлежность к предлагаемой библиотеке «Observable Slim». Alexis Tyler
189

object.watch (хотя это нестандартно).Вот моя реализация это работает в каждом текущем крупном браузере.

Обратите внимание, что object.watch и unwatch официально устарели и удалены в Firefox 58+. Если вам нужна эта функциональность часов, вам нужно использовать pollyfill. Steven
Есть ли пример того, как использовать это? Eyal
как я могу использовать этот? Mp de la Vega
Holden321, пожалуйста, комментируйте с помощью StackOverflow и не спамите в моем блоге. Eli Grey
Обратите внимание на предупреждение, написанное в ссылке: как правило, по возможности избегайте использования watch () и unwatch (). Эти два метода реализованы только в Gecko и предназначены главным образом для использования при отладке. Кроме того, использование точек наблюдения оказывает серьезное негативное влияние на производительность, что особенно актуально при использовании глобальных объектов, таких как окно. Вместо этого вы обычно можете использовать сеттеры и геттеры или прокси. Dor Cohen
22

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

var test = new Object();
test.watch("elem", function(prop,oldval,newval){
    //Your code
    return newval;
});

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

часы были объявлены устаревшими в Mozilla. Этот ответ может вводить в заблуждение. Arnaud
В сети разработчиков Mozilla это не рекомендуется. Object.prototype.watch () предназначался только для тестирования и не должен использоваться в рабочем коде.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... Dennis Bartlett
Часы не поддерживаются в Chrome или Safari на данный момент, только Firefox Paul McClean
@PaulMcClean этот ответ был в ответе на ответ Эли Грея, который содержал Polyfill.gist.github.com/eligrey/384583 Hobbyist
7

Ответ Люка Шефера (нота: это относится к его оригинальной должности; но весь смысл здесь остается в силе после редактирования), Я бы также предложил пару методов Get / Set для доступа к вашему значению.

Однако я хотел бы предложить некоторые модификации (и именно поэтому я публикую ...).

Проблема с этим кодом в том, что полеa объектаmyobj доступен напрямую, поэтому можно получить к нему доступ / изменить его значение, не вызывая слушателей:

var myobj = { a : 5, get_a : function() { return this.a;}, set_a : function(val) { this.a = val; }}
/* add listeners ... */
myobj.a = 10; // no listeners called!
Инкапсуляция

Таким образом, чтобы гарантировать, что слушатели действительно вызваны, мы должны запретить прямой доступ к полю.a, это сделать? Использоватьзакрытие!

var myobj = (function() { // Anonymous function to create scope.

    var a = 5;            // 'a' is local to this function
                          // and cannot be directly accessed from outside
                          // this anonymous function's scope

    return {
        get_a : function() { return a; },   // These functions are closures:
        set_a : function(val) { a = val; }  // they keep reference to
                                            // something ('a') that was on scope
                                            // where they were defined
    };
})();

Теперь вы можете использовать тот же метод для создания и добавления слушателей, как предложил Люк, но вы можете быть уверены, что нет никакого способа читать или писать вa происходит незамеченным!

Добавление инкапсулированных полей программно

Продолжая следить за Люком, я предлагаю теперь простой способ добавления инкапсулированных полей и соответствующих методов получения / установки к объектам с помощью простого вызова функции.

Обратите внимание, что это будет работать только стипы значений, Для этого нужно работать сссылочные типы, наподобиеглубокая копия должно быть реализовано (см.этот, например).

function addProperty(obj, name, initial) {
    var field = initial;
    obj["get_" + name] = function() { return field; }
    obj["set_" + name] = function(val) { field = val; }
}

Это работает так же, как и раньше: мы создаем локальную переменную для функции, а затем создаем замыкание.

это использовать? Просто:

var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
+1 для инкапсуляции. Это была моя первая мысль, но я хотел в конечном итоге добавить метод create_gets_sets, и, поскольку он неразборчив, скрывать значения - это не здорово :), мы можем сделать еще один шаг и написать некоторые вещи, чтобы скрыть значения, но Я думаю, что код, который я выложил, достаточно запутанен для большинства людей ... может быть, если есть вызов для этого ... Luke Schafer
7

-)), вы можете использовать .change () со скрытым элементом <input />.

Для тех, у кого такая же проблема, как и у меня, если событие изменения не вызывает, попробуйте $ ("# thisOne"). Val (myVariableWhichIsNew) .trigger ('change'). Надеюсь, это поможет Alator
Это лучшее решение в моих книгах. Просто, легко. Вместо изменения переменной в вашем коде, напримерvar1 = 'new value';вместо этого вы установите значение этого скрытого ввода, а затем добавите прослушиватель для изменения переменной.$("#val1").on('change', function(){ val1 = this.val(); ... do the stuff that you wanted to do when val1 changed... }); $("#val1").val('new value'); Tom Walker
<input type="hidden" value="" id="thisOne" /> и с помощью jQuery$("#thisOne").change(function() { do stuff here }); а также$("#thisOne").val(myVariableWhichIsNew); а затем.change() будет стрелять khaverim
Я думаю, что Чак предлагает, чтобы вы могли установить значение ввода, используя jquery, а затем и прослушиватель событий .change (). Если вы обновите значение ввода с помощью .val (), то сработает обратный вызов события .change (). jarederaj
Я не совсем понимаю. Как вы можете прикрепить переменную к скрытому<input/> элемент? Peter Lee
0

чтобы просто использовать вызов функции для установки значения глобальной переменной и никогда не устанавливать ее значение напрямую. Таким образом, вы имеете полный контроль:

var globalVar;

function setGlobalVar(value) {
    globalVar = value;
    console.log("Value of globalVar set to: " + globalVar);
    //Whatever else
}

Нет никакого способа обеспечить это, это просто требует дисциплины программирования ... хотя вы можете использоватьgrep (или что-то подобное), чтобы проверить, что нигде ваш код не устанавливает непосредственно значениеglobalVar.

Или вы можете инкапсулировать его в объектный и пользовательский методы getter и setter ... просто мысль.

-3
Utils = {
    eventRegister_globalVariable : function(variableName,handlers){
        eventRegister_JsonVariable(this,variableName,handlers);
    },
    eventRegister_jsonVariable : function(jsonObj,variableName,handlers){
        if(jsonObj.eventRegisteredVariable === undefined) {
            jsonObj.eventRegisteredVariable={};//this Object is used for trigger event in javascript variable value changes ku
        }
        Object.defineProperty(jsonObj, variableName , {
                    get: function() { 
                        return jsonObj.eventRegisteredVariable[variableName] },
                    set: function(value) {
                        jsonObj.eventRegisteredVariable[variableName] = value; handlers(jsonObj.eventRegisteredVariable[variableName]);}
                    });
            }

Похожие вопросы