Apakah mungkin untuk memiliki acara di JS yang menyala ketika nilai variabel tertentu berubah? JQuery diterima.
variable
, namun semua jawaban di sini merujuk property
. Saya ingin tahu apakah kita dapat mendengarkan local variable
perubahan.
Apakah mungkin untuk memiliki acara di JS yang menyala ketika nilai variabel tertentu berubah? JQuery diterima.
variable
, namun semua jawaban di sini merujuk property
. Saya ingin tahu apakah kita dapat mendengarkan local variable
perubahan.
Jawaban:
Ya, ini sekarang sangat mungkin!
Saya tahu ini adalah utas lama tetapi sekarang efek ini dimungkinkan menggunakan pengakses (getter dan setter): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters
Anda bisa mendefinisikan objek seperti ini, yang aInternal
mewakili bidang 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;
}
}
Kemudian Anda dapat mendaftarkan pendengar menggunakan yang berikut:
x.registerListener(function(val) {
alert("Someone changed the value of x.a to " + val);
});
Jadi, setiap kali ada perubahan nilai x.a
, fungsi pendengar akan diaktifkan. Menjalankan baris berikut akan memunculkan popup peringatan:
x.a = 42;
Lihat contoh di sini: https://jsfiddle.net/5o1wf1bn/1/
Anda juga dapat menggunakan array pendengar alih-alih slot pendengar tunggal, tapi saya ingin memberi Anda contoh paling sederhana.
this.aListener(val)
, Anda harus mengulang semua fungsi pendengar dan memanggil masing-masing lewat val
. Biasanya, metode ini disebut addListener
bukan registerListener
.
Sebagian besar jawaban untuk pertanyaan ini sudah usang, tidak efektif, atau memerlukan dimasukkannya perpustakaan besar yang membengkak:
Hari ini, Anda sekarang dapat menggunakan objek Proxy untuk memantau (dan mencegat) perubahan yang dilakukan pada suatu objek. Ini adalah tujuan yang dibangun untuk apa yang OP coba lakukan. Inilah contoh dasar:
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'
Satu-satunya kelemahan Proxy
objek adalah:
Proxy
objek tidak tersedia di browser lama (seperti IE11) dan polyfill tidak bisa sepenuhnya meniruProxy
fungsi.Date
) - Proxy
objek paling baik dipasangkan dengan Objek atau Array.Jika Anda perlu mengamati perubahan yang dilakukan pada objek bersarang , maka Anda perlu menggunakan perpustakaan khusus seperti Observable Slim (yang telah saya terbitkan) yang berfungsi seperti ini:
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
- itu tidak cukup akurat. The Proxy
objek tidak dimodifikasi - itu tidak memiliki salinan itu sendiri dari target. you just want to know when a property change on the target object
- Anda dapat mencapainya dengan Proxy
, itu salah satu kasus penggunaan utama untuk proxy.
target
, maka Anda harus melakukannya melalui proxy. Namun proxy.hello_world = "test"
tidak berarti Anda memodifikasi proxy, proxy tetap tidak berubah, target
akan dimodifikasi (jika set handler Anda dikonfigurasi untuk melakukannya). Sepertinya poin Anda adalah bahwa Anda tidak dapat mengamati secara langsung target.hello_world = "test"
. Itu benar. Tugas variabel biasa tidak memancarkan jenis acara apa pun. Itu sebabnya kita harus menggunakan alat seperti yang dijelaskan dalam jawaban untuk pertanyaan ini.
It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true.
itu maksud saya. Dalam kasus saya, saya memiliki objek yang dibuat di tempat lain dan telah diperbarui oleh beberapa kode lain ... proxy, dalam hal ini, tidak berguna karena perubahan akan dilakukan pada target.
Tidak.
Tapi, jika itu benar-benar penting, Anda memiliki 2 opsi (pertama diuji, kedua tidak):
Pertama, gunakan setter dan getter, seperti:
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);
maka Anda dapat melakukan sesuatu seperti:
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));
}
kemudian atur pendengar seperti:
listen_to(myobj, "a", function(oldval, newval) {
alert("old : " + oldval + " new : " + newval);
}
Kedua, saya benar-benar lupa, saya akan serahkan sementara saya memikirkannya :)
EDIT: Oh, saya ingat :) Anda bisa memberi nilai pada jam tangan:
Diberikan myobj di atas, dengan 'a' di atasnya:
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);
Menggunakan 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>
varw
membutuhkan 2 argumen tetapi bagian dari contoh Anda menunjukkan fungsi dipanggil hanya dengan parameter nilai.
Maaf untuk membuka utas lama, tetapi di sini ada manual kecil bagi mereka yang (seperti saya!) Tidak melihat bagaimana contoh kerja Eli Grey:
var test = new Object();
test.watch("elem", function(prop,oldval,newval){
//Your code
return newval;
});
Semoga ini bisa membantu seseorang
Sebagai jawaban Luke Schafer ( catatan : ini merujuk pada posting aslinya; tetapi seluruh poin di sini tetap valid setelah diedit ), saya juga menyarankan sepasang metode Get / Set untuk mengakses nilai Anda.
Namun saya akan menyarankan beberapa modifikasi (dan itulah sebabnya saya memposting ...).
Masalah dengan kode itu adalah bahwa bidang a
objek myobj
dapat diakses secara langsung, sehingga dimungkinkan untuk mengaksesnya / mengubah nilainya tanpa memicu pendengar:
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!
Jadi, untuk menjamin bahwa pendengar benar-benar dipanggil, kita harus melarang akses langsung ke lapangan a
. Bagaimana cara melakukannya? Gunakan penutupan !
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
};
})();
Sekarang Anda dapat menggunakan metode yang sama untuk membuat dan menambahkan pendengar seperti yang diusulkan Luke, tetapi Anda dapat yakin bahwa tidak ada cara yang mungkin untuk membaca atau menulis agar a
tidak diperhatikan!
Masih di jalur Luke, sekarang saya usulkan cara sederhana untuk menambahkan bidang enkapsulasi dan masing-masing getter / setter ke objek dengan cara pemanggilan fungsi sederhana.
Perhatikan bahwa ini hanya akan berfungsi dengan baik dengan tipe nilai . Agar ini bekerja dengan tipe referensi , beberapa jenis salinan yang dalam harus diimplementasikan (lihat yang ini , misalnya).
function addProperty(obj, name, initial) {
var field = initial;
obj["get_" + name] = function() { return field; }
obj["set_" + name] = function(val) { field = val; }
}
Ini berfungsi sama seperti sebelumnya: kita membuat variabel lokal pada suatu fungsi, dan kemudian kita membuat penutupan.
Bagaimana cara menggunakannya? Sederhana:
var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
Jika Anda menggunakan jQuery {UI} (yang harus digunakan semua orang :-)), Anda dapat menggunakan .change () dengan elemen <input /> tersembunyi.
<input/>
elemen tersembunyi ?
<input type="hidden" value="" id="thisOne" />
dan dengan jQuery $("#thisOne").change(function() { do stuff here });
dan $("#thisOne").val(myVariableWhichIsNew);
dan kemudian .change()
akan api.
var1 = 'new value';
, Anda malah akan menetapkan nilai input tersembunyi ini, lalu menambahkan pendengar untuk mengubah variabel. $("#val1").on('change', function(){ val1 = this.val(); ... do the stuff that you wanted to do when val1 changed... }); $("#val1").val('new value');
AngularJS
(Saya tahu ini bukan JQuery
, tapi itu mungkin bisa membantu. [JS Murni hanya baik secara teori]):
$scope.$watch('data', function(newValue) { ..
di mana "data" adalah nama variabel Anda dalam cakupan.
Ada tautan ke doc.
$scope.$apply()
dijalankan
Untuk penyetelan beberapa tahun kemudian:
Sebuah solusi untuk sebagian besar browser (dan IE6 +) tersedia yang menggunakan event onpropertychange dan spec defineProperty yang lebih baru. Tangkapan kecil adalah bahwa Anda harus membuat variabel Anda objek dom.
Detail lengkap:
http://johndyer.name/native-browser-get-set-properties-in-javascript/
Fungsionalitas yang Anda cari dapat dicapai melalui penggunaan metode "defineProperty ()" - yang hanya tersedia untuk browser modern:
Saya telah menulis ekstensi jQuery yang memiliki beberapa fungsi serupa jika Anda memerlukan lebih banyak dukungan lintas browser:
https://github.com/jarederaj/jQueue
Ekstensi jQuery kecil yang menangani antrean panggilan balik ke keberadaan variabel, objek, atau kunci. Anda dapat menetapkan sejumlah panggilan balik ke sejumlah titik data yang mungkin dipengaruhi oleh proses yang berjalan di latar belakang. jQueue mendengarkan dan menunggu data yang Anda tentukan muncul dan kemudian memecat callback yang benar dengan argumennya.
//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();
}
};
atau
/* 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);
atau
/* 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);
Solusi yang agak sederhana dan sederhana adalah dengan hanya menggunakan pemanggilan fungsi untuk mengatur nilai variabel global, dan tidak pernah menetapkan nilainya secara langsung. Dengan cara ini Anda memiliki kontrol total:
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
Tidak ada cara untuk menegakkan ini, itu hanya membutuhkan disiplin pemrograman ... meskipun Anda dapat menggunakan grep
(atau sesuatu yang serupa) untuk memeriksa bahwa tidak ada kode yang langsung mengatur nilaiglobalVar
.
Atau Anda bisa merangkumnya dalam objek dan metode pengambil dan penyetel pengguna ... hanya pemikiran.
var
dalam modul ES6 - ini adalah satu - satunya solusi.
Tolong ingat pertanyaan awal adalah untuk VARIABEL, bukan untuk BENDA;)
selain semua jawaban di atas, saya membuat lib kecil bernama forTheWatch.js , yang menggunakan cara yang sama untuk menangkap dan balik untuk perubahan dalam variabel global normal dalam javascript.
Kompatibel dengan variabel JQUERY, tidak perlu menggunakan OBYEK, dan Anda dapat melewati secara langsung ARRAY dari beberapa variabel jika diperlukan.
Jika ini bisa membantu ...:
https://bitbucket.org/esabora/forthewatch
Pada dasarnya Anda hanya perlu memanggil fungsi:
watchIt("theVariableToWatch", "varChangedFunctionCallback");
Dan maaf sebelumnya jika tidak relevan.
Cara termudah yang saya temukan, mulai dari jawaban ini :
// 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);
}
};
Lalu:
state.pageNumber = 0;
// watch the console
state.pageNumber = 15;
// watch the console
Dalam kasus saya, saya mencoba mencari tahu apakah ada perpustakaan yang saya masukkan dalam proyek saya yang mendefinisikan ulang perpustakaan saya window.player
. Jadi, pada awal kode saya, saya baru saja melakukannya:
Object.defineProperty(window, 'player', {
get: () => this._player,
set: v => {
console.log('window.player has been redefined!');
this._player = v;
}
});
Itu tidak mungkin secara langsung.
Namun, ini dapat dilakukan dengan menggunakan CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
Metode di bawah ini menerima array nama variabel sebagai input dan menambahkan pendengar acara untuk setiap variabel dan memicu acara untuk setiap perubahan pada nilai variabel.
Metode menggunakan polling untuk mendeteksi perubahan nilai. Anda dapat meningkatkan nilai waktu habis dalam milidetik.
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);
}
Dengan memanggil Metode:
watchVariables(['username', 'userid']);
Ini akan mendeteksi perubahan pada variabel username dan userid.
Dengan bantuan pengambil dan penyetel , Anda dapat menentukan kelas JavaScript yang melakukan hal seperti itu.
Pertama, kita mendefinisikan kelas kita yang disebut 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;
}
}
Anggap kita ingin mendengarkan money
perubahan, mari kita membuat contoh MonitoredVariable
dengan uang awal 0
:
const money = new MonitoredVariable(0);
Lalu kita bisa mendapatkan atau mengatur nilainya menggunakan money.val
:
console.log(money.val); // Get its value
money.val = 2; // Set its value
Karena kami belum menetapkan pendengar untuk itu, tidak ada yang istimewa terjadi setelah money.val
perubahan ke 2.
Sekarang mari kita mendefinisikan beberapa pendengar. Kami memiliki empat pendengar yang tersedia: beforeSet
, beforeChange
, afterChange
, afterSet
. Berikut ini akan terjadi secara berurutan ketika Anda digunakan money.val = newValue
untuk mengubah nilai variabel:
Sekarang kita mendefinisikan afterChange
pendengar yang dipicu hanya setelah money.val
telah berubah (sementara afterSet
akan dipicu bahkan jika nilai baru sama dengan yang lama):
money.afterChange = (newValue, oldValue) => {
console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};
Sekarang tetapkan nilai baru 3
dan lihat apa yang terjadi:
money.val = 3;
Anda akan melihat yang berikut di konsol:
Money has been changed from 2 to 3
Untuk kode lengkap, lihat https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab .
Ini adalah utas lama tapi saya menemukan jawaban tertinggi kedua (pendengar khusus) sambil mencari solusi menggunakan Angular. Sementara solusinya bekerja, angular memiliki cara yang lebih baik untuk menyelesaikan penggunaan @Output
dan pemancar acara ini. Keluar dari contoh dalam jawaban pendengar khusus:
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);
}
Ini sekarang akan diperbarui n
pada orang tua setiap kali tombol anak diklik. Bekerja stackblitz
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]);}
});
}