Obwohl ES5 uns die Methode Object.defineProperty zum Festlegen von Gettern und Settern zur Verfügung stellt, ist die Verwendung dieser nativen Methode unpraktisch. Warum nicht selbst eine Klasse implementieren, solange erben Diese Klasse und bestimmte Spezifikationen können Getter und Setter haben, die mit nativen vergleichbar sind.
Jetzt definieren wir die folgenden Spezifikationen:
Der Werter und Setter folgen dem Format: _xxxGetter/_xxxSetter, xxx stellt das Attribut dar, das gesteuert werden muss. Wenn Sie beispielsweise das foo-Attribut steuern möchten, muss das Objekt die Methode _fooGetter/_fooSetter als eigentlichen Wertgeber und den Controller bereitstellen, damit wir obj.get aufrufen können im Code ('foo') und obj.set('foo', value) zum Abrufen und Festlegen des Werts; andernfalls entspricht der Aufruf der get- und set-Methoden dem Code: obj.foo und obj.foo = value;
Bereitstellen der Watch-Funktion: obj.watch(attr, function(name, oldValue, newValue){}); Bei jedem Aufruf der Set-Methode wird der Funktionsparameter ausgelöst. Der Name in der Funktion stellt das geänderte Attribut dar, oldValue ist der letzte Wert des Attributs und newValue stellt den neuesten Wert des Attributs dar. Diese Methode gibt ein Handle-Objekt mit einer Remove-Methode zurück. Rufen Sie „remove“ auf, um den Funktionsparameter aus der Funktionskette zu entfernen.
Verwenden Sie zunächst den -Abschluss--Modus und verwenden Sie die Attribute-Variable als privates Attribut, um die Getter und Setter aller Attribute zu speichern:
var Stateful = (function(){ 'use strict'; var attributes = { Name: { s: '_NameSetter', g: '_NameGetter', wcbs: [] } }; var ST = function(){}; return ST; })()
wo wcbs verwendet wird, um alle Rückrufe zu speichern, wenn watch(name, callback) aufgerufen wird.
Die erste Version des Implementierungscodes lautet wie folgt:
var Stateful = (function(){ 'use strict'; var attributes = {}; function _getNameAttrs(name){ return attributes[name] || {}; } function _setNameAttrs(name) { if (!attributes[name]) { attributes[name] = { s: '_' + name + 'Setter', g: '_' + name + 'Getter', wcbs: [] } } } function _setNameValue(name, value){ _setNameAttrs(name); var attrs = _getNameAttrs(name); var oldValue = _getNameValue.call(this, name); //如果对象拥有_nameSetter方法则调用该方法,否则直接在对象上赋值。 if (this[attrs.s]){ this[attrs.s].call(this, value); } else { this[name] = value; } if (attrs.wcbs && attrs.wcbs.length > 0){ var wcbs = attrs.wcbs; for (var i = 0, len = wcbs.length; i < len; i++) { wcbs[i](name, oldValue, value); } } }; function _getNameValue(name) { _setNameAttrs(name); var attrs = _getNameAttrs(name); var oldValue = null; // 如果拥有_nameGetter方法则调用该方法,否则直接从对象中获取。 if (this[attrs.g]) { oldValue = this[attrs.g].call(this, name); } else { oldValue = this[name]; } return oldValue; }; function ST(){}; ST.prototype.set = function(name, value){ //每次调用set方法时都将name存储到attributes中 if (typeof name === 'string'){ _setNameValue.call(this, name, value); } else if (typeof name === object) { for (var p in name) { _setNameValue.call(this, p, name[p]); } } return this; }; ST.prototype.get = function(name) { if (typeof name === 'string') { return _getNameValue.call(this, name); } }; ST.prototype.watch = function(name, wcb) { var attrs = null; if (typeof name === 'string') { _setNameAttrs(name); attrs = _getNameAttrs(name); attrs.wcbs.push(wcb); return { remove: function(){ for (var i = 0, len = attrs.wcbs.length; i < len; i++) { if (attrs.wcbs[i] === wcb) { break; } } attrs.wcbs.splice(i, 1); } } } else if (typeof name === 'function'){ for (var p in attributes) { attrs = attributes[p]; attrs.wcbs.splice(0,0, wcb); //将所有的callback添加到wcbs数组中 } return { remove: function() { for (var p in attributes) { var attrs = attributes[p]; for (var i = 0, len = attrs.wcbs.length; i < len; i++) { if (attrs.wcbs[i] === wcb) { break; } } attrs.wcbs.splice(i, 1); } } } } }; return ST; })()
Testarbeit:
console.log(Stateful); var stateful = new Stateful(); function A(name){ this.name = name; }; A.prototype = stateful; A.prototype._NameSetter = function(n) { this.name = n; }; A.prototype._NameGetter = function() { return this.name; } function B(name) { this.name = name; }; B.prototype = stateful; B.prototype._NameSetter = function(n) { this.name = n; }; B.prototype._NameGetter = function() { return this.name; }; var a = new A(); var handle = a.watch('Name', function(name, oldValue, newValue){ console.log(name + 'be changed from ' + oldValue + ' to ' + newValue); }); a.set('Name', 'AAA'); console.log(a.name); var b = new B(); b.set('Name', 'BBB'); console.log(b.get('Name')); handle.remove(); a.set('Name', 'new AAA'); console.log(a.get('Name'), b.get('Name'))
Ausgabe:
function ST(){} Namebe changed from undefined to AAA AAA Namebe changed from undefined to BBB BBB new AAA BBB
Sie Sie können sehen, dass alle Überwachungsfunktionen im WCBS-Array gespeichert sind und alle Attribute mit demselben Namen in Unterklassen auf dasselbe WCBS-Array zugreifen. Gibt es eine Möglichkeit sicherzustellen, dass jede Instanz über eine eigene Überwachungsfunktionskette verfügt, ohne dass es zu Umweltverschmutzung kommt? Sie können diese Methode in Betracht ziehen: Fügen Sie jeder Instanz, die eine Funktion ist, ein _watchCallbacks-Attribut hinzu und speichern Sie alle Überwachungsfunktionsketten in dieser Funktion. Der Hauptcode lautet wie folgt:
ST.prototype.watch = function(name, wcb) { var attrs = null; var callbacks = this._watchCallbacks; if (!callbacks) { callbacks = this._watchCallbacks = function(n, ov, nv) { var execute = function(cbs){ if (cbs && cbs.length > 0) { for (var i = 0, len = cbs.length; i < len; i++) { cbs[i](n, ov, nv); } } } //在函数作用域链中可以访问到callbacks变量 execute(callbacks['_' + n]); execute(callbacks['*']);// 通配符 } } var _name = ''; if (typeof name === 'string') { var _name = '_' + name; } else if (typeof name === 'function') {//如果name是函数,则所有属性改变时都会调用该函数 _name = '*'; wcb = name; } callbacks[_name] = callbacks[_name] ? callbacks[_name] : []; callbacks[_name].push(wcb); return { remove: function(){ var idx = callbacks[_name].indexOf(wcb); if (idx > -1) { callbacks[_name].splice(idx, 1); } } }; };
Nach Änderungen Der Gesamtcode lautet wie folgt:
var Stateful = (function(){ 'use strict'; var attributes = {}; function _getNameAttrs(name){ return attributes[name] || {}; } function _setNameAttrs(name) { if (!attributes[name]) { attributes[name] = { s: '_' + name + 'Setter', g: '_' + name + 'Getter'/*, wcbs: []*/ } } } function _setNameValue(name, value){ if (name === '_watchCallbacks') { return; } _setNameAttrs(name); var attrs = _getNameAttrs(name); var oldValue = _getNameValue.call(this, name); if (this[attrs.s]){ this[attrs.s].call(this, value); } else { this[name] = value; } if (this._watchCallbacks){ this._watchCallbacks(name, oldValue, value); } }; function _getNameValue(name) { _setNameAttrs(name); var attrs = _getNameAttrs(name); var oldValue = null; if (this[attrs.g]) { oldValue = this[attrs.g].call(this, name); } else { oldValue = this[name]; } return oldValue; }; function ST(obj){ for (var p in obj) { _setNameValue.call(this, p, obj[p]); } }; ST.prototype.set = function(name, value){ if (typeof name === 'string'){ _setNameValue.call(this, name, value); } else if (typeof name === 'object') { for (var p in name) { _setNameValue.call(this, p, name[p]); } } return this; }; ST.prototype.get = function(name) { if (typeof name === 'string') { return _getNameValue.call(this, name); } }; ST.prototype.watch = function(name, wcb) { var attrs = null; var callbacks = this._watchCallbacks; if (!callbacks) { callbacks = this._watchCallbacks = function(n, ov, nv) { var execute = function(cbs){ if (cbs && cbs.length > 0) { for (var i = 0, len = cbs.length; i < len; i++) { cbs[i](n, ov, nv); } } } //在函数作用域链中可以访问到callbacks变量 execute(callbacks['_' + n]); execute(callbacks['*']);// 通配符 } } var _name = ''; if (typeof name === 'string') { var _name = '_' + name; } else if (typeof name === 'function') {//如果name是函数,则所有属性改变时都会调用该函数 _name = '*'; wcb = name; } callbacks[_name] = callbacks[_name] ? callbacks[_name] : []; callbacks[_name].push(wcb); return { remove: function(){ var idx = callbacks[_name].indexOf(wcb); if (idx > -1) { callbacks[_name].splice(idx, 1); } } }; }; return ST; })()
Test:
console.log(Stateful); var stateful = new Stateful(); function A(name){ this.name = name; }; A.prototype = stateful; A.prototype._NameSetter = function(n) { this.name = n; }; A.prototype._NameGetter = function() { return this.name; } function B(name) { this.name = name; }; B.prototype = stateful; B.prototype._NameSetter = function(n) { this.name = n; }; B.prototype._NameGetter = function() { return this.name; }; var a = new A(); var handle = a.watch('Name', function(name, oldValue, newValue){ console.log(name + 'be changed from ' + oldValue + ' to ' + newValue); }); a.set('Name', 'AAA'); console.log(a.name); var b = new B(); b.set('Name', 'BBB'); console.log(b.get('Name')); a.watch(function(name, ov, nv) { console.log('* ' + name + ' ' + ov + ' ' + nv); }); a.set({ foo: 'FOO', goo: 'GOO' }); console.log(a.get('goo')); a.set('Name', 'AAA+'); handle.remove(); a.set('Name', 'new AAA'); console.log(a.get('Name'), b.get('Name'))
Ausgabe:
function ST(obj){ for (var p in obj) { _setNameValue.call(this, p, obj[p]); } } Namebe changed from undefined to AAA AAA BBB * foo undefined FOO * goo undefined GOO GOO Namebe changed from AAA to AAA+ * Name AAA AAA+ * Name AAA+ new AAA new AAA BBB
Das obige ist der detaillierte Inhalt vonBeispielcode-Sharing für die Getter/Setter-Implementierung in JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!