是否有可能在JS中有一个事件,当某个变量的值发生变化时触发?JQuery被接受。
当前回答
最近我发现自己也有同样的问题。想要监听变量的变化并在变量变化时做一些事情。
有人提出了一个使用setter设置值的简单解决方案。
声明一个简单的对象来保存变量的值:
var variableObject = {
value: false,
set: function (value) {
this.value = value;
this.getOnChange();
}
}
对象包含一个set方法,通过该方法可以更改值。但是它也调用了一个getOnChange()方法。将定义它。
variableObject.getOnChange = function() {
if(this.value) {
// do some stuff
}
}
现在,每当我做variableObject.set(true)时,getOnChange方法就会触发,如果值被设置为所需的值(在我的例子中:true), if块也会执行。
这是我发现的最简单的方法。
其他回答
@akira和@mo-d-genesis的解决方案可以进一步简化,因为在这个例子中DOM操作不依赖于状态:
CodePen
const render = (val) => {
document.getElementById("numberDiv").innerHTML = val;
};
state = {
_value_internal: undefined,
set value(val) {
// 1. set state value
this._value_internal = val;
// 2. render user interface
render(val);
},
get value() {
return this._value_internal;
},
};
const onClick = () => {
state.value = state.value + 1; // state change leads to re-render!
};
// set default value
state.value = 0;
对应的html:
<div id="numberDiv"></div>
<button onclick="onClick()">
Click to rerender
</button>
备注:
我重命名了变量和函数,以更好地反映它们的语义。 仅供参考:通过改变变量,Svelte提供了非常类似的反应行为
一个相当简单和简单的解决方案是只使用函数调用来设置全局变量的值,而不要直接设置它的值。这样你就可以完全控制:
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
没有办法强制执行,它只需要编程纪律……尽管你可以使用grep(或类似的东西)来检查你的代码没有直接设置globalVar的值。
或者你可以把它封装在一个对象和用户getter和setter方法中…只是一个想法。
如果你正在使用jQuery {UI}(每个人都应该使用:-)),你可以使用.change()和一个隐藏的<input/>元素。
对于那些几年后收听的人来说:
大多数浏览器(和IE6+)都有一个解决方案,它使用onpropertychange事件和更新的规范defineProperty。有一点需要注意的是,您需要将变量设置为dom对象。
详情:
http://johndyer.name/native-browser-get-set-properties-in-javascript/
这是一个古老而伟大的问题,已经超过12年了。此外,有很多方法来解决它。然而,大多数都是复杂的或使用旧的JS概念,我们在2022年,我们可以使用ES6来改进我们的代码。
我将实现我经常使用的两个主要解决方案。
简单的变量
如果我们有一个简单的变量,我们不关心重用,那么我们可以将变量声明为一个对象。我们定义了一个set和get方法以及一个listener属性来处理“change”事件。
const $countBtn = document.getElementById('counter') const $output = document.getElementById('output') const counter = { v: 0, listener: undefined, set value(v) { this.v = v if (this.listener) this.listener(v) }, get value() { return this.v }, count() { this.value++ }, registerListener(callback) { this.listener = callback }, } const countOnClick = () => { counter.count() } $countBtn.onclick = countOnClick counter.registerListener(v => { $output.textContent = v }) counter.value = 50 #output { display: block; font-size: 2em; margin-top: 0.67em; margin-bottom: 0.67em; margin-left: 0; margin-right: 0; font-weight: bold; } <button id="counter">Count</button> <div id="output"></div>
用于重用的高级类
如果我们有多个变量,并且需要监视它们,我们可以创建一个类,然后将其应用于我们的变量。我建议在change之前和change之后添加两个侦听器,这将使您在不同的过程中灵活地使用变量。
class ObservableObject { constructor(v) { this.v = v ?? 0 this.on = { beforeChange(newValue, oldValue) {}, afterChange(newValue, oldValue) {}, } } set value(newValue) { const oldValue = this.v // newValue, oldValue are the same if (oldValue === newValue) return this.on.beforeChange(newValue, oldValue) this.v = newValue this.on.afterChange(newValue, oldValue) } get value() { return this.v } } const $countABtn = document.getElementById('counter-a') const $countBBtn = document.getElementById('counter-b') const $outputA = document.getElementById('output-a') const $outputB = document.getElementById('output-b') const counterA = new ObservableObject() const counterB = new ObservableObject() const countOnClick = counter => { counter.value++ } const onChange = (v, output) => { output.textContent = v } $countABtn.onclick = () => { countOnClick(counterA) } $countBBtn.onclick = () => { countOnClick(counterB) } counterA.on.afterChange = v => { onChange(v, $outputA) } counterB.on.afterChange = v => { onChange(v, $outputB) } counterA.value = 50 counterB.value = 20 .wrapper { display: flex; flex-flow: row wrap; justify-content: center; align-items: center; width: 100vw } .item { width: 50% } .output { display: block; font-size: 2em; margin-top: 0.67em; margin-bottom: 0.67em; margin-left: 0; margin-right: 0; font-weight: bold; } <div class="wrapper"> <div class="item"> <button id="counter-a">Count A</button> <div id="output-a" class="output"></div> </div> <div class="item"> <button id="counter-b">Count B</button> <div id="output-b" class="output"></div> </div> </div>