Lắng nghe những thay đổi trong JavaScript


462

Có thể có một sự kiện trong JS phát sinh khi giá trị của một biến nhất định thay đổi không? JQuery được chấp nhận.


@BenjaminGruenbaum có lẽ bạn muốn nói MutableObserver (cho DOM). Đối tượng chỉ dành cho các đối tượng JS từ những gì tôi nhớ.
HellBaby

2
@HellBaby câu hỏi này là về các biến - không phải DOM.
Benjamin Gruenbaum

9
@BenjaminGruenbaum theo developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... Object.observe là lỗi thời hoặc bị phản đối. Sự thay thế được đề xuất (trên cùng một trang) là đối tượng Proxy.
stannius

4
Câu hỏi chỉ hỏi về variable, nhưng tất cả các câu trả lời ở đây đề cập đến property. Tôi tự hỏi nếu chúng ta có thể lắng nghe local variablenhững thay đổi mặc dù.
Thariq Nugrohotomo

1
Có câu trả lời nào cho việc này không? Ý tôi là, chọn một
Braian Mellor

Câu trả lời:


100

Vâng, điều này bây giờ là hoàn toàn có thể!

Tôi biết đây là một chủ đề cũ nhưng bây giờ hiệu ứng này có thể sử dụng các bộ truy cập (getters và setters): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters

Bạn có thể định nghĩa một đối tượng như thế này, trong đó aInternalđại diện cho trường 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;
  }
}

Sau đó, bạn có thể đăng ký một người nghe bằng cách sử dụng như sau:

x.registerListener(function(val) {
  alert("Someone changed the value of x.a to " + val);
});

Vì vậy, bất cứ khi nào bất cứ điều gì thay đổi giá trị của x.a, chức năng nghe sẽ bị sa thải. Chạy dòng sau sẽ mang lại thông báo bật lên:

x.a = 42;

Xem ví dụ tại đây: https://jsfiddle.net/5o1wf1bn/1/

Bạn cũng có thể sử dụng một loạt các trình nghe thay vì một khe nghe, nhưng tôi muốn đưa ra một ví dụ đơn giản nhất có thể.


6
Đây là một tấn mã cho mỗi đối tượng, có cách nào để làm cho điều này tái sử dụng nhiều hơn?
Vael Victus

Mã của bạn ở đâu? Bạn đã bắt đầu một câu hỏi mới từ nó?
Akira

Điều gì về một cách để phát hiện khi một tài sản mới đã được thêm vào một đối tượng, hoặc một tài sản đã bị xóa?
Michael

Đây là một câu trả lời cũ, nhưng tôi muốn thêm rằng nó hoạt động tốt cho các giá trị mảng cũng như miễn là bạn đặt giá trị của mảng thay vì đẩy vào nó.
TabNotSpaces

1
Bạn chỉ cần có một mảng người nghe thay vì một người nghe và sau đó thay vì chỉ gọi this.aListener(val), bạn sẽ phải lặp qua tất cả các chức năng của người nghe và gọi từng người nghe val. Thông thường, phương thức được gọi addListenerthay vì registerListener.
Akira

75

Hầu hết các câu trả lời cho câu hỏi này đều lỗi thời, không hiệu quả hoặc yêu cầu bao gồm các thư viện cồng kềnh lớn:

  • Object.watchObject.observe đều không dùng nữa và không nên sử dụng.
  • onPropertyChange là một trình xử lý sự kiện phần tử DOM chỉ hoạt động trong một số phiên bản IE.
  • Object.defineProperty cho phép bạn tạo một thuộc tính đối tượng bất biến, điều này sẽ cho phép bạn phát hiện các thay đổi đã thử, nhưng nó cũng sẽ chặn mọi thay đổi.
  • Xác định setters và getters hoạt động, nhưng nó đòi hỏi rất nhiều mã thiết lập và nó không hoạt động tốt khi bạn cần xóa hoặc tạo các thuộc tính mới.

Ngày nay, bây giờ bạn có thể sử dụng đối tượng Proxy để theo dõi (và chặn) các thay đổi được thực hiện cho một đối tượng. Đó là mục đích được xây dựng cho những gì OP đang cố gắng thực hiện. Đây là một ví dụ cơ bản:

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'

Hạn chế duy nhất của Proxy đối tượng là:

  1. Đối Proxytượng không có sẵn trong các trình duyệt cũ hơn (như IE11) và polyfill không thể sao chép đầy đủ Proxychức năng.
  2. Các đối tượng proxy không luôn luôn hoạt động như mong đợi với các đối tượng đặc biệt (ví dụ Date:) -Proxy đối tượng được ghép nối tốt nhất với các Đối tượng hoặc Mảng đơn giản.

Nếu bạn cần quan sát các thay đổi được thực hiện cho một đối tượng lồng nhau , thì bạn cần sử dụng một thư viện chuyên dụng như Observable Slim (mà tôi đã xuất bản) hoạt động như thế này:

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
Tôi cũng sẽ thêm một nhược điểm nữa, bạn không thực sự xem các thay đổi trên đối tượng đích mà chỉ trên đối tượng proxy. Trong một số trường hợp, bạn chỉ muốn biết khi nào một thuộc tính thay đổi trên đối tượng đích (tức là target.hello_world = "test")
Cristiano

2
you don't actually watch changes on the target object but only on proxy object- điều đó không hoàn toàn chính xác. Đối Proxytượng không được sửa đổi - nó không có bản sao của mục tiêu. you just want to know when a property change on the target object- bạn có thể thực hiện điều đó với một Proxy, đó là một trong những trường hợp sử dụng chính cho proxy.
Elliot B.

2
Không, bởi vì bạn đang sửa đổi mục tiêu trực tiếp. Nếu bạn muốn quan sát sửa đổi target, thì bạn phải thực hiện thông qua proxy. Tuy nhiên proxy.hello_world = "test"không có nghĩa là bạn đang sửa đổi proxy, proxy vẫn không thay đổi, targetđược sửa đổi (nếu trình xử lý thiết lập của bạn được cấu hình để làm như vậy). Có vẻ như quan điểm của bạn là bạn không thể quan sát trực tiếp target.hello_world = "test". Điều đó đúng. Bài tập biến đơn giản không phát ra bất kỳ loại sự kiện. Đó là lý do tại sao chúng ta phải sử dụng các công cụ như những công cụ được mô tả trong câu trả lời cho câu hỏi này.
Elliot B.

2
Cảm ơn Elliot B. It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true.đó chính xác là quan điểm của tôi. Trong trường hợp của tôi, tôi có một đối tượng được tạo ở một nơi khác và được cập nhật bởi một số mã khác ... một proxy, trong trường hợp này, không hữu ích vì các thay đổi sẽ được thực hiện trên mục tiêu.
Cristiano

1
@Cristiano Tôi đoán điều Elliot đang cố nói là bạn có thể sử dụng proxy thay vì đối tượng thực tế, nghĩa là bạn có thể chuyển proxy xung quanh như nếu đó là đối tượng của bạn và làm cho các phần khác của ứng dụng tương tác với proxy của bạn. Thay đổi trên proxy sẽ được phản ánh trên đối tượng thực tế.
Zoltán Matók

33

Không.

Nhưng, nếu nó thực sự quan trọng, bạn có 2 tùy chọn (thứ nhất được kiểm tra, thứ hai không):

Đầu tiên, sử dụng setters và getters, như vậy:

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

sau đó bạn có thể làm một cái gì đó như:

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

sau đó đặt người nghe như:

listen_to(myobj, "a", function(oldval, newval) {
    alert("old : " + oldval + " new : " + newval);
}

Thứ hai, tôi thực sự đã quên, tôi sẽ gửi trong khi tôi nghĩ về nó :)

EDIT: Ồ, tôi nhớ :) Bạn có thể đặt một chiếc đồng hồ trên giá trị:

Đưa ra myobj ở trên, với 'a' trên đó:

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
"Đang xem" không thực sự phát hiện ra sự thay đổi. Đây không phải là sự kiện, và chắc chắn sẽ làm chậm toàn bộ ứng dụng rất sớm. Những cách tiếp cận này IMHO không bao giờ nên là một phần của một dự án thực sự.
Muhammad bin Yusrat

28

Sử dụng 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>

Ví dụ khác

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


Ví dụ thứ hai là một chút sai lệch, varwyêu cầu 2 đối số nhưng một phần của ví dụ của bạn cho thấy hàm được gọi chỉ bằng tham số giá trị.
Hlawuleka MAS

22

Xin lỗi để đưa ra một chủ đề cũ, nhưng đây là một hướng dẫn nhỏ cho những người (như tôi!) Không thấy ví dụ của Eli Grey hoạt động như thế nào:

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

Hy vọng điều này có thể giúp ai đó


9
Đồng hồ không được hỗ trợ trên Chrome hoặc Safari vào lúc này, chỉ có Firefox
Paul McClean

5
Mỗi mạng nhà phát triển mozilla, điều này không được khuyến khích. Object.prototype.watch () chỉ dành cho thử nghiệm và không nên được sử dụng trong mã sản xuất. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Khăn

4
@PaulMcClean câu trả lời này là Trả lời câu trả lời của Eli Grey có chứa Polyfill. gist.github.com/eligrey/384583
sở thích

2
Đồng hồ đã bị phản đối bởi Mozilla. Câu trả lời này có thể gây hiểu nhầm.
Arnaud

7

Như câu trả lời của Luke Schafer ( lưu ý : điều này đề cập đến bài viết gốc của anh ấy, nhưng toàn bộ quan điểm ở đây vẫn còn hiệu lực sau khi chỉnh sửa ), tôi cũng sẽ đề xuất một cặp phương thức Nhận / Đặt để truy cập giá trị của bạn.

Tuy nhiên tôi sẽ đề xuất một số sửa đổi (và đó là lý do tại sao tôi đăng ...).

Một vấn đề với mã đó là trường acủa đối tượng myobjcó thể truy cập trực tiếp, do đó có thể truy cập / thay đổi giá trị của nó mà không kích hoạt người nghe:

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!

Đóng gói

Vì vậy, để đảm bảo rằng người nghe thực sự được gọi, chúng tôi sẽ phải cấm truy cập trực tiếp vào lĩnh vực này a. Làm thế nào để làm như vậy? Sử dụng một đóng cử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
    };
})();

Bây giờ bạn có thể sử dụng cùng một phương pháp để tạo và thêm người nghe như Luke đề xuất, nhưng bạn có thể yên tâm rằng không có cách nào có thể đọc hoặc viết để akhông bị chú ý!

Thêm các trường được đóng gói theo chương trình

Vẫn theo dõi của Luke, bây giờ tôi đề xuất một cách đơn giản để thêm các trường được đóng gói và các getters / setters tương ứng vào các đối tượng bằng phương thức gọi hàm đơn giản.

Lưu ý rằng điều này sẽ chỉ hoạt động đúng với các loại giá trị . Để làm việc với các loại tham chiếu , một số loại bản sao sâu sẽ phải được thực hiện ( ví dụ, xem loại này ).

function addProperty(obj, name, initial) {
    var field = initial;
    obj["get_" + name] = function() { return field; }
    obj["set_" + name] = function(val) { field = val; }
}

Điều này hoạt động giống như trước: chúng ta tạo một biến cục bộ trên một hàm và sau đó chúng ta tạo một bao đóng.

Làm thế nào để sử dụng nó? Đơn giản:

var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);

2
+1 cho đóng gói. Đó là suy nghĩ đầu tiên của tôi, nhưng cuối cùng tôi muốn có khả năng thêm phương thức create_gets_sets và vì nó bừa bãi, việc ẩn các giá trị không tuyệt vời :) chúng tôi có thể tiến thêm một bước và viết một số điều để ẩn các giá trị, nhưng Tôi nghĩ rằng mã tôi đã đăng là đủ gây nhầm lẫn cho hầu hết mọi người ... có lẽ nếu có yêu cầu ...
Luke Schafer

6

Nếu bạn đang sử dụng jQuery {UI} (mà mọi người nên sử dụng :-)), bạn có thể sử dụng .change () với phần tử <input /> ẩn.


7
Tôi không hiểu lắm. Làm thế nào bạn có thể đính kèm một biến cho một <input/>yếu tố ẩn ?
Peter Lee

4
Tôi nghĩ Chuck đang gợi ý rằng bạn có thể đặt giá trị của đầu vào bằng cách sử dụng jquery và sau đó và một trình lắng nghe sự kiện .change (). Nếu bạn cập nhật giá trị của đầu vào bằng .val () thì cuộc gọi lại sự kiện .change () sẽ kích hoạt.
jarederaj

2
<input type="hidden" value="" id="thisOne" />và với jQuery $("#thisOne").change(function() { do stuff here });$("#thisOne").val(myVariableWhichIsNew);và sau đó là .change()ý chí hỏa hoạn.
khaverim

2
Đây là giải pháp tốt nhất trong sách của tôi. Đơn giản dễ dàng. Thay vì thay đổi biến trong mã của bạn, ví dụ: var1 = 'new value';thay vào đó, bạn sẽ đặt giá trị của đầu vào ẩn này, sau đó thêm một trình lắng nghe để thay đổi biến. $("#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
Đối với bất kỳ ai gặp vấn đề tương tự như tôi, nếu sự kiện thay đổi không kích hoạt hãy thử $ ("# thisOne"). Val (myVariableWhichIsNew) .trigger ('thay đổi'). Hy vọng điều này sẽ giúp
Alator

6

AngularJS(Tôi biết điều này không phải JQuery, nhưng điều đó có thể giúp ích. [Pure JS chỉ tốt về lý thuyết]):

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

trong đó "dữ liệu" là tên của biến của bạn trong phạm vi.

Có một liên kết đến doc.


Thật không may, nó buộc bạn phải liên kết biến với phạm vi
ruX

nó chỉ bị bắn khi $scope.$apply()chạy
iamawebgeek

5

Đối với những người điều chỉnh trong một vài năm sau:

Một giải pháp cho hầu hết các trình duyệt (và IE6 +) có sẵn sử dụng sự kiện onpropertychange và spec specProperty mới hơn. Một lưu ý nhỏ là bạn sẽ cần biến biến của mình thành đối tượng dom.

Chi tiết đầy đủ:

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


3

Chức năng bạn đang tìm kiếm có thể đạt được thông qua việc sử dụng phương thức "notifyProperty ()" - vốn chỉ khả dụng cho các trình duyệt hiện đại:

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

Tôi đã viết một tiện ích mở rộng jQuery có một số chức năng tương tự nếu bạn cần thêm hỗ trợ trình duyệt chéo:

https://github.com/jarederaj/jQueue

Một phần mở rộng jQuery nhỏ xử lý các cuộc gọi lại hàng đợi đến sự tồn tại của một biến, đối tượng hoặc khóa. Bạn có thể chỉ định bất kỳ số lượng cuộc gọi lại cho bất kỳ số điểm dữ liệu nào có thể bị ảnh hưởng bởi các quy trình đang chạy trong nền. jQueue lắng nghe và chờ đợi những dữ liệu này mà bạn chỉ định sẽ tồn tại và sau đó thực hiện cuộc gọi lại chính xác với các đối số của nó.


2

Không trực tiếp: bạn cần một cặp getter / setter với giao diện "addListener / removeListener" thuộc loại nào đó ... hoặc một plugin NPAPI (nhưng đó hoàn toàn là một câu chuyện khác).


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

hoặc là

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

hoặc là

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

Một giải pháp khá đơn giản và đơn giản là chỉ sử dụng lệnh gọi hàm để đặt giá trị của biến toàn cục và không bao giờ đặt giá trị của nó trực tiếp. Bằng cách này bạn có toàn quyền kiểm soát:

var globalVar;

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

Không có cách nào để thực thi điều này, nó chỉ yêu cầu kỷ luật lập trình ... mặc dù bạn có thể sử dụng grep(hoặc một cái gì đó tương tự) để kiểm tra xem không nơi nào mã của bạn trực tiếp đặt giá trị củaglobalVar .

Hoặc bạn có thể gói nó trong một đối tượng và các phương thức getter và setter của đối tượng ... chỉ là một ý nghĩ.


Đối với một biến không phải là thuộc tính của một đối tượng, người ta có thể truy cập - như trường hợp với các biến được khai báo vartrong các mô-đun ES6 - đây là giải pháp duy nhất .
Amn

1

Xin mọi người hãy nhớ câu hỏi ban đầu là dành cho VARIABLES, không phải cho ĐỐI TƯỢNG;)

Ngoài tất cả các câu trả lời ở trên, tôi đã tạo một lib nhỏ được gọi là forTheWatch.js , sử dụng cùng một cách để bắt và gọi lại cho các thay đổi trong các biến toàn cục thông thường trong javascript.

Tương thích với các biến JQUERY, không cần sử dụng ĐỐI TƯỢNG và bạn có thể truyền trực tiếp ARRAY của một số biến nếu cần.

Nếu nó có thể hữu ích ...: https://bitbucket.org/esabora/forthewatch
Về cơ bản, bạn chỉ cần gọi hàm:
watchIt("theVariableToWatch", "varChangedFunctionCallback");

Và xin lỗi trước nếu không liên quan.


1

Cách dễ nhất tôi đã tìm thấy, bắt đầu từ câu trả lời này :

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

Và sau đó:

state.pageNumber = 0;
// watch the console

state.pageNumber = 15;
// watch the console

1

Trong trường hợp của tôi, tôi đã cố gắng tìm hiểu xem có bất kỳ thư viện nào tôi đưa vào trong dự án của tôi đang định nghĩa lại không window.player. Vì vậy, khi bắt đầu mã của tôi, tôi đã làm:

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

0

Nó không thể trực tiếp.

Tuy nhiên, điều này có thể được thực hiện bằng CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent

Phương thức dưới đây chấp nhận một mảng các tên biến làm đầu vào và thêm trình lắng nghe sự kiện cho mỗi biến và kích hoạt sự kiện cho bất kỳ thay đổi nào đối với giá trị của các biến.

Phương pháp sử dụng bỏ phiếu để phát hiện sự thay đổi của giá trị. Bạn có thể tăng giá trị cho thời gian chờ tính bằng mili giây.

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

Bằng cách gọi Phương thức:

watchVariables(['username', 'userid']);

Nó sẽ phát hiện các thay đổi đối với tên người dùng và userid.


0

Với sự trợ giúp của gettersetter , bạn có thể định nghĩa một lớp JavaScript thực hiện điều đó.

Đầu tiên, chúng tôi định nghĩa lớp của chúng tôi được gọi là 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;
  }
}

Giả sử rằng chúng tôi muốn lắng nghe các moneythay đổi, hãy tạo một ví dụ MonitoredVariablevới số tiền ban đầu 0:

const money = new MonitoredVariable(0);

Sau đó, chúng tôi có thể nhận hoặc đặt giá trị của nó bằng cách sử dụng money.val:

console.log(money.val); // Get its value
money.val = 2; // Set its value

Vì chúng tôi chưa định nghĩa bất kỳ người nghe nào cho nó, nên không có gì đặc biệt xảy ra sau khi money.valthay đổi thành 2.

Bây giờ hãy xác định một số người nghe. Chúng tôi có bốn người nghe sẵn: beforeSet, beforeChange, afterChange, afterSet. Điều sau đây sẽ xảy ra tuần tự khi bạn sử dụng money.val = newValueđể thay đổi giá trị của biến:

  1. tiền.b Beforeset (newValue, oldValue);
  2. tiền.b BeforeChange (newValue, oldValue); (Sẽ bị bỏ qua nếu giá trị của nó không thay đổi)
  3. tiền.val = newValue;
  4. tiền.afterChange (newValue, oldValue); (Sẽ bị bỏ qua nếu giá trị của nó không thay đổi)
  5. tiền.afterset (newValue, oldValue);

Bây giờ chúng tôi xác định trình afterChangenghe chỉ được kích hoạt sau khi money.valđã thay đổi (trong khi afterSetsẽ được kích hoạt ngay cả khi giá trị mới giống với giá trị cũ):

money.afterChange = (newValue, oldValue) => {
  console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};

Bây giờ hãy đặt một giá trị mới 3và xem điều gì sẽ xảy ra:

money.val = 3;

Bạn sẽ thấy như sau trong bảng điều khiển:

Money has been changed from 2 to 3

Để biết mã đầy đủ, hãy xem https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab .


-2

Đây là một chủ đề cũ nhưng tôi tình cờ nhận được câu trả lời cao thứ hai (người nghe tùy chỉnh) trong khi tìm kiếm một giải pháp bằng cách sử dụng Angular. Trong khi giải pháp hoạt động, angular có cách xây dựng tốt hơn để giải quyết vấn đề này bằng cách sử dụng @Outputvà phát ra sự kiện. Đi ra khỏi ví dụ trong câu trả lời người nghe tùy chỉnh:

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

Điều này bây giờ sẽ cập nhật ntrên cha mẹ mỗi lần nhấp vào nút con. Làm việc 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]);}
                    });
            }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.