การฟังการเปลี่ยนแปลงตัวแปรใน JavaScript


462

เป็นไปได้ไหมที่จะมีเหตุการณ์ใน JS ที่เริ่มทำงานเมื่อค่าของตัวแปรเปลี่ยนแปลงไป? ได้รับการยอมรับ JQuery


@BenjaminGruenbaum คุณอาจต้องการพูด MutableObserver (สำหรับ DOM) Object เป็นเพียงวัตถุ JS จากสิ่งที่ฉันจำได้
HellBaby

2
@HellBaby คำถามนี้เกี่ยวกับตัวแปร - ไม่ใช่ DOM
Benjamin Gruenbaum

9
@BenjaminGruenbaum ตามdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Object.observe ล้าสมัยหรือเลิกใช้แล้ว การแทนที่ที่แนะนำ (สำหรับหน้าเดียวกันนั้น) คือวัตถุพร็อกซี
stannius

4
คำถามที่ถามเกี่ยวกับเพียงแต่ทุกคำตอบที่นี่หมายถึงvariable propertyฉันสงสัยว่าเราสามารถฟังlocal variableการเปลี่ยนแปลงได้หรือไม่
Thariq Nugrohotomo

1
มีคำตอบสำหรับสิ่งนี้หรือไม่? ฉันหมายถึงการเลือกหนึ่ง
Braian Mellor

คำตอบ:


100

ใช่ตอนนี้เป็นไปได้อย่างสมบูรณ์!

ฉันรู้ว่านี่เป็นเธรดเก่า แต่ตอนนี้เอฟเฟกต์นี้เป็นไปได้โดยใช้ accessors (getters และ setters): https://developer.mozilla.org/en-US/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/

คุณสามารถใช้อาร์เรย์ของผู้ฟังแทนการฟังสล็อตเดียว แต่ฉันต้องการให้ตัวอย่างที่ง่ายที่สุดที่เป็นไปได้


6
นี่เป็นโค้ดจำนวนมากต่อวัตถุมีวิธีทำให้สามารถใช้ซ้ำได้อีกหรือไม่
Vael Victus

รหัสของคุณอยู่ที่ไหน คุณเริ่มต้นคำถามใหม่จากมันหรือไม่?
อากิระ

วิธีการตรวจสอบเมื่อมีการเพิ่มคุณสมบัติใหม่ไปยังวัตถุหรือถูกลบออกไป?
Michael

นี่เป็นคำตอบเก่า แต่ฉันต้องการเพิ่มว่ามันทำงานได้ดีสำหรับค่าอาร์เรย์และตราบใดที่คุณตั้งค่าของอาร์เรย์แทนที่จะผลักดันมัน
TabsNotSpaces

1
คุณก็ต้องมีอาร์เรย์ของผู้ฟังแทนที่จะเป็นหนึ่งเดียวและแล้วแทนเพียงโทรthis.aListener(val), valคุณจะต้องห่วงผ่านฟังก์ชั่นฟังทั้งหมดและเรียกแต่ละคนผ่าน โดยปกติแล้ววิธีการที่เรียกว่าแทนaddListener registerListener
Akira

74

คำตอบส่วนใหญ่สำหรับคำถามนี้อาจล้าสมัยไม่มีประสิทธิภาพหรือต้องมีห้องสมุดขนาดใหญ่ป่อง:

  • Object.watchและObject.observeมีทั้งที่คัดค้านและไม่ควรใช้
  • onPropertyChangeเป็นตัวจัดการเหตุการณ์องค์ประกอบ DOM ที่ใช้งานได้ใน IE บางรุ่นเท่านั้น
  • Object.definePropertyช่วยให้คุณสามารถทำให้คุณสมบัติของวัตถุไม่เปลี่ยนรูปซึ่งจะช่วยให้คุณตรวจพบการเปลี่ยนแปลงที่พยายาม แต่มันก็จะบล็อกการเปลี่ยนแปลงใด ๆ
  • การกำหนด setters และ getters ใช้งานได้ แต่ต้องมีรหัสการตั้งค่าจำนวนมากและมันไม่ทำงานได้ดีเมื่อคุณต้องการลบหรือสร้างคุณสมบัติใหม่

วันนี้คุณสามารถใช้วัตถุพร็อกซีเพื่อตรวจสอบการเปลี่ยนแปลง (และสกัดกั้น) ที่เกิดขึ้นกับวัตถุ มันมีวัตถุประสงค์เพื่อสร้างสิ่งที่ 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วัตถุคือ:

  1. Proxyวัตถุไม่สามารถใช้ได้ในเบราว์เซอร์รุ่นเก่า (เช่น IE11) และpolyfillไม่สามารถทำซ้ำได้อย่างเต็มที่Proxyการทำงาน
  2. วัตถุพร็อกซีไม่ได้ทำงานตามที่คาดไว้กับวัตถุพิเศษเสมอไป (เช่น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}}]

1
ฉันจะเพิ่มข้อเสียเปรียบอื่นคุณไม่ได้ดูการเปลี่ยนแปลงในวัตถุเป้าหมาย แต่เฉพาะกับวัตถุพร็อกซี ในบางกรณีคุณเพียงต้องการที่จะรู้ว่าเมื่อมีการเปลี่ยนแปลงสถานที่ให้บริการในวัตถุเป้าหมาย (เช่นtarget.hello_world = "test")
Cristiano

2
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- คุณสามารถทำสิ่งนั้นได้ด้วย a Proxyซึ่งเป็นหนึ่งในกรณีการใช้งานหลักสำหรับพร็อกซี่
เอลเลียต B.

2
ไม่เพราะคุณกำลังแก้ไขเป้าหมายโดยตรง หากคุณต้องการสังเกตการแก้ไขtargetคุณต้องทำผ่านพร็อกซี อย่างไรก็ตามproxy.hello_world = "test"ไม่ได้หมายความว่าคุณกำลังแก้ไขพร็อกซีพร็อกซียังคงไม่เปลี่ยนแปลงtargetรับการแก้ไข (หากตั้งค่าตัวจัดการชุดของคุณให้ทำ) target.hello_world = "test"มันเสียงเหมือนจุดของคุณคือการที่คุณไม่สามารถสังเกตได้โดยตรง นั่นเป็นความจริง. การกำหนดตัวแปรธรรมดาไม่ปล่อยเหตุการณ์ใด ๆ นั่นเป็นเหตุผลที่เราต้องใช้เครื่องมือเช่นที่อธิบายไว้ในคำตอบของคำถามนี้
เอลเลียต B.

2
ขอบคุณ Elliot B. It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true.นั่นคือประเด็นของฉัน ในกรณีของฉันฉันมีวัตถุที่สร้างขึ้นที่อื่นและได้รับการปรับปรุงโดยรหัสอื่น ... พร็อกซีในกรณีนี้ไม่มีประโยชน์เนื่องจากการเปลี่ยนแปลงจะเกิดขึ้นกับเป้าหมาย
Cristiano

1
@ Cristiano ฉันเดาว่าเอลเลียตกำลังพยายามจะพูดอะไรคือคุณสามารถใช้พร็อกซีแทนวัตถุจริงซึ่งหมายความว่าคุณสามารถส่งผ่านพร็อกซีได้ราวกับว่ามันเป็นวัตถุของคุณและทำให้ส่วนอื่น ๆ ของแอปโต้ตอบกับพร็อกซีของคุณ การเปลี่ยนแปลงในพร็อกซีจะปรากฏบนวัตถุจริง
ZoltánMatók

33

เลขที่

แต่ถ้าสำคัญจริง ๆ คุณมี 2 ตัวเลือก (ทดสอบครั้งแรกไม่ใช่วินาที):

ก่อนอื่นใช้ setters และ getters ดังนี้:

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);

8
"การเฝ้าดู" ไม่ได้ตรวจพบการเปลี่ยนแปลง นี่ไม่ใช่เหตุการณ์ที่เกิดขึ้นและจะทำให้แอปทั้งหมดช้าลงอย่างมากในไม่ช้า วิธีการเหล่านี้ IMHO ไม่ควรเป็นส่วนหนึ่งของโครงการจริง
มูฮัมหมัดบิน Yusrat

28

การใช้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ต้องใช้อาร์กิวเมนต์ 2 ตัว แต่ตัวอย่างส่วนหนึ่งของคุณแสดงฟังก์ชันที่ถูกเรียกด้วยพารามิเตอร์พารามิเตอร์
Hlawuleka MAS

22

ขออภัยที่จะนำเธรดเก่าออกมา แต่นี่เป็นคู่มือเล็กน้อยสำหรับผู้ที่ (เช่นฉัน!) ไม่เห็นตัวอย่างการทำงานของ Eli Grey:

var test = new Object();
test.watch("elem", function(prop,oldval,newval){
    //Your code
    return newval;
});

หวังว่าสิ่งนี้จะช่วยให้ใครบางคน


9
ขณะนี้ Chrome ไม่ได้รับการสนับสนุนสำหรับนาฬิกาหรือ Safari เท่านั้น Firefox
Paul McClean

5
สำหรับเครือข่ายนักพัฒนา mozilla ไม่แนะนำให้ทำเช่นนี้ Object.prototype.watch () มีไว้สำหรับการทดสอบเท่านั้นและไม่ควรใช้ในรหัสการผลิต developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ …
Dennis Bartlett

4
@PaulMcClean คำตอบนี้เป็นคำตอบของ Eli Grey ซึ่งบรรจุ Polyfill gist.github.com/eligrey/384583
งานอดิเรก

2
นาฬิกาเลิกใช้แล้วโดย Mozilla คำตอบนี้อาจทำให้เข้าใจผิด
Arnaud

7

ในฐานะที่เป็นคำตอบของลุคชาเชเฟอร์ ( หมายเหตุ : นี่หมายถึงโพสต์ต้นฉบับของเขา แต่จุดทั้งหมดที่นี่ยังคงใช้ได้หลังจากการแก้ไข ) ฉันขอแนะนำวิธีการรับ / ตั้งค่าคู่หนึ่งเพื่อเข้าถึงค่าของคุณ

อย่างไรก็ตามฉันขอแนะนำการแก้ไขบางอย่าง (และนั่นเป็นสาเหตุที่ฉันโพสต์ ... )

ปัญหาของรหัสนั้นคือฟิลด์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!

encapsulation

ดังนั้นเพื่อรับประกันว่าผู้ฟังจะได้รับการเรียกจริงเราจะต้องห้ามการเข้าถึงสนาม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
    };
})();

ตอนนี้คุณสามารถใช้วิธีเดียวกันในการสร้างและเพิ่มผู้ฟังตามที่ลุคเสนอ แต่คุณสามารถมั่นใจได้ว่าไม่มีวิธีที่เป็นไปได้ที่จะอ่านหรือเขียนจากการaไม่สนใจ!

การเพิ่มฟิลด์ที่ถูกห่อหุ้มโดยทางโปรแกรม

ยังอยู่ในการติดตามของลุคฉันเสนอวิธีง่ายๆในการเพิ่มฟิลด์ที่ถูกห่อหุ้มและตัวเชื่อมต่อ / ตัวเซ็ตที่เกี่ยวข้องไปยังวัตถุโดยใช้การเรียกฟังก์ชันอย่างง่าย

ทราบว่านี้จะทำงานอย่างถูกต้องกับประเภทค่า สำหรับการทำงานกับประเภทของการอ้างอิง , ชนิดของสำเนาลึกจะต้องมีการดำเนินการ (ดูคนนี้เป็นต้น)

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);

2
+1 สำหรับการห่อหุ้ม นั่นเป็นความคิดแรกของฉัน แต่ฉันต้องการความสามารถในการเพิ่มเมธอด create_gets_sets ในที่สุดและเนื่องจากมันไม่ได้พิจารณาการซ่อนค่าไม่ดี :) เราสามารถทำขั้นตอนต่อไปและเขียนบางสิ่งเพื่อซ่อนค่า แต่ ผมคิดว่ารหัสที่ได้โพสต์มีความสับสนพอสำหรับคนส่วนใหญ่ ... บางทีถ้ามีการเรียกร้องให้มัน ...
ลุค Schafer

6

หากคุณใช้ jQuery {UI} (ซึ่งทุกคนควรใช้ :-)) คุณสามารถใช้. change () พร้อมอิลิเมนต์ <input /> ที่ซ่อนอยู่


7
ฉันไม่ค่อยเข้าใจ คุณจะแนบตัวแปรเข้ากับ<input/>องค์ประกอบที่ซ่อนอยู่ได้อย่างไร
ปีเตอร์ลี

4
ฉันคิดว่าชัคแนะนำว่าคุณสามารถกำหนดค่าของอินพุตโดยใช้ jquery จากนั้นและผู้ฟังเหตุการณ์. change () หากคุณอัปเดตค่าของอินพุตด้วย. val () ดังนั้นการเรียกกลับเหตุการณ์. change () จะเริ่มทำงาน
jarederaj

2
<input type="hidden" value="" id="thisOne" />และกับ jQuery $("#thisOne").change(function() { do stuff here });และ$("#thisOne").val(myVariableWhichIsNew);แล้ว.change()ไฟประสงค์
khaverim

2
นี่เป็นทางออกที่ดีที่สุดในหนังสือของฉัน ง่ายและสะดวก แทนที่จะเปลี่ยนตัวแปรในรหัสของคุณเช่น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');
Tom Walker

2
สำหรับทุกคนที่มีปัญหาเช่นเดียวกับฉันถ้าเหตุการณ์การเปลี่ยนแปลงไม่ทริกเกอร์ลองใช้ $ ("# thisOne") val (myVariableWhichIsNew). trigger ('เปลี่ยน') หวังว่าสิ่งนี้จะช่วยได้
Alator

6

AngularJS(ฉันรู้ว่านี่ไม่ใช่JQueryแต่อาจช่วยได้ [JS บริสุทธิ์เป็นสิ่งที่ดีในทางทฤษฎีเท่านั้น]):

$scope.$watch('data', function(newValue) { ..

โดยที่ "data" เป็นชื่อของตัวแปรของคุณในขอบเขต

มีลิงก์ไปยังเอกสาร


น่าเสียดายที่มันบังคับให้คุณผูกตัวแปรกับขอบเขต
ruX

มันจะ$scope.$apply()ทำงานเมื่อถูกเรียกใช้เท่านั้น
iamawebgeek

5

สำหรับการปรับจูนในสองสามปีต่อมา:

โซลูชันสำหรับเบราว์เซอร์ส่วนใหญ่ (และ IE6 +) พร้อมใช้งานที่ใช้เหตุการณ์ onpropertychange และ define specProperty ที่ใหม่กว่า สิ่งที่จับได้เล็กน้อยคือคุณจะต้องทำให้ตัวแปรของคุณเป็นวัตถุโดม

รายละเอียดแบบเต็ม:

http://johndyer.name/native-browser-get-set-properties-in-javascript/


3

ฟังก์ชั่นที่คุณกำลังมองหาสามารถทำได้โดยใช้วิธี "defineProperty ()" ซึ่งใช้ได้เฉพาะเบราว์เซอร์สมัยใหม่เท่านั้น:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

ฉันเขียนส่วนขยาย jQuery ที่มีฟังก์ชั่นคล้ายกันบ้างถ้าคุณต้องการการสนับสนุนข้ามเบราว์เซอร์เพิ่มเติม:

https://github.com/jarederaj/jQueue

นามสกุล jQuery ขนาดเล็กที่จัดการการโทรกลับเพื่อรอคิวการมีอยู่ของตัวแปรวัตถุหรือคีย์ คุณสามารถกำหนดจำนวนการเรียกกลับไปยังจำนวนจุดข้อมูลใด ๆ ที่อาจได้รับผลกระทบจากกระบวนการที่ทำงานในพื้นหลัง jQueue รอฟังและรอข้อมูลเหล่านี้ที่คุณระบุว่ามีอยู่จริงจากนั้นจะทำการปิดการเรียกกลับที่ถูกต้องด้วยอาร์กิวเมนต์


2

ไม่ใช่โดยตรง: คุณต้องการคู่ getter / setter ที่มีอินเทอร์เฟซ "addListener / removeListener" ของการเรียงลำดับบางอย่าง ... หรือปลั๊กอิน NPAPI (แต่นั่นเป็นอีกเรื่องหนึ่ง)


1
//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);

1

วิธีแก้ปัญหาที่ค่อนข้างง่ายและง่ายคือเพียงแค่ใช้การเรียกใช้ฟังก์ชันเพื่อตั้งค่าตัวแปรส่วนกลางและไม่เคยตั้งค่าโดยตรง วิธีนี้คุณสามารถควบคุมได้ทั้งหมด:

var globalVar;

function setGlobalVar(value) {
    globalVar = value;
    console.log("Value of globalVar set to: " + globalVar);
    //Whatever else
}

ไม่มีทางที่จะบังคับใช้สิ่งนี้มันแค่ต้องมีวินัยในการเขียนโปรแกรม ... แม้ว่าคุณจะสามารถใช้grep(หรือบางอย่างที่คล้ายกัน) เพื่อตรวจสอบว่าไม่มีที่ไหนที่รหัสของคุณจะตั้งค่าโดยตรงglobalVarเพื่อตรวจสอบว่าไม่มีที่ไหนรหัสของคุณโดยตรงตั้งค่าของ

หรือคุณสามารถแค็ปซูลในวัตถุและวิธีการทะเยอทะยานของผู้ใช้และผู้ตั้งค่า ... แค่คิด


สำหรับตัวแปรที่ไม่ใช่คุณสมบัติของวัตถุที่สามารถเข้าถึงได้ - เช่นเดียวกับกรณีที่มีตัวแปรที่ประกาศด้วยvarในโมดูล ES6 - นี่เป็นวิธีแก้ปัญหาเดียว
AMN

1

ได้โปรดจำไว้ว่าคำถามเริ่มต้นมีไว้สำหรับตัวแปรไม่ใช่สำหรับวัตถุประสงค์;)

นอกเหนือจากคำตอบทั้งหมดข้างต้นฉันได้สร้าง lib เล็ก ๆ ชื่อforTheWatch.jsซึ่งใช้วิธีเดียวกันในการตรวจจับและติดต่อกลับสำหรับการเปลี่ยนแปลงในตัวแปรทั่วโลกในจาวาสคริปต์

เข้ากันได้กับตัวแปร JQUERY ไม่จำเป็นต้องใช้ OBJECTS และคุณสามารถผ่าน ARRAY ของตัวแปรหลายตัวได้โดยตรงหากจำเป็น

หากเป็นประโยชน์ ... : https://bitbucket.org/esabora/forthewatch
โดยทั่วไปคุณเพียงแค่เรียกใช้ฟังก์ชัน:
watchIt("theVariableToWatch", "varChangedFunctionCallback");

และขออภัยล่วงหน้าหากไม่เกี่ยวข้อง


1

วิธีที่ง่ายที่สุดที่ฉันค้นพบเริ่มต้นจากคำตอบนี้ :

// 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

1

window.playerในกรณีของผมผมพยายามที่จะหาว่าฉันห้องสมุดรวมทั้งในโครงการของฉันถูกกําหนดของฉัน ดังนั้นเมื่อเริ่มต้นรหัสของฉันฉันเพิ่งทำ:

Object.defineProperty(window, 'player', {
  get: () => this._player,
  set: v => {
    console.log('window.player has been redefined!');
    this._player = v;
  }
});

0

มันเป็นไปไม่ได้โดยตรง

อย่างไรก็ตามสามารถทำได้โดยใช้ CustomEvent: https://developer.mozilla.org/en-US/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']);

มันจะตรวจจับการเปลี่ยนแปลงตัวแปรชื่อผู้ใช้และหมายเลขผู้ใช้


0

ด้วยความช่วยเหลือของ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เพื่อเปลี่ยนค่าของตัวแปร:

  1. money.beforeSet (newValue, oldValue);
  2. money.beforeChange (newValue, oldValue); (จะถูกข้ามหากไม่มีการเปลี่ยนแปลงค่า)
  3. money.val = newValue;
  4. money.afterChange (newValue, oldValue); (จะถูกข้ามหากไม่มีการเปลี่ยนแปลงค่า)
  5. money.afterSet (newValue, oldValue);

ตอนนี้เรากำหนด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


-2

นี่เป็นเธรดเก่า แต่ฉันพบคำตอบที่สูงที่สุดอันดับสอง (ผู้ฟังกำหนดเอง) ในขณะที่มองหาวิธีแก้ปัญหาโดยใช้ Angular ในขณะที่วิธีการแก้ปัญหาการทำงานเชิงมุมมีวิธีที่ดีกว่าในการแก้ไขปัญหาการใช้งาน@Outputและตัวปล่อยเหตุการณ์ การเลิกใช้ตัวอย่างในคำตอบที่ผู้ฟังกำหนดเอง:

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กับผู้ปกครองทุกครั้งที่มีการคลิกปุ่มลูก การทำงานของstackblitz


-3
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]);}
                    });
            }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.