Heim > Artikel > Web-Frontend > Beispiel einer Methode zur Überwachung von Änderungen in JS-Variablen
Ich muss jetzt die Änderung einer bestimmten Variablen in js überwachen. Wenn sich die Variable ändert, kann ich kein Zeitintervall und keine anderen geplanten Überwachungsmethoden verwenden Lösung.
Das gemeinsame Merkmal beliebter MVVM-JS-Bibliotheken/Frameworks ist die Datenbindung, die automatisch relevante Berechnungen durchführt und die DOM-Anzeige reaktionsschnell ändert, nachdem sich die Daten geändert haben. Daher kann diese Frage auch so verstanden werden, wie die Datenbindung der MVVM-Bibliothek/des MVVM-Frameworks implementiert wird.
Zu den gängigen Datenbindungsimplementierungen gehören die Erkennung schmutziger Werte, ES5-basierte Getter und Setter, das aufgegebene Object.observe von ES und der in ES6 hinzugefügte Proxy.
Angular verwendet die Dirty-Value-Erkennung. Das Prinzip besteht darin, den neuen Wert mit dem alten Wert zu vergleichen und dann das DOM zu ändern, wenn sich der Wert tatsächlich ändert, sodass ein $ vorhanden ist im eckigen Digest. Warum ändern sich integrierte Anweisungen wie ng-click automatisch, nachdem sie ausgelöst wurden? Das Prinzip ist auch sehr einfach. $digest wird an die integrierten Anweisungen wie ng-click am Ende angehängt.
Einfache Implementierung einer Dirty-Value-Erkennung:
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title>two-way binding</title> </head> <body onload="init()"> <button ng-click="inc"> Increase </button> <button ng-click="reset"> Reset </button> <span style="color:red" ng-bind="counter"></span> <span style="color:blue" ng-bind="counter"></span> <span style="color:green" ng-bind="counter"></span> <script type="text/javascript"> /* 数据模型区开始 */ var counter = 0; function inc() { counter++; } function reset() { counter = 0; } /* 数据模型区结束 */ /* 绑定关系区开始 */ function init() { bind(); } function bind() { var list = document.querySelectorAll("[ng-click]"); for (var i=0; i<list.length; i++) { list[i].onclick = (function(index) { return function() { window[list[index].getAttribute("ng-click")](); apply(); }; })(i); } } function apply() { var list = document.querySelectorAll("[ng-bind='counter']"); for (var i=0; i<list.length; i++) { if (list[i].innerHTML != counter) { list[i].innerHTML = counter; } } } /* 绑定关系区结束 */ </script> </body></html>
Der Nachteil dabei ist, dass Sie nach der Änderung der Daten das DOM nicht automatisch ändern können. Sie müssen einen Weg finden, apply( auszulösen. ), daher können Sie die Verpackung von ng-click nur verwenden, um die Überwachung echter Klickereignisse in ng-click einzubinden und die Erkennung schmutziger Werte hinzuzufügen, um zu bestimmen, ob das DOM aktualisiert werden soll.
Ein weiterer Nachteil besteht darin, dass, wenn Sie nicht aufpassen, bei jeder Erkennung schmutziger Werte eine große Datenmenge erkannt wird und viele Daten für die Erkennung nicht erforderlich sind, was sich leicht auf die Leistung auswirken kann.
In Bezug auf die Implementierung einer Dirty-Value-Erkennung ähnlich wie bei Angular gibt es nach Kenntnis des Prinzips noch viel zu tun, zu optimieren und so weiter. Wenn Sie interessiert sind, können Sie „Build Your Own Angular.js“ lesen, das Migong Shu einmal empfohlen hat. Im ersten Kapitel, Scope, wird erläutert, wie der Umfang und die Erkennung schmutziger Werte von Angular implementiert werden. Das obige Beispiel ist übrigens auch leicht vom Blog des Wanderarbeiter-Onkels abgeändert. Es wird empfohlen, den Originaltext am Ende zu lesen.
In ES5 wird eine neue Object.defineProperty hinzugefügt, die direkt eine neue Eigenschaft für ein Objekt definieren oder eine vorhandene Eigenschaft ändern und das Objekt zurückgeben kann.
Object.defineProperty(obj, prop, descriptor)
Der dritte akzeptierte Parameter kann get und set sein und entspricht jeweils einer Getter- und Setter-Methode:
var a = { zhihu:0 };Object.defineProperty(a, 'zhihu', { get: function() { console.log('get:' + zhihu); return zhihu; }, set: function(value) { zhihu = value; console.log('set:' + zhihu); } }); a.zhihu = 2; // set:2console.log(a.zhihu); // get:2 // 2
Man kann sagen, dass der Getter und Setter auf ES5 basieren fast perfekt gewünscht. Warum fast sagen?
Erstens können IE8 und niedrigere Versionen des IE nicht verwendet werden, und diese Funktion verfügt nicht über eine Polyfüllung und kann nicht auf nicht unterstützten Plattformen implementiert werden. Aus diesem Grund ist dies bei Vue.js, das auf ES5-Gettern und -Settern basiert, nicht der Fall unterstützt IE8 und höher. Der Grund ist die niedrigere Version des IE. Vielleicht erwähnt jemand Avalon, um ähnliche Funktionen in niedrigeren Versionen des IE zu erreichen.
Darüber hinaus besteht ein weiteres Problem darin, dass das Ändern der Länge des Arrays, das direkte Festlegen von Elementen mit Indizes wie items[0] = {} und Mutationsmethoden wie Push des Arrays den Setter nicht auslösen können. Wenn Sie dieses Problem lösen möchten, können Sie sich auf den Ansatz von Vue beziehen. In Vues Observer/Array.js ändert Vue direkt die Prototyp-Methode des Arrays:
const arrayProto = Array.prototypeexport const arrayMethods = Object.create(arrayProto)/** * Intercept mutating methods and emit events */;[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] .forEach(function (method) { // cache original method var original = arrayProto[method] def(arrayMethods, method, function mutator () { // avoid leaking arguments: // http://jsperf.com/closure-with-arguments var i = arguments.length var args = new Array(i) while (i--) { args[i] = arguments[i] } var result = original.apply(this, args) var ob = this.__ob__ var inserted switch (method) { case 'push': inserted = args break case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify() return result }) });
Auf diese Weise ist die Prototyp-Methode neu geschrieben und das Array wird ausgeführt. Die Aktualisierung der Ansicht kann nach der Mutation der Methode weiterhin ausgelöst werden.
Das Problem, die Länge des Arrays zu ändern und den Index direkt zum Festlegen von Elementen wie „items[0] = {}“ zu verwenden, kann jedoch immer noch nicht gelöst werden Beziehen Sie sich auf den Ansatz von Vue: Für das erstere Problem können Sie direkt ein neues Array verwenden, um das alte Array zu ersetzen. Das letztere Problem kann durch eine $set-Methode für das Array erweitert werden, die nach der Änderung die Aktualisierung der Ansicht auslöst.
Object.observe war im Entwurf von ES7 enthalten und erreichte Stufe 2 des Vorschlags, wurde aber schließlich aufgegeben. Hier ist nur ein Beispiel von MDN:
// 一个数据模型var user = { id: 0, name: 'Brendan Eich', title: 'Mr.'};// 创建用户的greetingfunction updateGreeting() { user.greeting = 'Hello, ' + user.title + ' ' + user.name + '!'; } updateGreeting();Object.observe(user, function(changes) { changes.forEach(function(change) { // 当name或title属性改变时, 更新greeting if (change.name === 'name' || change.name === 'title') { updateGreeting(); } }); });
Da es sich um eine veraltete Funktion handelt, werde ich hier nicht näher darauf eingehen, obwohl sie früher unterstützt wurde Wenn Sie interessiert sind, können Sie danach suchen. Früher war dies eine vielversprechende Funktion (die Datenbindungsrevolution, die durch Object.observe() hervorgerufen wurde). Natürlich gibt es einige Alternativen zu Polymer/observe-js.
ähnelt, wie der Name schon sagt, dem Proxy in HTTP:
var p = new Proxy(target, handler);
Ziel ist das Zielobjekt, das ein beliebiger Typ sein kann B. ein Array, eine Funktion oder sogar ein anderes Proxy-Objekt. Handler ist ein Prozessorobjekt, das eine Reihe von Proxy-Methoden enthält, um verschiedene Verhaltensweisen der generierten Proxy-Objekte zu steuern.
Zum Beispiel:
let a = new Proxy({}, { set: function(obj, prop, value) { obj[prop] = value; if (prop === 'zhihu') { console.log("set " + prop + ": " + obj[prop]); } return true; } }); a.zhihu = 100;
Die Fähigkeiten von Proxy gehen natürlich weit darüber hinaus und können auch Proxy-Weiterleitung usw. implementieren.
Es sollte jedoch beachtet werden, dass derzeit nur Firefox 18 diese Funktion unterstützt und Babel offiziell angibt, dass es diese Funktion nicht unterstützt:
Unsupported feature Due to the limitations of ES5, Proxies cannot be transpiled or polyfilled.
Es gibt derzeit Babel-Plug-Ins, die dies können Implementieren Sie es, aber es wird gesagt, dass die Implementierung komplizierter ist. Wenn es sich um Node handelt, sollten Sie es nach dem Upgrade auf die neueste Version verwenden können. Die Testumgebung für das obige Beispiel ist Node v6.4.0.
Verwandte Empfehlungen:
Detaillierte Erläuterung der JS-Variablenförderung
Einführung in JS-Variablen und ihre Wissenspunkte zum Umfang
Das obige ist der detaillierte Inhalt vonBeispiel einer Methode zur Überwachung von Änderungen in JS-Variablen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!