[javascript] 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 ()를 사용할 수 있습니다.