JS'de belirli bir değişkenin değeri değiştiğinde tetiklenen bir olay olabilir mi? JQuery kabul edilir.
variable
, ancak buradaki tüm cevaplar atıfta bulunuyor property
. local variable
Değişiklikleri dinleyip dinleyemeyeceğimizi merak ediyorum .
JS'de belirli bir değişkenin değeri değiştiğinde tetiklenen bir olay olabilir mi? JQuery kabul edilir.
variable
, ancak buradaki tüm cevaplar atıfta bulunuyor property
. local variable
Değişiklikleri dinleyip dinleyemeyeceğimizi merak ediyorum .
Yanıtlar:
Evet, bu artık tamamen mümkün!
Bu eski bir iş parçacığı olduğunu biliyorum ama şimdi bu etki erişimciler (alıcılar ve ayarlayıcılar) kullanarak mümkündür: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters
aInternal
Alanı temsil eden şöyle bir nesne tanımlayabilirsiniz 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;
}
}
Ardından aşağıdakileri kullanarak bir dinleyici kaydedebilirsiniz:
x.registerListener(function(val) {
alert("Someone changed the value of x.a to " + val);
});
Böylece, herhangi bir değer değerini değiştirdiğinde, x.a
dinleyici işlevi tetiklenir. Aşağıdaki satırı çalıştırmak uyarı açılır penceresini getirecektir:
x.a = 42;
Buradaki bir örneğe bakın: https://jsfiddle.net/5o1wf1bn/1/
Tek bir dinleyici yuvası yerine bir dizi dinleyici de kullanabilirsiniz, ancak size mümkün olan en basit örneği vermek istedim.
this.aListener(val)
aramak yerine, tüm dinleyici işlevleri arasında dolaşmanız ve her birini çağırmanız gerekir val
. Genellikle, yöntem addListener
yerine çağrılır registerListener
.
Bu soruya verilen cevapların çoğu eski, etkisizdir veya büyük şişirilmiş kütüphanelerin dahil edilmesini gerektirir:
Bugün, artık bir nesnede yapılan değişiklikleri izlemek (ve kesmek) için Proxy nesnesini kullanabilirsiniz . OP'nin yapmaya çalıştığı amaç için üretilmiştir. İşte temel bir örnek:
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
Nesnenin tek dezavantajı :
Proxy
Nesne (örneğin IE11 gibi) eski tarayıcılarda kullanılamaz ve polyfill can tamamen çoğaltmak değil Proxy
işlevsellik.Date
) Beklendiği gibi davranmaz - Proxy
nesne en iyi şekilde düz Nesneler veya Dizilerle eşleştirilir.Yuvalanmış bir nesnede yapılan değişiklikleri gözlemlemeniz gerekiyorsa, aşağıdaki gibi Gözlenebilir İnce (yayınladığım) gibi özel bir kitaplık kullanmanız gerekir :
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
- bu pek doğru değil. Proxy
Nesne değiştirilmez - bu hedefin o kendi kopyasını yoktur. you just want to know when a property change on the target object
- bunu a ile yapabilirsiniz Proxy
, bu proxy'ler için birincil kullanım durumlarından biridir.
target
istiyorsanız, bir proxy üzerinden yapmanız gerekir. Bununla birlikte proxy.hello_world = "test"
, proxy'yi değiştirdiğiniz anlamına gelmez, proxy değişmeden kalır, target
değiştirilir (ayarlanan işleyiciniz bunu yapacak şekilde yapılandırılmışsa). Demek istediğin, doğrudan gözlemleyemediğin gibi geliyor target.hello_world = "test"
. Bu doğru. Basit değişken atamaları herhangi bir olay yayınlamaz. Bu yüzden bu sorunun cevaplarında açıklananlar gibi araçlar kullanmalıyız.
It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true.
Bu benim amacım. Benim durumumda başka bir yerde oluşturulan ve başka bir kod tarafından güncellenen bir nesnem var ... bu durumda, değişiklikler hedefe yapılacak çünkü bir proxy, yararlı değil.
Hayır.
Ancak, gerçekten bu kadar önemliyse, 2 seçeneğiniz var (ilk test edilir, ikincisi değil):
İlk olarak ayarlayıcıları ve alıcıları aşağıdaki gibi kullanın:
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);
o zaman şöyle bir şey yapabilirsiniz:
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));
}
sonra dinleyiciyi aşağıdaki gibi ayarlayın:
listen_to(myobj, "a", function(oldval, newval) {
alert("old : " + oldval + " new : " + newval);
}
İkincisi, aslında unuttum, düşünürken göndereceğim :)
EDIT: Oh, hatırlıyorum :) Sen değerine bir saat koyabilirsiniz:
Yukarıda 'm' ile myobj verildiğinde:
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);
Kullanarak Prototype
: https://developer.mozilla.org/tr-TR/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 argüman gerektirir , ancak örneğinizin bir kısmı sadece value parametresiyle çağrılan işlevi gösterir.
Eski bir konu açtığım için üzgünüm, ama işte (benim gibi!) Eli Grey'in örneğinin nasıl çalıştığını görmeyenler için küçük bir kılavuz:
var test = new Object();
test.watch("elem", function(prop,oldval,newval){
//Your code
return newval;
});
Umarım bu birine yardımcı olabilir
As Luke Schafer cevabı ( not : bu onun orijinal yayına gelir; ancak bütün mesele burada düzenleme sonrasında geçerli kalır ), ben de al bir çift / değerinizi erişmek için set yöntemleri öneririm.
Ancak bazı değişiklikler öneriyorum (ve bu yüzden gönderiyorum ...).
Bu kodla ilgili bir sorun a
, nesnenin alanına myobj
doğrudan erişilebilmesidir, bu nedenle dinleyicileri tetiklemeden ona erişmek / değerini değiştirmek mümkündür:
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!
Bu nedenle, dinleyicilerin gerçekten çağrıldığını garanti etmek için, alana doğrudan erişimi yasaklamak zorundayız a
. Nasıl yapılır? Bir kapak kullanın !
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
};
})();
Artık Luke'un önerdiği dinleyicileri oluşturmak ve eklemek için aynı yöntemi kullanabilirsiniz, ancak a
fark edilmeden okumak veya yazmak için olası bir yol olmadığından emin olabilirsiniz !
Hala Luke'un yolunda, şimdi basit bir fonksiyon çağrısı ile kapsüllenmiş alanları ve ilgili alıcıları / ayarlayıcıları nesnelere eklemenin basit bir yolunu öneriyorum.
Bunun yalnızca değer türleriyle düzgün çalışacağını unutmayın . İle çalışmalarına Bunun için başvuru türleri , bir tür derin kopya (bkz uygulanacak olurdu bu bir örneğin).
function addProperty(obj, name, initial) {
var field = initial;
obj["get_" + name] = function() { return field; }
obj["set_" + name] = function(val) { field = val; }
}
Bu, öncekiyle aynı şekilde çalışır: bir işlev üzerinde yerel bir değişken oluştururuz ve sonra bir kapanış oluştururuz.
Bu nasıl kullanılır? Basit:
var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
JQuery {UI} (herkesin :-) kullanması gerekir) kullanıyorsanız, gizli bir <input /> öğesi ile .change () kullanabilirsiniz.
<input/>
öğeye nasıl değişken ekleyebilirsiniz ?
<input type="hidden" value="" id="thisOne" />
ve jQuery $("#thisOne").change(function() { do stuff here });
ile $("#thisOne").val(myVariableWhichIsNew);
ve sonra .change()
irade ateş.
var1 = 'new value';
, bu gizli girişin değerini ayarlayacak ve daha sonra değişkeni değiştirmek için bir dinleyici ekleyeceksiniz. $("#val1").on('change', function(){ val1 = this.val(); ... do the stuff that you wanted to do when val1 changed... }); $("#val1").val('new value');
AngularJS
(Bunun olmadığını biliyorum JQuery
, ama bu yardımcı olabilir. [Pure JS sadece teoride iyidir]):
$scope.$watch('data', function(newValue) { ..
burada "veri", kapsamdaki değişkeninizin adıdır.
Doc bağlantısı var .
$scope.$apply()
çalıştırıldığında
Birkaç yıl sonra ayar yapanlar için:
Çoğu tarayıcı (ve IE6 +) için onpropertychange olayını ve daha yeni spec defineProperty'yi kullanan bir çözüm mevcuttur. Hafif yakalama, değişkeninizi dom nesnesi yapmanız gerektiğidir.
Tüm detaylar:
http://johndyer.name/native-browser-get-set-properties-in-javascript/
Aradığınız işlevsellik, yalnızca modern tarayıcılar tarafından kullanılabilen "defineProperty ()" yöntemini kullanarak elde edilebilir:
Daha fazla çapraz tarayıcı desteğine ihtiyacınız varsa benzer işlevlere sahip bir jQuery uzantısı yazdım:
https://github.com/jarederaj/jQueue
Değişken, nesne veya anahtarın varlığına sıraya giren geri çağrıları işleyen küçük bir jQuery uzantısı. Arka planda çalışan işlemlerden etkilenebilecek herhangi bir sayıda veri noktasına istediğiniz sayıda geri arama atayabilirsiniz. jQueue dinlediğinizi belirttiğiniz bu verileri dinler ve bekler ve ardından argümanlarıyla doğru geri aramayı başlatır.
//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();
}
};
veya
/* 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);
veya
/* 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);
Oldukça basit ve basit bir çözüm, global değişkenin değerini ayarlamak için sadece bir işlev çağrısı kullanmak ve değerini asla doğrudan ayarlamamaktır. Bu şekilde tam kontrole sahip olursunuz:
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
Bunu zorlamanın bir yolu yoktur, sadece programlama disiplini gerektirir ... ancak grep
kodunuzun hiçbir yerde doğrudan değerini ayarlamadığını kontrol etmek için kullanabilirsiniz (veya benzer bir şey) globalVar
.
Ya da bir nesneyi ve kullanıcı alıcı ve ayarlayıcı yöntemlerini içine alabilirsin ... sadece bir düşünce.
var
, ES6 modüllerinde bildirilen değişkenlerde olduğu gibi, bu tek çözümdür.
Lütfen ilk sorunun NESNELER için değil DEĞİŞKENLER için olduğunu unutmayın;)
Yukarıdaki tüm cevaplara ek olarak , javascript normal küresel değişkenlerdeki değişiklikleri yakalamak ve geri çağırmak için aynı yolu kullanan forTheWatch.js adlı küçük bir lib oluşturdum .
JQUERY değişkenleriyle uyumludur, NESNELER kullanmaya gerek yoktur ve gerekirse birkaç değişkenin doğrudan bir ARRAY'ini geçirebilirsiniz.
Yararlı olabilirse ...:
https://bitbucket.org/esabora/forthewatch
Temel olarak sadece işlevi çağırmanız gerekir:
watchIt("theVariableToWatch", "varChangedFunctionCallback");
Ve ilgili değilse önceden peşin üzgünüm.
Bu cevaptan başlayarak bulduğum en kolay yol :
// 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);
}
};
Ve sonra:
state.pageNumber = 0;
// watch the console
state.pageNumber = 15;
// watch the console
Benim durumumda, projeme dahil ettiğim herhangi bir kütüphanenin yeniden tanımlanıp tanımlanmadığını öğrenmeye çalışıyordum window.player
. Yani, kodumun başında, ben sadece yaptım:
Object.defineProperty(window, 'player', {
get: () => this._player,
set: v => {
console.log('window.player has been redefined!');
this._player = v;
}
});
Doğrudan mümkün değil.
Ancak, bu CustomEvent kullanılarak yapılabilir: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
Aşağıdaki yöntem, değişken adları dizisini girdi olarak kabul eder ve her değişken için olay dinleyicisi ekler ve değişkenin değerinde herhangi bir değişiklik olması için olayı tetikler.
Yöntem, değerdeki değişikliği algılamak için yoklama kullanır. Zaman aşımı değerini milisaniye cinsinden artırabilirsiniz.
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);
}
Yöntemi çağırarak:
watchVariables(['username', 'userid']);
Kullanıcı adı ve kullanıcı kimliği değişkenlerindeki değişiklikleri tespit edecektir.
Alıcı ve ayarlayıcı yardımıyla böyle bir şey yapan bir JavaScript sınıfı tanımlayabilirsiniz.
İlk olarak, sınıfımızı şöyle tanımlarız 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
Değişiklikleri dinlemek istediğimizi varsayalım, MonitoredVariable
ilk parayla bir örnek oluşturalım 0
:
const money = new MonitoredVariable(0);
Sonra değerini kullanarak veya ayarlayabiliriz money.val
:
console.log(money.val); // Get its value
money.val = 2; // Set its value
Bunun için herhangi bir dinleyici tanımlamadığımızdan, money.val
2'de yapılan değişikliklerden sonra özel bir şey olmaz .
Şimdi bazı dinleyicileri tanımlayalım. Biz dört dinleyici mevcut vardır: beforeSet
, beforeChange
, afterChange
, afterSet
. money.val = newValue
Değişkenin değerini değiştirmek için kullandığınızda aşağıdakiler sırayla gerçekleşir :
Şimdi afterChange
sadece money.val
değiştikten sonra tetiklenecek dinleyiciyi tanımlıyoruz ( afterSet
yeni değer eskisiyle aynı olsa bile tetiklenecek):
money.afterChange = (newValue, oldValue) => {
console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};
Şimdi yeni bir değer belirleyin 3
ve neler olduğunu görün:
money.val = 3;
Konsolda aşağıdakileri göreceksiniz:
Money has been changed from 2 to 3
Kodun tamamı için bkz. Https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab .
Bu eski bir konu ama Angular kullanarak bir çözüm ararken ikinci en yüksek cevaba (özel dinleyiciler) rastladım. Çözüm çalışırken, açısal bu kullanımı @Output
ve olay yayıcıları çözmek için daha iyi bir şekilde inşa edilmiştir . Özel dinleyici yanıtında örnekten vazgeçme:
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);
}
Bu n
, alt düğmeye her tıklandığında ebeveyn üzerinde güncellenecektir . Çalışma yığı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]);}
});
}