某个变量的值更改时,是否可能在JS中触发事件?接受JQuery。
variable
,而这里的所有答案都是提到的property
。我不知道我们是否可以听取local variable
变化。
某个变量的值更改时,是否可能在JS中触发事件?接受JQuery。
variable
,而这里的所有答案都是提到的property
。我不知道我们是否可以听取local variable
变化。
Answers:
是的,现在完全有可能!
我知道这是一个旧线程,但是现在可以使用访问器(获取器和设置器)实现这种效果:https : //developer.mozilla.org/zh-CN/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/
您还可以使用一组侦听器而不是一个侦听器插槽,但是我想给您提供最简单的示例。
this.aListener(val)
,还必须遍历所有侦听器函数并调用每个传递的函数val
。通常,该方法addListener
代替调用registerListener
。
该问题的大多数答案要么过时,无效,要么要求包含大型的libraries肿库:
今天,您现在可以使用代理对象来监视(和拦截)对对象所做的更改。它是为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
对象的唯一缺点是:
Proxy
对象在较旧的浏览器(例如IE11)中不可用,并且polyfill无法完全复制Proxy
功能。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}}]
target.hello_world = "test"
)
you don't actually watch changes on the target object but only on proxy object
-不太准确。该Proxy
对象未修改-它没有它自己的目标副本。you just want to know when a property change on the target object
-您可以使用来完成此任务Proxy
,这是代理的主要用例之一。
target
,则必须通过代理进行修改。但是,proxy.hello_world = "test"
这并不意味着您正在修改代理,该代理保持不变,target
会被修改(如果您的设置处理程序已配置为执行此操作)。听起来您的观点是您无法直接观察target.hello_world = "test"
。那是真实的。纯变量分配不会发出任何类型的事件。这就是为什么我们必须使用该问题的答案中所述的工具。
It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true.
这就是我的意思。在我的情况下,我在其他地方创建了一个对象,并通过其他一些代码对其进行了更新……在这种情况下,代理没有用,因为更改将在目标上完成。
没有。
但是,如果真的那么重要,则有两种选择(第一种经过测试,第二种未经测试):
首先,使用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/zh-CN/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参数调用的函数。
抱歉,打了个旧话题,但是对于那些不了解Eli Grey的示例如何工作的人,这里有一些手册:
var test = new Object();
test.watch("elem", function(prop,oldval,newval){
//Your code
return newval;
});
希望这可以帮助某人
作为Luke Schafer的答案(请注意:这是他的原始帖子;但是,此处的要点在编辑后仍然有效),我还建议使用一对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
};
})();
现在,您可以使用与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}(每个人都应该使用:-)),则可以将.change()与隐藏的<input />元素一起使用。
<input/>
元素?
<input type="hidden" value="" id="thisOne" />
并使用jQuery $("#thisOne").change(function() { do stuff here });
,$("#thisOne").val(myVariableWhichIsNew);
然后.change()
将触发。
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');
对于几年后的调优:
提供了适用于大多数浏览器(和IE6 +)的解决方案,该解决方案使用onpropertychange事件和更新的规范defineProperty。轻微的缺点是您需要使变量成为dom对象。
详细信息:
http://johndyer.name/native-browser-get-set-properties-in-javascript/
您正在寻找的功能可以通过使用“ defineProperty()”方法来实现,该方法仅适用于现代浏览器:
如果您需要更多的跨浏览器支持,我已经编写了一个具有一些类似功能的jQuery扩展:
https://github.com/jarederaj/jQueue
一个小的jQuery扩展,用于处理对存在变量,对象或键的队列回调。您可以将任意数量的回调分配给可能受后台运行的进程影响的任意数量的数据点。jQueue监听并等待您指定的这些数据存在,然后使用其参数触发正确的回调。
//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方法中……只是一个想法。
var
ES6模块中声明的变量一样),这是唯一的解决方案。
请大家记住最初的问题是变量,而不是对象;)
除了上述所有答案外,我还创建了一个名为forTheWatch.js的小型库,该库使用相同的方式来捕获和回调javascript中常规全局变量的更改。
与JQUERY变量兼容,无需使用OBJECTS,如果需要,您可以直接传递多个变量的ARRAY。
如果有帮助...:https :
//bitbucket.org/esabora/forthewatch
基本上,您只需要调用该函数:
watchIt("theVariableToWatch", "varChangedFunctionCallback");
如果不相关,请提前抱歉。
从这个答案开始,我找到了最简单的方法:
// variable holding your data
const state = {
count: null,
update() {
console.log(`this gets called and your value is ${this.pageNumber}`);
},
get pageNumber() {
return this.count;
},
set pageNumber(pageNumber) {
this.count = pageNumber;
// here you call the code you need
this.update(this.count);
}
};
然后:
state.pageNumber = 0;
// watch the console
state.pageNumber = 15;
// watch the console
就我而言,我试图找出我在项目中包含的任何图书馆是否正在重新定义我的window.player
。因此,在我的代码开始时,我只是做了:
Object.defineProperty(window, 'player', {
get: () => this._player,
set: v => {
console.log('window.player has been redefined!');
this._player = v;
}
});
这不是直接可能的。
但是,可以使用CustomEvent完成此操作:https : //developer.mozilla.org/zh-CN/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的更改。
借助getter和setter,您可以定义一个执行此类操作的JavaScript类。
首先,我们定义我们的类MonitoredVariable
:
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;
}
}
假设我们想听money
变化,让我们创建一个MonitoredVariable
初始资金为的实例0
:
const money = new MonitoredVariable(0);
然后我们可以使用money.val
以下方法获取或设置其值:
console.log(money.val); // Get its value
money.val = 2; // Set its value
由于我们尚未为其定义任何侦听器,因此money.val
更改为2 后不会发生任何特殊情况。
现在让我们定义一些监听器。我们有四个听众提供:beforeSet
,beforeChange
,afterChange
,afterSet
。当您money.val = newValue
用来更改变量的值时,将依次发生以下情况:
现在,我们定义afterChange
仅在money.val
更改后才触发的侦听器(afterSet
即使新值与旧值相同,也会被触发):
money.afterChange = (newValue, oldValue) => {
console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};
现在设置一个新值3
,看看会发生什么:
money.val = 3;
您将在控制台中看到以下内容:
Money has been changed from 2 to 3
有关完整代码,请参见https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab。
这是一个旧线程,但是我在寻找使用Angular的解决方案时偶然发现了第二高的答案(自定义侦听器)。虽然该解决方案有效,但是angular具有更好的内置方式来使用@Output
and事件发射器来解决此问题。在自定义侦听器中回答示例:
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]);}
});
}