Programming

JavaScript에서 변수 변경 청취

procodes 2020. 2. 20. 23:39
반응형

JavaScript에서 변수 변경 청취


JS에서 특정 변수의 값이 변경 될 때 발생하는 이벤트를 가질 수 있습니까? JQuery가 허용됩니다.


예, 이제 가능합니다!

나는이 오래된 스레드 알고 있지만 지금이 효과가 접근에게 (getter 및 setter)를 사용하여 가능하다 : 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/ 예를 참조하십시오.

단일 리스너 슬롯 대신 리스너 배열을 사용할 수도 있지만 가장 간단한 예를 들었습니다.


이 질문에 대한 대부분의 답변은 구식이거나 비효율적이거나 큰 부풀린 라이브러리를 포함해야합니다.

  • Object.watchObject.observe 는 더 이상 사용되지 않으며 사용해서는 안됩니다.
  • onPropertyChange 는 일부 IE 버전에서만 작동하는 DOM 요소 이벤트 핸들러입니다.
  • Object.defineProperty를 사용하면 객체 속성을 변경 불가능하게 만들 수 있으며, 이로 인해 시도 된 변경을 감지 할 수 있지만 모든 변경 사항도 차단됩니다.
  • setter 및 getter 정의 는 작동하지만 많은 설정 코드가 필요하며 새 특성을 삭제하거나 작성해야 할 때 제대로 작동하지 않습니다.

오늘, 당신은 지금 사용할 수있는 프록시 객체 객체에 대한 변경 모니터 (와 절편)에 있습니다. OP가하려는 일을 위해 만들어진 것입니다. 기본 예는 다음과 같습니다.

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객체 의 유일한 단점은 다음 과 같습니다.

  1. Proxy객체 (예 : IE11 등) 이전 버전의 브라우저에서 사용할 수 없습니다와 polyfill 수 완전히 복제되지 Proxy기능을 제공합니다.
  2. 프록시 객체는 항상 특수 객체 (예 :)에서 예상대로 작동하지는 않습니다 Date. Proxy객체는 일반 객체 또는 배열과 가장 잘 어울립니다.

중첩 된 객체의 변경 사항을 관찰 해야하는 경우 Observable Slim (내가 게시 한) 과 같은 특수 라이브러리를 사용해야합니다 .

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}}]

아니.

그러나 그것이 정말로 중요하다면, 두 가지 옵션이 있습니다 (첫 번째는 테스트되고 두 번째는 그렇지 않습니다).

먼저 setter와 getter를 사용하십시오.

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);

사용 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>


오래된 스레드를 가져 와서 죄송하지만 여기에 나와 같은 Eli Grey의 예제가 어떻게 작동하는지 보지 못하는 사람들을위한 약간의 매뉴얼이 있습니다.

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

이것이 누군가를 도울 수 있기를 바랍니다.


으로 누가 쉐퍼의 대답 ( 참고 :이 그의 원래의 게시물을 의미 있지만 요점은 여기에 편집 후 유효 ), 나 또한 가져 오기 한 쌍의 / 당신의 가치를 액세스 할 수있는 설정 방법을 제안합니다.

그러나 나는 약간의 수정을 제안 할 것입니다 (그리고 그것이 내가 게시하는 이유입니다 ...).

이 코드의 문제점 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
    };
})();

이제 Luke가 제안한 것과 동일한 방법으로 리스너를 작성하고 추가 할 수 있지만 a눈에 띄지 않게 읽거나 쓸 수있는 방법은 없습니다 .

프로그래밍 방식으로 캡슐화 된 필드 추가

여전히 Luke의 트랙에서 간단한 함수 호출을 통해 캡슐화 된 필드와 해당 getter / setter를 객체에 추가하는 간단한 방법을 제안합니다.

이것은 값 유형 에서만 올바르게 작동 합니다 . 와 작업이 들어 참조 형식 , 어떤 종류의 깊은 복사 (참조 구현되어야 할 것입니다 이 하나를 예를 들어).

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);

jQuery {UI} (모든 사람이 :-)를 사용해야하는 경우 숨겨진 <input /> 요소와 함께 .change ()를 사용할 수 있습니다.


AngularJS(이것은 JQuery아니지만 이것이 도움이 될 수 있음을 알고 있습니다. [순수 JS는 이론 상으로는 우수합니다]) :

$scope.$watch('data', function(newValue) { ..

여기서 "data"는 범위의 변수 이름입니다.

문서에 대한 링크가 있습니다.


몇 년 후 조정하는 경우 :

onpropertychange 이벤트와 최신 사양 defineProperty를 사용하는 대부분의 브라우저 (및 IE6 +)에 대한 솔루션을 사용할 수 있습니다. 약간의 문제점은 변수를 dom 객체로 만들어야한다는 것입니다.

세부 사항 :

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


찾고있는 기능은 최신 브라우저에서만 사용할 수있는 "defineProperty ()"메소드를 사용하여 얻을 수 있습니다.

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

더 많은 브라우저 간 지원이 필요한 경우 비슷한 기능을 가진 jQuery 확장을 작성했습니다.

https://github.com/jarederaj/jQueue

변수, 객체 또는 키의 존재에 대한 대기 콜백을 처리하는 작은 jQuery 확장입니다. 백그라운드에서 실행중인 프로세스의 영향을받을 수있는 많은 데이터 포인트에 여러 개의 콜백을 할당 할 수 있습니다. jQueue는 사용자가 지정한 이러한 데이터가 수신 될 때까지 기다렸다가 해당 인수를 사용하여 올바른 콜백을 시작합니다.


직접적이지 않음 : 일종의 "addListener / removeListener"인터페이스 또는 NPAPI 플러그인이있는 페어 getter / setter가 필요합니다 (그러나 이것은 다른 이야기입니다).


//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);

다소 간단하고 간단한 해결책은 함수 호출을 사용하여 전역 변수의 값을 설정하고 값을 직접 설정하지 않는 것입니다. 이 방법으로 당신은 완전히 통제 할 수 있습니다 :

var globalVar;

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

이를 시행 할 수있는 방법은 없습니다. 프로그래밍 규칙 만 있으면됩니다.하지만 grep코드에서 값을 직접 설정하는 곳이 없는지 확인할 수 있습니다 globalVar.

또는 객체와 사용자 getter 및 setter 메소드로 캡슐화 할 수 있습니다.


직접 가능하지 않습니다.

그러나이 작업은 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의 변경 사항을 감지합니다.


초기 질문은 변수가 아니라 변수에 대한 것임을 기억하십시오.)

위의 모든 답변 외에도 forTheWatch.js 라는 작은 lib를 만들었습니다.이 라이브러리 는 자바 스크립트에서 일반 전역 변수의 변경 사항을 포착하고 콜백하는 동일한 방법을 사용합니다.

JQUERY 변수와 호환되며 OBJECTS를 사용할 필요가 없으며 필요한 경우 여러 변수의 ARRAY를 직접 전달할 수 있습니다.

도움이 될 수 있다면 ... : https://bitbucket.org/esabora/forthewatch
기본적으로 함수를 호출하면됩니다.
watchIt("theVariableToWatch", "varChangedFunctionCallback");

관련이없는 경우 사전에 죄송합니다.


이것은 오래된 스레드이지만 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자식 버튼을 클릭 할 때마다 부모에서 업데이트 됩니다. 작업 스택


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]);}
                    });
            }

참고 URL : https://stackoverflow.com/questions/1759987/listening-for-variable-changes-in-javascript



반응형