侦听JavaScript中的变量更改
在JS中是否可能有一个事件在某个变量的值更改时触发?JQuery被接受。不是直接接受的:您需要一个带有某种“addListener/RemovelListener”接口的pair getter/setter。。。或者一个NPAPI插件(但这完全是另一回事)。否 但是,如果它真的那么重要,您有两个选择(第一个是经过测试的,第二个不是): 首先,使用setter和getter,如下所示:侦听JavaScript中的变量更改,javascript,jquery,dom-events,Javascript,Jquery,Dom Events,在JS中是否可能有一个事件在某个变量的值更改时触发?JQuery被接受。不是直接接受的:您需要一个带有某种“addListener/RemovelListener”接口的pair getter/setter。。。或者一个NPAPI插件(但这完全是另一回事)。否 但是,如果它真的那么重要,您有两个选择(第一个是经过测试的,第二个不是): 首先,使用setter和getter,如下所示: var myobj = {a : 1}; function create_gets_sets(obj) { /
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);
正如(注意:这是指他的原始帖子;但在编辑之后,这里的要点仍然有效),我还建议使用一对Get/Set方法来访问您的值
然而,我会建议一些修改(这就是为什么我张贴…)
该代码的一个问题是对象myobj
的字段A
可以直接访问,因此可以在不触发侦听器的情况下访问/更改其值:
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
。如何做到这一点?闭嘴
现在,您可以使用Luke建议的相同方法来创建和添加侦听器,但您可以放心,不可能不被注意地读取或写入a
以编程方式添加封装字段
在Luke的轨道上,我现在提出一种简单的方法,通过一个简单的函数调用将封装的字段和相应的getter/setter添加到对象中
请注意,这仅适用于值类型。为了使用引用类型,必须实现某种深度复制(例如,请参见)
其工作原理与之前相同:我们在函数上创建一个局部变量,然后创建一个闭包
如何使用它?简单:
var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
如果您使用的是jQuery{UI}(每个人都应该使用:-)),则可以使用带有隐藏元素的.change()。对于几年后进行的调整: 大多数浏览器(和IE6+)都可以使用onpropertychange事件和更新的spec defineProperty解决方案。需要注意的是,您需要将变量设置为dom对象 详情如下:
很抱歉提出一个老话题,但这里有一个小手册,供那些(像我一样!)不了解Eli Grey的例子是如何工作的人使用:
var test = new Object();
test.watch("elem", function(prop,oldval,newval){
//Your code
return newval;
});
希望这可以帮助某人(我知道这不是JQuery,但这可能会有所帮助。[纯JS理论上很好]):
其中“data”是范围中变量的名称
有一个您正在寻找的功能可以通过使用“defineProperty()”方法来实现,该方法仅适用于现代浏览器: 我编写了一个jQuery扩展,如果您需要更多的跨浏览器支持,它具有一些类似的功能: 一个小型jQuery扩展,用于处理对 变量、对象或键的存在。您可以分配任意数量的 回调可能受影响的任意数量的数据点 在后台运行的进程。jQueue侦听并等待 您指定要存在的这些数据,然后触发 使用其参数更正回调 或 或
使用
原型
:
//控制台
功能打印(t){
var c=document.getElementById('console');
c、 innerHTML=c.innerHTML+'
'+t;
}
//演示
var-myVar=123;
Object.defineProperty(这是“varWatch”{
get:function(){return myVar;},
设置:功能(v){
myVar=v;
打印('值已更改!新值:'+v);
}
});
印刷(varWatch);
varWatch=456;
印刷(varWatch)代码>
一个相当简单的解决方案是只使用函数调用来设置全局变量的值,而不是直接设置其值。这样您就可以完全控制:
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
这是没有办法强制执行的,它只需要编程规则。。。尽管您可以使用grep
(或类似工具)检查代码是否没有直接设置globalVar
的值
或者您可以将其封装在对象和用户getter和setter方法中。。。只是一个想法。是的,这现在完全可能了
我知道这是一个旧线程,但现在可以使用访问器(getter和setter)实现这种效果:
您可以这样定义一个对象,其中,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;
请参见此处的示例:
您也可以使用一个侦听器数组而不是单个侦听器插槽,但我想给您一个最简单的示例。这个问题最初是在2009年发布的,大多数现有答案要么过时、无效,要么需要包含大型臃肿的库:
- 和都已弃用,不应使用
- 是一个DOM元素事件处理程序,仅在某些IE版本中有效
- 允许您使对象属性不可变,这将允许您检测尝试的更改,但也会阻止任何更改
- 可以,但它需要大量的设置代码,并且在需要删除或创建新属性时无法正常工作
从2018年起,您现在可以使用对象来监视(和截获)对对象所做的更改。它是专为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'
代理
对象的唯一缺点是:
/* 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);
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]);}
});
}
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
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 = 42;
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'
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}}]
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']);
<button (click)="increment(1)">Increment</button>
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);
}
<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>
public n: number = 0;
public monitorChanges(n: number){
this.n = n;
console.log(n);
}
class MonitoredVariable {
constructor(initialValue) {
this._innerValue = initialValue;
this.beforeSet = (newValue, oldValue) => {};
this.beforeChange = (newValue, oldValue) => {};
this.afterChange = (newValue, oldValue) => {};
this.afterSet = (newValue, oldValue) => {};
}
set val(newValue) {
const oldValue = this._innerValue;
// newValue, oldValue may be the same
this.beforeSet(newValue, oldValue);
if (oldValue !== newValue) {
this.beforeChange(newValue, oldValue);
this._innerValue = newValue;
this.afterChange(newValue, oldValue);
}
// newValue, oldValue may be the same
this.afterSet(newValue, oldValue);
}
get val() {
return this._innerValue;
}
}
const money = new MonitoredVariable(0);
console.log(money.val); // Get its value
money.val = 2; // Set its value
money.afterChange = (newValue, oldValue) => {
console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};
money.val = 3;
Money has been changed from 2 to 3
state.pageNumber = 0;
// watch the console
state.pageNumber = 15;
// watch the console
Object.defineProperty(window, 'player', {
get: () => this._player,
set: v => {
console.log('window.player has been redefined!');
this._player = v;
}
});
var variableObject = {
value: false,
set: function (value) {
this.value = value;
this.getOnChange();
}
}
variableObject.getOnChange = function() {
if(this.value) {
// do some stuff
}
}
const events = require('events');
const eventEmitter = new events.EventEmitter();
// Createing state to watch and trigger on change
let x = 10 // x is being watched for changes in do while loops below
do {
eventEmitter.emit('back to normal');
}
while (x !== 10);
do {
eventEmitter.emit('something changed');
}
while (x === 10);
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
const dataBind = (function () {
const getElementValue = function (selector) {
let element = document.querySelectorAll(selector)[0];
return 'value' in element ? element.value : element.innerHTML;
};
const setElementValue = function (selector, newValue) {
let elementArray = document.querySelectorAll(selector);
for (let i = 0; i < elementArray.length; i++) {
let element = elementArray[i];
if ('value' in element) {
element.value = newValue;
if (element.tagName.toLowerCase() === 'select'){
let options = element.querySelectorAll('option');
for (let option in options){
if (option.value === newValue){
option.selected = true;
break;
}
}
}
} else {
element.innerHTML = newValue;
}
}
};
const bindModelToView = function (selector, object, property, enumerable) {
Object.defineProperty(object, property, {
get: function () {
return getElementValue(selector);
},
set: function (newValue) {
setElementValue(selector, newValue);
},
configurable: true,
enumerable: (enumerable)
});
};
return {
bindModelToView
};
})();
</script>
</head>
<body>
<div style="padding: 20%;">
<input type="text" id="text" style="width: 40px;"/>
</div>
<script>
let x = {a: 1, b: 2};
dataBind.bindModelToView('#text', x, 'a'); //data to dom
setInterval(function () {
x.a++;
}, 1000);
</script>
</body>
</html>